Base MCP Plugin Specification
This file is the source of truth for authoring native Base MCP plugins. It applies to both internally maintained plugins and partner-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 How to Adapt an Existing Plugin. A plugin is a single Markdown file atskills/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”).
Frontmatter Schema
Every plugin file must open with YAML frontmatter. Fields marked required must be present; others default as noted.Required fields
Capability flags
All optional. Defaults:shell: none, allowlist: [], externalMcp: null, cliPackage: null, auth: none, risk: [].
Choosing each field’s value
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):- Every operation goes through a shell CLI, no HTTP API and no separate MCP →
cli-only. - The plugin calls an HTTP API to read data or build calldata, which you then submit through a Base MCP tool →
http-api. - The plugin depends on a separate MCP server whose tools the agent calls directly →
external-mcp. - The plugin only composes Base MCP’s own higher-level tools (
swap,send) with no external service →semantic-base-tool. - It needs two or more of the above depending on the surface (e.g. a CLI when a shell exists, an MCP/HTTP fallback otherwise) →
hybrid. Document the per-surface paths in## Surface Routing.
- Every operation goes through a shell CLI, no HTTP API and no separate MCP →
-
chains— list the Base MCP chain strings (e.g.base,optimism) the plugin targets — these are the same strings thechainparam takes in Base MCP tool calls likesend_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-sepoliais the only testnet;swapis mainnet-only.) Read thechainparameter 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 (thenamealready 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 MCPweb_requestallowlist for chat-only surfaces to reach them. Leave[]forcli-onlyandexternal-mcpplugins that make no direct HTTP calls. -
requires.externalMcp— set when the plugin depends on a separate MCP server; otherwisenull. Pick thetransportfirst, then fill the matching fields (full schema and guardrails in MCP Provisioning):- Remote MCP (
transport: httporsse) — a hosted server the agent connects to over the network. Provideurl(e.g.https://mcp.<protocol>.example/mcp). This is the lower-trust case: the server runs on the partner’s infra, not the user’s machine.transportis optional here and defaults tohttpwhenurlis present, so the legacy{ name, url }shape remains valid; settransport: sseexplicitly when needed. - Local MCP (
transport: stdio) — a server launched on the user’s machine (typically vianpx/uvx). Providecommand,args(with a pinned version, never@latest), andenv(required env-var names only, never values). A local MCP is arbitrary code execution on the user’s machine, so it also requires thelocal-execrisk tag and a## Surface Routingstop on shell-less surfaces.
- Remote MCP (
-
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 fulluvx --from <spec> <cmd>command); otherwisenull. If the package is an MCP server (registered once and then queried as tools, not invoked per call), leavecliPackage: nulland userequires.externalMcpwithtransport: stdioinstead. -
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 MCPsign).oauth-on-install— OAuth performed when the MCP/connector is installed.
-
risk— include every tag that applies; any non-emptyriskmakes## Risks & Warningsrequired. Tags:
Example frontmatter
Integration Types
Theintegration 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).
MCP Provisioning
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.
Remote MCP (transport: http | sse)
A hosted server the agent connects to over the network. The partner runs it on their own infrastructure.
urlis required;command/args/envare omitted.- No code runs on the user’s machine, so no
local-execrisk is implied (other risks still apply on their own merits). - Works on chat-only surfaces if the harness supports remote MCP connectors; otherwise
## Surface Routingstates the fallback.
Local MCP (transport: stdio)
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.
command+argsare required;urlis omitted.- Pin the version. A floating
@latestlets the partner ship new code to every user with no review — pin an exact version and bump it in a tracked PR (same discipline asversion). envlists 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-execrisk is mandatory. Running a partner package is arbitrary code execution on the user’s machine — a categorically larger trust surface than a remote MCP or anhttp-apiplugin.## Risks & Warningsmust spell out that the user is installing and running third-party code.## Surface Routingmust 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 aweb_requestworkaround.
## Installation content
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.
Backwards compatibility
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.
Runtime Routing Primitives
## 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):
- Harness HTTP tool — when the harness has a direct fetch/curl/shell (Claude Code, Codex, Cursor), use it first for HTTP calls: any method, no allowlist needed.
- Base MCP
web_request— a server-side fetch for chat-only surfaces (Claude.ai, ChatGPT). Only reaches hosts on theweb_requestallowlist. - Shell / CLI — runs a
requires.cliPackagecommand. Needsrequires.shell≠none. - External MCP — the agent calls tools advertised by a separate MCP server (
requires.externalMcp), reading their descriptions from the MCP itself. The server is either remote (hostedurl) or local (stdio, launched on the user’s machine); see MCP Provisioning. - UI / user-paste fallback — for chat-only surfaces that can’t make the call directly: link the user to the protocol’s web UI, or (GET-only) construct a URL and ask the user to paste it back so the agent may fetch it.
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:
Any write tool that returns an approval URL follows the approval/polling flow in approval-mode.md.
Required Body Sections
Sections appear in this order. R = required in every plugin. C = conditional on frontmatter flags. O = optional.What goes in each section
> [!IMPORTANT]onboarding callout — one line telling the agent to run Base MCP onboarding (defined inSKILL.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 noxyz_*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. ForexternalMcpfollow MCP Provisioning: a remote (http/sse) connector entry, or a local (stdio)command/args/enventry with a pinned version. CLIs that run vianpx/uvxneed no install step — say so and show the invocation.## Auth(auth ≠ none) — the exact credential or flow: the header name forapi-key; the ordered step sequence forsiwe-jwt(get address → start → sign → complete → reuse token), token lifetime, and how to refresh; the connector setup foroauth-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.” Forcli-only, state plainly that no-shell surfaces are unsupported and the agent must not improvise aweb_request/paste workaround.## Endpoints/## Commands—http-api→## Endpoints: each endpoint with method, URL, parameters, and response shape.cli-only/ the CLI path ofhybrid→## Commands: each command with flags and output shape.external-mcpomits 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 perrisktag: 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.
Canonical heading names
Use these exact names. Synonyms from older plugins must be renamed on the next meaningful edit.How to Author a New Plugin
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 partner can reproduce mechanically.
- Classify the integration. Pick the single most specific
integrationvalue using the ordered questions in Choosing each field’s value. This choice drives which body sections are required (see the Integration Types table). - Fill the frontmatter using the schema and the per-field guidance above. Every required field must be present; capability flags default as noted. Be honest about
risk— it’s what triggers## Risks & Warningsand shapes the agent’s caution. - Write the body sections in canonical order (see Required Body Sections and What goes in each section). Include every R section, every C section your frontmatter flags imply, and use the exact canonical heading names.
- Name the submission tool.
## Submissionmust say which Base MCP tool the flow lands on —send_calls,swap,sign, ornone— and the exact mapping/normalization needed to get there. - Show the happy path.
## Orchestrationwalks user intent → Base MCP call as ordered steps.## Example Promptsgives 2–4 concrete prompts, each mapped to numbered steps. - Self-review against the Authoring Checklist and confirm your diff stays within Contribution Scope. Run the
plugin-reviewskill to validate the file against this spec and resolve its findings, then open a PR that addsskills/base-mcp/plugins/<slug>.md.
Contribution Scope
Keep a plugin PR’s diff minimal. A contribution should change only:- Your plugin file —
skills/base-mcp/plugins/<slug>.md(the file you are adding). This is the only file most plugin PRs touch. - The tag vocabulary — only if you introduce a genuinely new
tag— by appending it to the list in Choosing each field’s value. Don’t otherwise edit that list.
- The plugins table in
skills/base-mcp/SKILL.md(the registry of available plugins). - The Examples column of the Integration Types table.
- The Existing Plugin Conformance table and its plugin count.
How to Adapt an Existing Plugin
Given a non-conforming or partially-conforming plugin file (or a protocol’s raw docs), bring it into conformance conservatively — without losing content:- Read the whole file and identify what it already documents: how it reaches the protocol (CLI? HTTP? external MCP?), which hosts/commands/tools it uses, what it submits, and any risks.
- Derive and add the frontmatter using Choosing each field’s value. If frontmatter is missing entirely, add the full block; if partial, fill the gaps. Bump
version. - Map existing headings to canonical names (see Canonical heading names). Rename synonyms; don’t create duplicates.
- Reorder sections into the canonical order from Required Body Sections.
- Add every missing R section, and every C section the frontmatter now implies. Synthesize them from existing content where possible:
- No
## Overview? Write one from the intro prose. - No
## Surface Routing? Derive the table from how the file says calls are made. - No
## Submission? Extract the “how we send it” content (often buried in an orchestration step or a rawsend_callsblock) into its own section and name the tool. - No
## Example Prompts? Write 2–4 from the documented capabilities.
- No
- Preserve content. Move deep reference material under
## Notesor 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#anchorreferences elsewhere in the file. - Self-review against the Authoring Checklist, then run the
plugin-reviewskill to confirm the adapted file conforms to this spec.
Plugin Skeleton Template
Copy this, fill the placeholders, and delete any C/O sections that don’t apply to your integration:Authoring Checklist
Before opening a PR, confirm:- Frontmatter present with all required fields; enum values are valid (
integration,shell,auth,risktags);tagsset for discovery (reusing existing vocabulary where it fits). -
integrationis 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 Promptsall present (the R sections). - Conditional sections included where flags demand:
## Detection/## Installationfor external MCPs,## Authwhenauth != none,## Risks & Warningswhenriskis non-empty,## Endpoints(http-api) or## Commands(cli-only / CLI path). - For
externalMcp:transportset, with the matching fields (urlforhttp/sse;command/args/envforstdio). Local (stdio) MCPs pin an exact version (no@latest), listenvnames only, carry thelocal-execrisk tag, and## Surface Routingstops on shell-less surfaces. -
## Surface Routingstates the shell-less / chat-only behavior (fallback or stop). - Heading names are canonical — no synonyms (see Canonical heading names).
-
## Submissionnames a concrete Base MCP tool and shows the mapping into it. - Sections appear in canonical order; no internal
#anchorlinks were broken by a rename. - Diff stays within Contribution Scope: only
plugins/<slug>.mdis added (plus a net-new tag in the vocabulary list if applicable). TheSKILL.mdregistry, the Integration Types Examples column, and the Existing Plugin Conformance table are left for maintainers.
Existing Plugin Conformance
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:
All seven are at
version: 0.2.0 as of the spec-conformance restructure.