Subchapter 2.5
references/plugin-spec.mdMarkdown33 KBView on GitHub
This file is the source of truth for authoring native Base MCP plugins. It applies to both internally maintained plugins and third-party-authored plugins submitted via PR.
It is written to be self-contained: if you are an agent and someone hands you only this file plus a protocol’s docs (or an existing, non-conforming plugin file), this spec alone should tell you how to produce — or fix — a conforming plugin. Work top to bottom: decide the frontmatter, then write the body sections in canonical order. To bring an existing file into line, jump to .
A plugin is a single Markdown file at skills/base-mcp/plugins/<slug>.md: YAML frontmatter (routing signals for the agent) followed by a fixed, canonically-named body. No build step or validator runs today — conformance is by review — so the file must be correct by construction.
To make that review repeatable, this repo ships the plugin-review skill (.claude/skills/plugin-review/). It validates a plugin file against this spec and produces a conformance report with actionable findings. Plugin authors should run it to self-check a plugin before opening a PR, and reviewers use it to evaluate incoming submissions. Invoke it in Claude Code with /plugin-review (or just ask to “review my plugin against the spec”).
Every plugin file must open with YAML frontmatter. Fields marked required must be present; others default as noted.
| Field | Type | Description |
|---|---|---|
title | string | "<Protocol> Plugin" |
description | string | One line: what the plugin does + how it routes to Base MCP. |
tags | string[] | Lowercase capability/category keywords used for discovery. The agent matches a user’s request to a plugin by these (surfaced in the SKILL.md plugins table). See Choosing each field’s value. |
name | string | Lowercase slug (e.g. morpho). Used for cross-links and the SKILL.md table. |
version | string | Plugin doc version, semver (e.g. 0.2.0). Bump on meaningful changes (a spec-conformance restructure counts). |
integration | enum | See Integration Types. |
chains | string[] | Base MCP chain strings the plugin targets, limited to chains Base MCP supports (e.g. [base], [base, optimism]). |
All optional. Defaults: shell: none, allowlist: [], externalMcp: null, cliPackage: null, auth: none, risk: [].
| Field | Type | Description |
|---|---|---|
requires.shell | required | optional | none | Whether shell/terminal access is needed. |
requires.allowlist | string[] | Hosts that must be in the Base MCP web_request allowlist. |
requires.externalMcp | { name, transport?: http|sse, url } | { name, transport: stdio, command, args, env? } | null | Separate MCP server the plugin depends on. transport: http | sse (remote, hosted) needs url; transport: stdio (local, launched on the user’s machine) needs command, args, and optional env. transport is optional for remote MCPs and defaults to http when url is present (so the legacy { name, url } shape stays valid); it is required for stdio. See MCP Provisioning. |
requires.cliPackage | string | null | npx/uvx invocation for a CLI the agent shells out to per call (e.g. npx @morpho-org/cli@latest). This is not for an MCP server you register once — use externalMcp with transport: stdio for that. |
auth | none | api-key | siwe-jwt | oauth-on-install | Auth model used by the plugin’s external services. |
risk | string[] | Risk tags that trigger ## Risks & Warnings. See the tag list below. |
Derive every value from the protocol’s actual behavior — don’t copy another plugin’s flags blindly.
integration — answer, in order, until one fits (the first match wins; this is the most specific value):
cli-only.http-api.external-mcp.swap, send) with no external service → semantic-base-tool.hybrid. Document the per-surface paths in ## Surface Routing.chains — list the Base MCP chain strings (e.g. base, optimism) the plugin targets — these are the same strings the chain param takes in Base MCP tool calls like send_calls. Limit to chains Base MCP supports: intersect the protocol’s supported networks with Base MCP’s. The supported chain strings are:
arbitrum, avalanche, base, base-sepolia, bsc, ethereum, optimism, polygon
(base-sepolia is the only testnet; swap is mainnet-only.) Read the chain parameter on the Base MCP tools to confirm the current set — it may change over time. If the plugin never routes an onchain transaction through Base MCP (e.g. an external MCP that only uses a Base MCP signature to log in), use [].
tags — 3–5 lowercase, hyphenated keywords describing what the user can do — capability and category, not the protocol name (the name already covers that). These drive routing: the agent reads the SKILL.md tags column to decide which plugin matches a request. Reuse existing tags where they fit so similar plugins cluster, but add new tags as you see fit. Current vocabulary: lending, borrowing, yield, vaults, dex, swap, liquidity, perps, leverage, derivatives, trading, token-launches, memecoins, discovery, ai-agents, agent-commerce, payment-cards, email, nft, marketplace, drops — when you introduce a new tag, add it to this list so the vocabulary stays shared.
requires.shell:
required — the plugin cannot function without a shell/terminal (its only path is a CLI). On shell-less surfaces the agent must stop.optional — a shell unlocks a richer path (a CLI, or a tx-builder), but the plugin still works without one via an HTTP/MCP/UI fallback.none — the plugin never needs a shell (pure HTTP API or external MCP).requires.allowlist — every external host the plugin fetches over HTTP. These must be on the Base MCP web_request allowlist for chat-only surfaces to reach them. Leave [] for cli-only and external-mcp plugins that make no direct HTTP calls.
requires.externalMcp — set when the plugin depends on a separate MCP server; otherwise null. Pick the transport first, then fill the matching fields (full schema and guardrails in MCP Provisioning):
transport: http or sse) — a hosted server the agent connects to over the network. Provide url (e.g. https://mcp.<protocol>.example/mcp). This is the lower-trust case: the server runs on the third party’s infra, not the user’s machine. transport is optional here and defaults to http when url is present, so the legacy { name, url } shape remains valid; set transport: sse explicitly when needed.transport: stdio) — a server launched on the user’s machine (typically via npx/uvx). Provide command, args (with a pinned version, never @latest), and env (required env-var names only, never values). A local MCP is arbitrary code execution on the user’s machine, so it also requires the local-exec risk tag and a ## Surface Routing stop on shell-less surfaces.requires.cliPackage — the exact invocation string for a CLI the agent shells out to per call (e.g. npx @morpho-org/cli@latest, or a full uvx --from <spec> <cmd> command); otherwise null. If the package is an MCP server (registered once and then queried as tools, not invoked per call), leave cliPackage: null and use requires.externalMcp with transport: stdio instead.
auth:
none — public endpoints, no credentials.api-key — a static API key / header is required on requests.siwe-jwt — Sign-In-With-Ethereum producing a session JWT (the challenge is signed via Base MCP sign).oauth-on-install — OAuth performed when the MCP/connector is installed.risk — include every tag that applies; any non-empty risk makes ## Risks & Warnings required. Tags:
| Tag | Use when |
|---|---|
liquidation | Leveraged or borrow positions can be liquidated. |
slippage | Swaps/trades can fill materially worse than quoted. |
low-liquidity | Thin markets — price impact, failed fills, rug risk (e.g. fresh token launches). |
pii | The plugin handles personal/sensitive data (emails, card numbers, OTPs, 3DS codes). |
irreversible | Actions can’t be undone once submitted (most onchain writes; flag when worth emphasizing). |
local-exec | The plugin runs third-party code on the user’s machine — a local (transport: stdio) MCP server or a cliPackage. Required whenever externalMcp.transport: stdio is set. |
---
title: "Moonwell Plugin"
description: "Lending on Moonwell via HTTP API → send_calls on Base and Optimism."
tags: [lending, borrowing, yield]
name: moonwell
version: 0.2.0
integration: http-api
chains: [base, optimism]
requires:
shell: none
allowlist: [api.moonwell.fi]
externalMcp: null
cliPackage: null
auth: none
risk: [liquidation]
---The integration field classifies how the plugin reaches Base MCP. Choose the most specific type that applies (use the ordered questions in Choosing each field’s value); use hybrid only when a single type can’t describe the routing.
The Examples column is illustrative and maintainer-managed — don’t add your plugin to it in a contribution PR (see Contribution Scope).
| Value | What it means | Required body sections it implies | Examples |
|---|---|---|---|
cli-only | All calls go through a shell CLI. No HTTP API, no external MCP. | ## Commands; shell: required; cliPackage set | Aerodrome |
http-api | Plugin calls an HTTP API (via web_request or harness HTTP tool) to read data or build calldata. | ## Endpoints; list allowlist hosts | Moonwell, Uniswap, Bankr |
external-mcp | Plugin relies on a separate MCP server — remote (hosted, transport: http|sse) or local (launched on the user’s machine, transport: stdio). The agent reads that MCP’s own tool catalog — this plugin file does not enumerate its tools. | ## Detection + ## Installation; externalMcp set (+ local-exec risk for stdio); omit ## Endpoints/## Commands | remote (http/sse), local (stdio) |
semantic-base-tool | Plugin composes Base MCP’s higher-level semantic tools (swap, send) rather than producing raw calldata. | ## Submission names the semantic tool | (future) |
hybrid | Combines two or more paths with surface-dependent routing. | union of the above; document the routing matrix in ## Surface Routing | Avantis, Morpho |
external-mcp plugins (and hybrid plugins with an MCP path) depend on a separate MCP server. That server is reached one of two ways, set by externalMcp.transport. Choose the transport first; it determines the rest of the externalMcp object, the ## Installation snippet, and the risk tags.
A hosted server the agent connects to over the network. The third party runs it on their own infrastructure.
requires:
externalMcp:
name: <protocol>
transport: http # or sse
url: https://mcp.<protocol>.example/mcp
shell: none
auth: oauth-on-install # or api-key / none, as the server requiresurl is required; command/args/env are omitted.local-exec risk is implied (other risks still apply on their own merits).## Surface Routing states the fallback.A server launched on the user’s machine, typically via npx/uvx, communicating over stdio. The harness registers it once in its MCP client config.
requires:
externalMcp:
name: <protocol>
transport: stdio
command: npx
args: ["-y", "@<org>/mcp@1.2.3"] # PIN the version — never @latest
env: [PROTOCOL_API_KEY] # required env-var NAMES only — never values
shell: required
risk: [local-exec] # required for any stdio MCPcommand + args are required; url is omitted.@latest lets the third party ship new code to every user with no review — pin an exact version and bump it in a tracked PR (same discipline as version).env lists names only. Never put secret values in the plugin file; the user provisions them. Document what each is and how to obtain it in ## Auth.local-exec risk is mandatory. Running a third-party package is arbitrary code execution on the user’s machine — a categorically larger trust surface than a remote MCP or an http-api plugin. ## Risks & Warnings must spell out that the user is installing and running third-party code.## Surface Routing must stop on shell-less / consumer surfaces. A local MCP only runs where there is a shell and a user-managed MCP config (e.g. Claude Code, Codex, Cursor). On Claude.ai / ChatGPT the agent must stop and tell the user the integration requires a local install — it must not improvise a web_request workaround.Provide a copy-pasteable MCP client config snippet for the common harnesses (Claude Code, Codex, Cursor / JSON-config) — and, for remote MCPs that support it, Claude.ai / ChatGPT connectors. For local (stdio) MCPs the snippet is the standard command/args/env client entry; show the pinned version and name each required env var (without values). CLIs invoked per call via npx/uvx need no install step — say so and show the invocation instead.
This schema is additive. The pre-transport shape externalMcp: { name, url } is still valid and is read as a remote http MCP (transport defaults to http when url is present). Existing external-mcp plugins need no change to keep working; add transport on the next meaningful edit (and bump version), the same way heading-name synonyms are migrated. New plugins should set transport explicitly, and stdio always requires it. A future validator should treat a missing transport as an inferred default (warn), not an error.
## Surface Routing and ## Submission describe how the plugin executes. This is the menu of paths and tools they draw from — know it before writing those sections.
Execution paths (an agent picks one per call, depending on the surface):
web_request — a server-side fetch for chat-only surfaces (Claude.ai, ChatGPT). Only reaches hosts on the web_request allowlist.requires.cliPackage command. Needs requires.shell ≠ none.requires.externalMcp), reading their descriptions from the MCP itself. The server is either remote (hosted url) or local (stdio, launched on the user’s machine); see MCP Provisioning.The full decision tree (harness HTTP → web_request → user-paste), and the GET-only constraint on consumer surfaces, live in custom-plugins.md. Reference it from ## Surface Routing rather than restating it.
Base MCP submission tools — what ## Submission names:
| Tool | Use for |
|---|---|
send_calls | A batch of unsigned { to, value, data } calls (EIP-5792). The target for any plugin that builds raw calldata. See batch-calls.md. |
swap | Base MCP’s semantic swap — symbol/address in, routing handled for you. |
sign | Message signing (e.g. a SIWE login challenge). |
none | The plugin doesn’t submit through Base MCP (e.g. an external MCP executes against its own backend after a Base MCP sign). State this explicitly. |
Any write tool that returns an approval URL follows the approval/polling flow in approval-mode.md.
Sections appear in this order. R = required in every plugin. C = conditional on frontmatter flags. O = optional.
| # | Heading | Status | Condition |
|---|---|---|---|
| 1 | > [!IMPORTANT] onboarding callout | R | Always — pointer to Base MCP onboarding in SKILL.md. |
| 2 | ## Overview | R | Always — 1 paragraph: protocol summary, chain(s), routing one-liner. |
| 3 | ## Detection | C | integration is external-mcp, or hybrid with an MCP path. |
| 4 | ## Installation | C | requires.externalMcp or requires.cliPackage is set. |
| 5 | ## Auth | C | auth != none. |
| 6 | ## Surface Routing | R | Always — table or prose mapping capability × surface to execution path. |
| 7 | ## Endpoints or ## Commands | C | http-api → use ## Endpoints. cli-only or CLI path of hybrid → use ## Commands. external-mcp plugins omit this section entirely — the agent reads the MCP’s own tool catalog. |
| 8 | ## Orchestration | R | Always — ordered steps from user intent to Base MCP call. Use ### sub-headings for sub-flows. |
| 9 | ## Submission | R | Always — names the target Base MCP tool (send_calls, swap, sign, or none) and any mapping or normalization needed to reach it. |
| 10 | ## Example Prompts | R | Always — 2–4 concrete prompt → numbered steps. |
| 11 | ## Risks & Warnings | C | risk is non-empty. |
| 12 | ## Notes | O | Constants, token addresses, known gotchas. |
> [!IMPORTANT] onboarding callout — one line telling the agent to run Base MCP onboarding (defined in SKILL.md) before any plugin call. Note any session prerequisite here too (e.g. “authenticate once per session — see ## Auth“).## Overview — one paragraph: what the protocol is, which chain(s) it runs on, and a one-line routing statement (what the plugin reads/builds and which Base MCP tool it lands on). Say whether it returns unsigned calldata or executes through a semantic tool.## Detection (external-mcp / hybrid-with-MCP) — how the agent tells whether the required MCP/tooling is present, e.g. “if no xyz_* tools are exposed, the MCP isn’t installed → see ## Installation.” Don’t reach the protocol’s HTTP API directly when the MCP is the supported path.## Installation (externalMcp or cliPackage set) — per-harness install steps (Claude Code, Codex, Cursor / JSON-config, Claude.ai, ChatGPT) plus a config snippet for MCPs. For externalMcp follow MCP Provisioning: a remote (http/sse) connector entry, or a local (stdio) command/args/env entry with a pinned version. CLIs that run via npx/uvx need no install step — say so and show the invocation.## Auth (auth ≠ none) — the exact credential or flow: the header name for api-key; the ordered step sequence for siwe-jwt (get address → start → sign → complete → reuse token), token lifetime, and how to refresh; the connector setup for oauth-on-install.## Surface Routing — a table mapping each capability (typically split read vs write) × surface → execution path, using the Runtime Routing Primitives. Always state what happens on a shell-less / chat-only surface: the fallback, or an explicit “stop.” For cli-only, state plainly that no-shell surfaces are unsupported and the agent must not improvise a web_request/paste workaround.## Endpoints / ## Commands — http-api → ## Endpoints: each endpoint with method, URL, parameters, and response shape. cli-only / the CLI path of hybrid → ## Commands: each command with flags and output shape. external-mcp omits both — the agent reads the MCP’s catalog at runtime.## Orchestration — the happy-path sequence from user intent to the Base MCP call, as ordered steps (read state → build calldata → submit → confirm). Use ### sub-flows for distinct operations (e.g. swap vs LP, CLI path vs MCP path). Say where the wallet address comes from (get_wallets) and call out any pre-submit validation.## Submission — name the target tool (send_calls / swap / sign / none) and show the exact mapping from the endpoint/command/MCP output into that tool’s input: the { to, value, data } normalization, chain-string mapping, and batching order (approvals before the action). Reference approval-mode.md for the approval/polling flow.## Example Prompts — 2–4 realistic user prompts, each followed by numbered steps that reference the sections above. Cover the main capabilities (a read, a primary write, and at least one edge such as a management action or chat-only fallback).## Risks & Warnings (risk non-empty) — one bullet per risk tag: the hazard, the guardrail (what to check, e.g. health factor / slippage threshold), what to confirm with the user, and what never to do silently (e.g. auto-raise slippage, auto-buy).## Notes (optional) — constants, token/contract addresses, unit-scaling rules, gotchas, and deep reference tables. This is where conservatively-preserved detail lands when adapting a large existing plugin.Use these exact names. Synonyms from older plugins must be renamed on the next meaningful edit.
| Correct | Instead of |
|---|---|
## Risks & Warnings | “Safety Rules”, “Safety Notes”, “Execution Warnings”, “Important Notes” |
## Orchestration | “Orchestration Pattern”, “Swap Orchestration”, “CLI Orchestration” |
## Endpoints | “API Services”, “Endpoint reference” |
## Commands | “Morpho CLI Path”, “Runner” |
## Surface Routing | “Surface routing”, inline routing prose inside ## Overview |
## Auth | “Auth Headers”, “Auth Flow” |
## Detection | “Environment Detection”, “MCP Server” |
## Submission | “Base MCP Conversion”, inline send_calls blocks |
## Example Prompts | “Example Flows” |
Follow these steps to write a new plugin file (skills/base-mcp/plugins/<slug>.md). The goal is a file an agent can route from at runtime and a third party can reproduce mechanically.
integration value using the ordered questions in Choosing each field’s value. This choice drives which body sections are required (see the Integration Types table).risk — it’s what triggers ## Risks & Warnings and shapes the agent’s caution.## Submission must say which Base MCP tool the flow lands on — send_calls, swap, sign, or none — and the exact mapping/normalization needed to get there.## Orchestration walks user intent → Base MCP call as ordered steps. ## Example Prompts gives 2–4 concrete prompts, each mapped to numbered steps.plugin-review skill to validate the file against this spec and resolve its findings, then open a PR that adds skills/base-mcp/plugins/<slug>.md.Keep a plugin PR’s diff minimal. A contribution should change only:
skills/base-mcp/plugins/<slug>.md (the file you are adding). This is the only file most plugin PRs touch.tag — by appending it to the list in Choosing each field’s value. Don’t otherwise edit that list.Do not edit the following in a plugin PR — they are maintainer-managed and updated when a plugin is reviewed and accepted into the registry:
skills/base-mcp/SKILL.md (the registry of available plugins).Maintainers add the SKILL.md registry row and the conformance entry after review. Including these edits in a contribution is unnecessary and causes merge conflicts between in-flight submissions.
Given a non-conforming or partially-conforming plugin file (or a protocol’s raw docs), bring it into conformance conservatively — without losing content:
version.## Overview? Write one from the intro prose.## Surface Routing? Derive the table from how the file says calls are made.## Submission? Extract the “how we send it” content (often buried in an orchestration step or a raw send_calls block) into its own section and name the tool.## Example Prompts? Write 2–4 from the documented capabilities.## Notes or as ### subsections rather than deleting it. When you change a heading that internal links point to, keep the link text in sync — Markdown anchors are derived from heading text, so a rename can break #anchor references elsewhere in the file.plugin-review skill to confirm the adapted file conforms to this spec.Copy this, fill the placeholders, and delete any C/O sections that don’t apply to your integration:
---
title: "<Protocol> Plugin"
description: "<one line: what it does + how it routes to Base MCP>"
tags: [<3-5 capability keywords>]
name: <slug>
version: 0.2.0
integration: cli-only | http-api | external-mcp | semantic-base-tool | hybrid
chains: [base]
requires:
shell: none
allowlist: []
externalMcp: null # remote: { name, transport: http|sse, url }; local: { name, transport: stdio, command, args, env } — see MCP Provisioning
cliPackage: null
auth: none
risk: [] # add local-exec when externalMcp.transport: stdio
---
# <Protocol> Plugin
> [!IMPORTANT]
> Run Base MCP onboarding first (see SKILL.md). <one-line pointer; note any per-session prerequisite.>
## Overview
<1 paragraph: protocol summary, chain(s), one-line routing statement; unsigned-calldata vs semantic tool.>
<!-- ## Detection — external-mcp or hybrid-with-MCP only: how to tell the MCP/tooling is present. -->
<!-- ## Installation — only if requires.externalMcp or requires.cliPackage is set. -->
<!-- ## Auth — only if auth != none: header / SIWE steps / OAuth, token lifetime + refresh. -->
## Surface Routing
<table mapping capability × surface → execution path (harness HTTP tool, web_request, CLI, external MCP, UI fallback). Always state the shell-less / chat-only behavior.>
<!-- ## Endpoints — http-api: method, URL, params, response per endpoint. -->
<!-- ## Commands — cli-only / CLI path of hybrid: each command, flags, output. -->
<!-- external-mcp omits Endpoints/Commands entirely. -->
## Orchestration
<ordered steps from user intent to the Base MCP call (read → build → submit → confirm). Use ### sub-flows.>
## Submission
<names the target Base MCP tool: send_calls | swap | sign | none, plus the exact mapping/normalization.>
## Example Prompts
<2–4 concrete prompts, each → numbered steps referencing the sections above.>
<!-- ## Risks & Warnings — only if risk is non-empty: one bullet per risk tag (hazard + guardrail). -->
## Notes
<constants, token addresses, scaling rules, known gotchas, deep reference material.>Before opening a PR, confirm:
integration, shell, auth, risk tags); tags set for discovery (reusing existing vocabulary where it fits).integration is the most specific value that fits; flags (shell, allowlist, externalMcp, cliPackage, auth, risk) are accurate and derived from real behavior.> [!IMPORTANT] onboarding callout is first.## Overview, ## Surface Routing, ## Orchestration, ## Submission, ## Example Prompts all present (the R sections).## Detection/## Installation for external MCPs, ## Auth when auth != none, ## Risks & Warnings when risk is non-empty, ## Endpoints (http-api) or ## Commands (cli-only / CLI path).externalMcp: transport set, with the matching fields (url for http/sse; command/args/env for stdio). Local (stdio) MCPs pin an exact version (no @latest), list env names only, carry the local-exec risk tag, and ## Surface Routing stops on shell-less surfaces.## Surface Routing states the shell-less / chat-only behavior (fallback or stop).## Submission names a concrete Base MCP tool and shows the mapping into it.#anchor links were broken by a rename.plugins/<slug>.md is added (plus a net-new tag in the vocabulary list if applicable). The SKILL.md registry, the Integration Types Examples column, and the Existing Plugin Conformance table are left for maintainers.Maintainer-managed. Do not edit this table or its count in a plugin contribution PR — maintainers update it when a plugin is accepted (see Contribution Scope).
Current integration classification for the 7 native plugins:
| Plugin | integration | chains | tags | shell | auth | risk |
|---|---|---|---|---|---|---|
| Aerodrome | cli-only | [base] | [dex, swap, liquidity, staking] | required | none | [slippage] |
| Avantis | hybrid | [base] | [perps, leverage, trading, derivatives] | optional | none | [liquidation, slippage, irreversible] |
| Bankr | http-api | [base] | [token-launches, trading, memecoins, discovery] | none | none | [low-liquidity, irreversible] |
| Moonwell | http-api | [base, optimism] | [lending, borrowing, yield] | none | none | [liquidation] |
| Morpho | hybrid | [base] | [lending, borrowing, vaults, yield] | optional | none | [liquidation] |
| Uniswap | http-api | [base] | [dex, swap, liquidity] | none | api-key | [slippage] |
| Virtuals | external-mcp | [] | [ai-agents, agent-commerce, payment-cards, email] | none | siwe-jwt | [pii] |
All seven are at version: 0.2.0 as of the spec-conformance restructure.