Skip to content
153 changes: 101 additions & 52 deletions references/apigee-skills-serving/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ custom MCP hosts, etc.) can discover and install.

This reference implementation shows how to:

1. **Author** a skill (a `SKILL.md` + optional `scripts/`) and describe it
with a `manifest.yaml` against a locked schema.
1. **Author** a skill (a `SKILL.md` + optional `scripts/`) and describe
it with a `manifest.yaml` against a locked schema.
2. **Sign** the skill bundle with an Ed25519 key so consumers can verify
integrity at install time.
3. **Upload** the signed `.skill` archive to a Google Cloud Storage bucket.
3. **Upload** the signed `.skill` archive to a Google Cloud Storage
bucket.
4. **Register** the skill (and its API hub attribute taxonomy) so it is
discoverable through API hub's search and attribute filters.
5. **Install** a skill on the consumer side: search API hub, fetch the
Expand All @@ -21,6 +22,20 @@ This reference implementation shows how to:
The whole loop runs on standard Apigee X / hybrid plus API hub — no
custom infrastructure.

Two ready-to-run agent skills wrap the reference implementation into
a one-line install for end users:

- **`skills/skill-finder/`** — consumer-side discovery client. Queries
API hub for signed skills, verifies their Ed25519 signatures against
locally-installed per-deployment trust roots, and installs matching
skills into the agent runtime's skills directory.
- **`skills/skill-publisher/`** — author-side pipeline orchestrator.
Wraps pack → sign → upload → register into a single skill an agent
can invoke on a source skill directory.

Both are installed by `bin/install-skill-finder.sh` (which installs
both by default).

## Why Apigee API hub for skills?

Agent skills are, structurally, just metadata-tagged content addressed
Expand Down Expand Up @@ -48,12 +63,17 @@ references/apigee-skills-serving/
├── pytest.ini test configuration
├── docs/
│ ├── architecture.md design overview and trust model
│ ├── publish-and-install.md end-to-end walkthrough
│ ├── provisioning.md customer-onboarding runbook (five-minute path)
│ ├── publish-and-install.md detailed end-to-end walkthrough (under-the-hood)
│ └── policy-skill-catalog.md about the apigee-policy-top10 example
├── bin/
│ ├── check-prerequisites.sh pre-flight environment validator
│ ├── demo-setup.sh env export + readiness print
│ └── demo-cleanup.sh remove demo artifacts
│ ├── demo-setup.sh env export + readiness print (legacy)
│ ├── demo-cleanup.sh remove locally-installed skills
│ ├── install-skill-finder.sh one-line installer for skill-finder + skill-publisher
│ ├── install-skill-publisher.sh install skill-publisher only
│ └── provision.sh one-line customer bootstrap (APIs, bucket,
│ taxonomy, key gen, publish, trust root)
├── schema/
│ └── skill-manifest.schema.yaml locked v1 manifest schema
├── scripts/ publisher-side toolchain
Expand All @@ -63,8 +83,10 @@ references/apigee-skills-serving/
│ ├── register_skill.py register the manifest with API hub
│ ├── update_taxonomy.py create/update API hub attribute taxonomy
│ └── common/ shared libraries (retry, IAM, schema)
├── skills/ example skills
│ ├── apigee-policy-top10/ skill that documents Apigee policy patterns
├── skills/ shipped skills
│ ├── skill-finder/ consumer-side discovery + install client
│ ├── skill-publisher/ author-side pipeline orchestrator
│ ├── apigee-policy-top10/ example skill that reports top Apigee policies
│ ├── currency-converter/ minimal example
│ └── weather-lookup/ minimal example
├── examples/
Expand Down Expand Up @@ -93,42 +115,54 @@ references/apigee-skills-serving/
6. The roles `roles/apihub.editor` and `roles/storage.objectCreator` on
the target project.

## Quickstart
## Quickstart (five minutes)

The fast path uses two shell scripts:

```bash
# 1. Clone and enter the reference
# 1. Install skill-finder + skill-publisher on your machine.
curl -fsSL https://raw.githubusercontent.com/apigee/devrel/main/\
references/apigee-skills-serving/bin/install-skill-finder.sh \
| bash -s -- --runtime gemini # or --runtime opencode / antigravity

# 2. Clone the reference and provision your GCP project.
git clone https://github.com/apigee/devrel.git
cd devrel/references/apigee-skills-serving
bash bin/provision.sh --project MY_PROJECT --yes
```

# 2. Install Python dependencies into a virtualenv
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
`provision.sh` enables the required GCP APIs, creates a GCS
bucket, sets up the API hub attribute taxonomy, generates a
fresh Ed25519 signing keypair on your machine (private key
stays there), and publishes the three example skills. Every
step is idempotent — re-run any time to reconcile drift.

Full runbook (including troubleshooting and key rotation):
[`docs/provisioning.md`](docs/provisioning.md).

### Under the hood

# 3. Set up environment variables (edit values to match your project)
cp env.sh.example env.sh
$EDITOR env.sh
. ./env.sh

# 4. Verify prerequisites
./bin/check-prerequisites.sh

# 5. (One-time) Create the API hub attribute taxonomy
python3 scripts/update_taxonomy.py \
--project "$APIHUB_PROJECT" \
--location "$APIHUB_LOCATION"

# 6. Pack, sign, upload, register a skill
python3 scripts/pack_skill.py skills/currency-converter /tmp/cc.skill
python3 scripts/sign_skill.py /tmp/cc.skill --key-file ./signing.key
python3 scripts/upload_skill.py /tmp/cc.skill --bucket "$GCS_BUCKET"
python3 scripts/register_skill.py \
--project "$APIHUB_PROJECT" --location "$APIHUB_LOCATION" \
--manifest /tmp/cc.skill
The scripts above chain the five underlying operations that
this reference exists to demonstrate:

```bash
# For each skill:
python3 -m scripts.pack_skill --src skills/<name> --out /tmp/<name>.skill
python3 -m scripts.upload_skill --zip /tmp/<name>.skill --bucket "$GCS_BUCKET"
python3 -m scripts.sign_skill --manifest <patched-manifest> \
--zip /tmp/<name>.skill \
--priv-key <signing.raw> \
--out <signed-manifest>
python3 -m scripts.register_skill --manifest <signed-manifest> \
--project "$APIHUB_PROJECT" \
--location "$APIHUB_LOCATION"
```

Full walkthrough: [`docs/publish-and-install.md`](docs/publish-and-install.md).
Design rationale and trust model: [`docs/architecture.md`](docs/architecture.md).
Full walkthrough:
[`docs/publish-and-install.md`](docs/publish-and-install.md).

Design rationale and trust model:
[`docs/architecture.md`](docs/architecture.md).

## Running the tests

Expand All @@ -144,26 +178,41 @@ pytest -q
`pipeline.sh` runs the same suite and is what apigee/devrel CI invokes
nightly.

## Example skills
## Shipped skills

| Skill | Purpose |
| ---------------------- | --------------------------------------------------------------------------------------------------- |
| `currency-converter` | Minimal example. A `SKILL.md` plus a `manifest.yaml`. Useful as a copy-and-edit starting point. |
| `weather-lookup` | Minimal example demonstrating a skill with API-key-based external HTTP calls. |
| `apigee-policy-top10` | A skill that documents the ten most useful Apigee policy patterns, with a script that enumerates |
| | the policies present in your org. See [`docs/policy-skill-catalog.md`](docs/policy-skill-catalog.md). |
| `examples/apigee-proxy-skill` | A complete, production-shaped skill: 18 MCP tools, 25 Jinja2 policy templates, full manifest. |
| Skill | Purpose |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `skill-finder` | Consumer-side. Discovers, verifies, and installs signed skills from the customer's API hub. Multi-key trust root; per-deployment provisioning. |
| `skill-publisher` | Author-side. Wraps pack → sign → upload → register into a single skill an agent can invoke on a source skill directory. |
| `currency-converter` | Minimal example. A `SKILL.md` plus a `manifest.yaml`. Useful as a copy-and-edit starting point. |
| `weather-lookup` | Minimal example demonstrating a skill with API-key-based external HTTP calls. |
| `apigee-policy-top10` | A skill that documents the ten most useful Apigee policy patterns, with a script that enumerates the policies present in your org. See [`docs/policy-skill-catalog.md`](docs/policy-skill-catalog.md). |
| `examples/apigee-proxy-skill` | A complete, production-shaped skill: 18 MCP tools, 25 Jinja2 policy templates, full manifest. |

## Limitations and non-goals

- The skill registry uses API hub's standard search; ranking is keyword
overlap, not semantic. For semantic ranking, integrate a vector
search component separately.
- The publisher and consumer share an Ed25519 trust root. Key rotation
is a manual operator workflow; this reference does not implement
automatic rotation.
- Skills are sandboxed by the consumer runtime (OpenCode, agent host).
This reference does not introduce additional sandboxing on top.
- The skill registry uses API hub's standard search; ranking is
keyword overlap, not semantic. For semantic ranking, integrate
a vector search component separately.
- The trust root is **per-deployment**, not shared. Each customer
who runs `provision.sh` generates their own Ed25519 keypair on
their machine. The `apigee/devrel` repo intentionally contains
no signing key material and no signed release bundles;
skill-finder itself is trusted by provenance (TLS + `apigee`
GitHub org). See
[`docs/architecture.md#trust-model`](docs/architecture.md#trust-model)
for the full rationale.
- Key rotation is a supported operator workflow (multi-key
trust root; add-then-remove pattern with no install-time gap),
but is not automated. See
[`docs/provisioning.md#key-rotation-with-zero-downtime`](docs/provisioning.md#key-rotation-with-zero-downtime).
- The GCS bucket for signed bundles must be public-read because
skill-finder downloads via anonymous HTTPS. This is a demo
choice; production would use signed URLs or an authenticated
consumer.
- Skills are sandboxed by the consumer runtime (OpenCode, Gemini
CLI, Antigravity, custom MCP hosts). This reference does not
introduce additional sandboxing on top.

## License

Expand Down
70 changes: 50 additions & 20 deletions references/apigee-skills-serving/bin/demo-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# If you re-target the demo at a different GCP project, edit
# these four lines. Everything else flows from here.

readonly DEMO_APIHUB_PROJECT="apigee-product-demo"
readonly DEMO_APIHUB_PROJECT="geirs-spaces-demo"
readonly DEMO_APIHUB_LOCATION="us-west1"
readonly DEMO_APIGEE_ORG="apigee-product-demo"
readonly DEMO_APIGEE_ORG="geirs-spaces-demo"
readonly DEMO_KEYWORD_OVERLAP="1"

# ---- Color codes -----------------------------------------
Expand Down Expand Up @@ -185,7 +185,14 @@ while [ $# -gt 0 ]; do
esac
done

# ---- Step 1: prereq check + ADC --------------------------
# ---- Step 1: ADC ----------------------------------------
#
# ADC is checked FIRST because check-prerequisites.sh also
# checks it and we want a specific, actionable failure line
# (`gcloud auth application-default login`) before the more
# generic prereq output. If ADC is broken, the prereq check
# would spend time on env-var checks whose answers don't
# matter until ADC is fixed.

echo "[setup] Verifying Application Default Credentials..."
if gcloud auth application-default print-access-token \
Expand All @@ -199,24 +206,22 @@ else
exit 1
fi

if [ "$skip_preflight" -eq 0 ]; then
echo
echo "[setup] Running pre-flight checker..."
if bash "${REPO_ROOT}/bin/check-prerequisites.sh"; then
_check "All required prerequisites met" 0
else
_check "Pre-flight failed" 1 \
"see [prereq] lines above"
echo
echo "Fix the failing prereqs, then re-run:"
echo " ./bin/demo-setup.sh"
exit 1
fi
else
echo "[setup] --skip-preflight given; pre-flight check skipped."
fi

# ---- Step 2: env export + persistent config --------------
#
# We MUST export the demo env vars BEFORE running the
# pre-flight checker. The checker reads APIHUB_PROJECT,
# APIHUB_LOCATION, APIGEE_ORG, and APIGEE_SKILLS_MIN_KEYWORD_OVERLAP
# from the current process environment. In a fresh shell (the
# common case -- an operator has just opened a terminal to
# start the demo), none of those are set yet. If we ran the
# checker before the export, it would fail on 3 of 4 required
# variables and abort, even though the demo is fully
# self-configuring and would work if we let it run.
#
# The persistent config file at ~/.config/apigee-skills-demo/
# is written here for the same reason: subprocesses launched
# without the exports still resolve the values via
# scripts/common/config.py's fallback.

_export_demo_env

Expand All @@ -233,6 +238,31 @@ echo "[setup] in the calling process)..."
_write_demo_config_file
echo

# ---- Step 3: pre-flight checker -------------------------
#
# Runs AFTER the export so the checker sees the demo values.
# The checker still enforces the invariant on behalf of an
# operator who prefers to source env.sh manually -- it just
# won't spuriously fail when the operator relied on this
# script to configure the shell.

if [ "$skip_preflight" -eq 0 ]; then
echo "[setup] Running pre-flight checker..."
if bash "${REPO_ROOT}/bin/check-prerequisites.sh"; then
_check "All required prerequisites met" 0
else
_check "Pre-flight failed" 1 \
"see [prereq] lines above"
echo
echo "Fix the failing prereqs, then re-run:"
echo " ./bin/demo-setup.sh"
exit 1
fi
else
echo "[setup] --skip-preflight given; pre-flight check skipped."
fi
echo

# ---- Step 3: ready --------------------------------------

echo -e "${GREEN}[setup] READY.${NC}"
Expand Down
Loading
Loading