Plugin quickstart
Zero to a running plugin in about 30 minutes.
This guide walks you from an empty directory to a signed plugin running in a tenant sandbox.
What you'll build
A minimal plugin that:
- Registers one MCP tool (
hello_learner) with the Plugin Gateway. - Consumes
enrollment.completed.v1from Kafka. - Logs its LLM token usage to the telemetry pipeline.
Prerequisites
dockerandpnpm(oruvfor Python plugins)- Publisher account with a Cosign signing key
adapstoryCLI:curl -fsSL https://adapstory.com/cli/install | sh
If you don't have a publisher account yet, request one in #adapstory-plugins or via the admin portal. You'll get a signing key and a sandbox tenant to test in.
Steps
Scaffold the project
adapstory plugin new hello-learner --runtime python
cd hello-learnerYou'll get:
hello-learner/
├── plugin.yaml # manifest
├── pyproject.toml # uv-managed deps
├── src/
│ └── hello_learner/
│ ├── __init__.py
│ ├── main.py # FastAPI app
│ └── tools.py # @mcp_tool handlers
├── tests/
└── DockerfileDeclare the manifest
Open plugin.yaml and fill in the integration points:
apiVersion: adapstory.com/v1
kind: Plugin
metadata:
name: hello-learner
version: 0.1.0
publisher: your-org
spec:
runtime:
image: registry.adapstory.com/your-org/hello-learner:0.1.0
resources:
requests: { cpu: 100m, memory: 256Mi }
limits: { cpu: 500m, memory: 512Mi }
mcpTools:
- name: hello_learner
description: Greet a learner by name with an AI-generated message.
endpoint: /mcp/hello
schema: mcp/hello-learner.schema.json
events:
consumes: [enrollment.completed.v1]
llm:
gatewayBudget: 100000
defaultModel: claude-haiku-4-5-20251001
guardrails:
content: [no-pii, education-safe]The full field reference is here.
Implement the MCP tool
# src/hello_learner/tools.py
from adapstory_plugin_sdk import mcp_tool, LlmGateway
@mcp_tool(name="hello_learner")
async def hello_learner(
learner_name: str,
gateway: LlmGateway,
) -> dict:
"""Return a friendly, education-safe greeting."""
response = await gateway.chat(
model="claude-haiku-4-5-20251001",
messages=[
{"role": "system", "content": "You are an encouraging tutor."},
{"role": "user", "content": f"Say hi to {learner_name} in one short sentence."},
],
)
return {"message": response.text}// src/tools.ts
import { mcpTool, type LlmGateway } from '@adapstory/plugin-sdk';
export const helloLearner = mcpTool({
name: 'hello_learner',
async handler({ learnerName }: { learnerName: string }, ctx: { gateway: LlmGateway }) {
const response = await ctx.gateway.chat({
model: 'claude-haiku-4-5-20251001',
messages: [
{ role: 'system', content: 'You are an encouraging tutor.' },
{ role: 'user', content: `Say hi to ${learnerName} in one short sentence.` },
],
});
return { message: response.text };
},
});The SDK injects LlmGateway — never call model providers directly.
Run it locally
adapstory plugin devThis spins up a dockerised sandbox with a mock Plugin Gateway and a mock tenant. Invoke your tool:
curl -XPOST http://localhost:8787/mcp/invoke \
-H 'content-type: application/json' \
-d '{"tool":"hello_learner","args":{"learnerName":"Alex"}}'You should see a greeting — and a telemetry line showing the mock token spend.
Sign and publish
adapstory plugin build # → registry.adapstory.com/your-org/hello-learner:0.1.0
adapstory plugin sign # → Cosign signature + SBOM + SLSA provenance
adapstory plugin publish # → BC-02 picks it up, verifies signature, makes it installableThat last step is fail-closed: unsigned images are rejected at admission.
Install into a tenant
adapstory plugin install hello-learner --tenant sandbox-tenant-1Poll status until ACTIVE:
adapstory plugin status hello-learner --tenant sandbox-tenant-1 --watchWhat you have now
- A signed plugin image in the registry.
- An active install in a sandbox tenant, visible in ArgoCD under
plugin-sandbox-tenant-1-hello-learner. - Telemetry flowing: traces in Tempo, CPU profile in Pyroscope, logs in Loki.
Next steps
- Manifest reference — every field and its meaning.
- MCP tools — deep dive on schemas, streaming, and cancellation.
- Lifecycle — how upgrades, rollbacks, and multi-tenant rollouts work.
- Observability — make your plugin debuggable in production.