protocol agentproto.shcli cli.agentproto.shpanel /panel
agentproto

AIP-32: MCP.md — agentmcp/v1 (MCP driver specialisation)

A markdown + frontmatter format for declaring a Model Context Protocol driver — its server reference, transport (stdio / SSE / HTTP), per-tool MCP tool-name binding, prompts/resources composition, and lifecycle. Specialises AIP-30 DRIVER for the `kind: mcp` case. Wraps Anthropic-spec MCP servers (filesystem, github, postgres, …) as agent tools.

FieldValue
AIP32
TitleMCP.md — agentmcp/v1 (MCP driver specialisation)
StatusDraft
TypeSchema
FamilyDriver
Driver kindmcp
SpecialisesAIP-30 DRIVER.md
RequiresAIP-14 (TOOL), AIP-16 (IO), AIP-19 (SECRETS), AIP-30 (DRIVER)
Resources./resources/aip-32 — MCP.schema.json, ADAPTER.md, EXAMPLES.md, SKILL.md

Abstract

MCP.md is the kind: mcp specialisation of AIP-30 DRIVER. It packages a Model Context Protocol server integration — its server reference (binary, container, or remote URL), transport (stdio / SSE / HTTP), and per-tool mapping from abstract TOOL contracts to MCP tool names.

MCP is Anthropic's open protocol for connecting LLMs to data sources, tools, and prompts via a JSON-RPC-like interface. Reference servers exist for filesystems, git, GitHub, postgres, sqlite, Brave search, etc. This AIP wraps those servers as conformant drivers in the AIP-30 supertype.

The frontmatter inherits everything from DRIVER and adds MCP- specific fields: server, transport, mcp_tools mapping, plus optional prompts and resources composition. The format pairs with defineDriver({ kind: "mcp", ... }).

Motivation

MCP is rapidly becoming the de-facto protocol for "give an LLM access to a tool". Anthropic ships ~15 reference servers; community servers proliferate. Without a shared MCP-driver format:

  1. Each MCP server has its own setup story. "Run via npx", "build from source", "docker pull then expose stdio" — five flavours of install, no shared declaration.

  2. MCP tools don't carry contracts. An MCP server exposes tools/list returning JSON-Schema-validated tool definitions, but those don't naturally map to AIP-14 abstract contracts. Two different MCP servers may both expose read_file with subtly different schemas; a contract-bound layer normalises.

  3. Auth state is per-server. GitHub MCP server needs OAuth; filesystem MCP server needs no auth; postgres MCP server needs a connection string. The DRIVER auth shape covers all three; without it, callers handle each separately.

  4. Routing across same-purpose servers. Multiple MCP servers may serve fs.read (Anthropic's reference filesystem-mcp, alternative implementations, custom server). The resolver picks; without the AIP-30 layering, "MCP" is a bypass that hides alternatives.

  5. Lifecycle. MCP servers spawn, connect, list tools, get torn down. Without a driver format, every host re-implements the lifecycle.

Design principles

  1. Inherit, don't repeat. Identity, auth, install, sandbox, policy_tags, region all come from DRIVER. MCP only adds what's MCP-specific: server location, transport, tool-name mapping.

  2. Server is data, not code. server declares "where the MCP server lives" (binary path, npm package + args, docker image, or remote URL). The host's MCP runtime spawns / connects per the declaration; driver authors write zero connection code in the common case.

  3. mcp_tools is the binding. Each entry in implements[] includes metadata.mcp.tool_name declaring which MCP tool name serves the contract. The host calls mcp.tools/call({ name, arguments }) with the validated input.

  4. Transport-agnostic at the contract layer. stdio, SSE, and HTTP transports all conform to the same MCP semantics; driver declares transport, host's MCP client handles dispatch.

  5. Prompts and resources are first-class. MCP exposes prompts and resources alongside tools. v1 references them via optional prompts / resources blocks; full integration with AIP-3 SKILL and AIP-54 REF is a follow-up.

  6. Local stdio for self-hosted, HTTP for remote. Default-recommend stdio when the MCP server runs locally (filesystem, sqlite, localhost postgres). Use HTTP/SSE for remote managed services or shared-network deployments.

Specification

File location

MCP drivers live in a single folder:

.drivers/
  filesystem-mcp/
    DRIVER.md            ← this AIP, kind: mcp
    driver.ts            ← optional entry (custom resource handler)
    server.json            ← MCP server config (sometimes inlined into DRIVER.md)
    SECRETS.md             ← AIP-19 inventory (when auth is needed)

Convention: id ends with -mcp. The folder name SHOULD match id.

Frontmatter

Inherits all fields from AIP-30 DRIVER.md plus the MCP-specific fields below.

Required MCP-specific fields

FieldTypeDescription
kindconst "mcp"Per AIP-30.
serverobjectWhere the MCP server lives. See The server block.
transportenum"stdio" | "sse" | "http". How the host connects to the server.

Optional MCP-specific fields

FieldTypeDefaultDescription
protocol_versionstringlatest knownMCP protocol version this driver targets. v1 default tracks the latest stable MCP release.
promptsobject[][]MCP prompts the server exposes. Each { name, description, integrate_as }. v1 supports integrate_as: "skill_block" (composes into AIP-3 SKILL.md) or "raw" (exposed verbatim).
resourcesobject[][]MCP resources the server exposes. Each { uri_template, description, integrate_as }. v1 integrate_as: "ref_kind" registers a new AIP-54 REF kind.
connection_optionsobject{}Transport-specific connection params: timeout_ms, max_retries, keepalive.

Per-tool metadata.mcp (in implements[N].metadata.mcp)

Every entry in implements[] adds:

FieldTypeDescription
tool_namestringThe MCP tool name to call (tools/call({ name: <here> })). MUST be a tool exposed by the server's tools/list.
argument_mappingobjectOptional rename: contract-input key → MCP-tool argument name. Default: identity (contract input keys passed verbatim).
result_extractstringJSONPath-lite to extract the contract's outputs shape from the MCP tools/call response. Default: identity ($).

The server block

Declares where the MCP server lives. Four shapes:

1. Local binary (stdio)

server:
  kind: binary
  path: /usr/local/bin/mcp-filesystem
  args: ["/workspace"]
transport: stdio

The host spawns the binary as a subprocess with stdio piped.

2. npm package (stdio, most common pattern)

server:
  kind: npm
  package: "@modelcontextprotocol/server-filesystem"
  args: ["/workspace"]
transport: stdio
install:
  - { method: npm, package: "@modelcontextprotocol/server-filesystem", global: false }

Host runs npx <package> <args> (or installs and runs).

3. Docker image (stdio or http)

server:
  kind: docker
  image: "modelcontextprotocol/server-postgres:latest"
  env:
    DATABASE_URL: "${secrets.POSTGRES_URL}"
transport: stdio

Host pulls + runs the image, connecting via stdio (with -i flag) or exposed port.

4. Remote HTTP / SSE endpoint

server:
  kind: remote
  url: "https://mcp.example.com/v1"
transport: sse                # or "http"

Host connects directly; no spawn, no install.

Transport semantics

TransportUse whenLifecycle
stdioMCP server is a local subprocess (binary, npm, docker -i).Spawn-on-first-use, keep alive across calls, terminate on host shutdown or idle timeout.
sseMCP server is remote, supports server-sent-events for bidirectional streaming.HTTP connection upgrade; one connection per driver, multiplexed across calls.
httpMCP server is remote, request-response per call.One HTTP request per tools/call.

Drivers SHOULD prefer stdio for local servers and sse for remote managed services with bidirectional needs.

MCP tool mapping

Each implements[] entry binds an abstract TOOL.md to an MCP tool exposed by the server:

implements:
  - tool: ./tools/fs-read/TOOL.md
    version: "^1.0.0"
    metadata:
      mcp:
        tool_name: read_file              # the MCP tool's name from tools/list
        argument_mapping:
          path: path                       # contract input → MCP arg
        result_extract: "$.contents"       # MCP returns { contents, mime_type }
  - tool: ./tools/fs-write/TOOL.md
    version: "^1.0.0"
    metadata:
      mcp:
        tool_name: write_file
        argument_mapping:
          path: path
          content: content

The host's MCP runtime:

  1. Spawns/connects the server (per server and transport).
  2. Calls tools/list to validate the declared tool_names exist.
  3. Per call: applies argument_mapping to the validated contract input, calls tools/call, applies result_extract to the response.

Prompts integration (preview)

When the MCP server exposes prompts (via prompts/list), the DRIVER MAY declare integration:

prompts:
  - name: summarize-document
    description: "Generate a structured summary of a document."
    integrate_as: skill_block
    skill_ref: ./skills/summarize/SKILL.md

integrate_as: "skill_block" registers the prompt as a callable sub-skill within the named SKILL.md. v1 implementation is preview quality; full integration with AIP-3 is on the roadmap.

Resources integration (preview)

When the MCP server exposes resources (via resources/list), the DRIVER MAY declare integration:

resources:
  - uri_template: "file:///{path}"
    description: "Filesystem resource."
    integrate_as: ref_kind
    ref_kind_id: mcp_filesystem

integrate_as: "ref_kind" registers a new AIP-54 REF kind (mcp_filesystem:file:///path/to/file) so other AIPs can reference MCP-server-backed resources via the unified REF format. v1 preview.

Stable identity

Per AIP-30. Bumping the MCP protocol version, the server source (npm package version major), or the transport is a major change.

The defineDriver({ kind: "mcp", ... }) signature

interface DriverDefinition<"mcp"> extends DriverDefinition {
  kind: "mcp"
  server: McpServer
  transport: "stdio" | "sse" | "http"
  protocolVersion?: string
  prompts?: McpPrompt[]
  resources?: McpResource[]
  connectionOptions?: { timeout_ms?: number; max_retries?: number; keepalive?: boolean }

  // Optional behavioural adapters.
  buildArguments?: (args: BuildArgumentsArgs) => Record<string, unknown>
  parseToolResult?: (args: ParseToolResultArgs) => DriverResult<unknown>
}

type McpServer =
  | { kind: "binary"; path: string; args?: string[]; env?: Record<string, string> }
  | { kind: "npm";    package: string; args?: string[]; env?: Record<string, string> }
  | { kind: "docker"; image: string; args?: string[]; env?: Record<string, string> }
  | { kind: "remote"; url: string; auth?: McpRemoteAuth }

A frontmatter-only MCP driver is valid when argument_mapping + result_extract cover the dispatch shape — most reference MCP servers fit this pattern.

Authoring with SKILL.md

The canonical way to generate an MCP DRIVER.md is via the paired author-mcp skill. The skill walks the agent through:

  1. Identify the MCP server (npm package / docker image / remote URL).
  2. Identify the transport (stdio for local, sse for remote).
  3. Connect once and list MCP tools (tools/list).
  4. Map each MCP tool to a TOOL.md contract (existing or new).
  5. Author argument_mapping and result_extract per tool.
  6. Validate against ./resources/aip-32/draft/MCP.schema.json.

Example

---
name: Filesystem MCP
id: filesystem-mcp
description:
  Anthropic's reference filesystem MCP server. Stdio transport. Exposes
  read_file, write_file, list_directory as the contracts fs-read,
  fs-write, fs-list.
version: 1.0.0
kind: mcp

server:
  kind: npm
  package: "@modelcontextprotocol/server-filesystem"
  args: ["/workspace"]
transport: stdio
protocol_version: "2025-03"

install:
  - { method: npm, package: "@modelcontextprotocol/server-filesystem", global: false }
version_check:
  cmd: "npx @modelcontextprotocol/server-filesystem --version"
  parse: 'v(\S+)'
  range: ">=0.5"

network:
  egress: []                              # local stdio, no network
region: ["self-hosted"]
policy_tags: ["self-hosted", "pii-safe"]

implements:
  - tool: ./tools/fs-read/TOOL.md
    version: "^1.0.0"
    metadata:
      mcp:
        tool_name: read_file
        argument_mapping: { path: path }
        result_extract: "$.contents"
  - tool: ./tools/fs-write/TOOL.md
    version: "^1.0.0"
    metadata:
      mcp:
        tool_name: write_file
        argument_mapping: { path: path, content: content }
        result_extract: "$"
  - tool: ./tools/fs-list/TOOL.md
    version: "^1.0.0"
    metadata:
      mcp:
        tool_name: list_directory
        argument_mapping: { path: path }
        result_extract: "$.entries"

resources:
  - uri_template: "file:///{path}"
    description: "Filesystem resources accessible via this MCP server."
    integrate_as: ref_kind
    ref_kind_id: mcp_filesystem

tags: [mcp, filesystem, anthropic-reference, pii-safe]
---

See ./resources/aip-32/draft/EXAMPLES.md for additional patterns: postgres MCP, GitHub MCP with OAuth, custom remote MCP server.

Compatibility

With AIP-30 DRIVER.md

Strict specialisation. Universal fields validate against AIP-30's schema; MCP-specific fields validate against AIP-32's. Hosts run both in sequence.

With AIP-14 TOOL.md

MCP drivers reference TOOL.md contracts. The contract's inputSchema is what the runtime validates; argument_mapping renames keys for the MCP server. The contract's outputSchema is what result_extract produces.

With AIP-3 SKILL.md (preview)

prompts[].integrate_as: "skill_block" lets MCP-server prompts plug into named skills. Full integration is on the roadmap; v1 wires the prompt as a callable block within the named skill.

With AIP-54 REF (preview)

resources[].integrate_as: "ref_kind" registers MCP-resource URIs as new REF kinds. Other AIPs can then reference MCP resources via mcp_<kind_id>:<uri> strings.

With Anthropic MCP reference servers

The reference-server registry at github.com/modelcontextprotocol/servers provides ~15 servers. Each can be wrapped as a kind: mcp driver following the patterns in EXAMPLES.md. The author-mcp skill templates the wrapping.

Security considerations

  • Spawned subprocess sandbox. Stdio MCP servers spawn a subprocess per driver. The DRIVER's network.egress and (for CLI-style) sandbox apply to the subprocess. Hosts MUST NOT allow the MCP subprocess to escape the declared sandbox.
  • MCP tool definitions are server-supplied. A malicious MCP server could expose a tool whose schema doesn't match the contract. Hosts MUST validate tools/list responses against the registered contract; mismatches fail at registration, not at first call.
  • Resource URIs may be user-controlled. When a tool input is a resource URI (file:///user/path), hosts MUST sanitise against the MCP server's declared sandbox before forwarding.
  • Connection state across users. A stdio MCP server is a long-lived subprocess; per-call user context (auth, tenant) MUST be passed in tools/call.arguments, never bound at spawn time. Otherwise users share state.
  • Remote MCP servers expose the same risks as third-party HTTP APIs (per AIP-31's security considerations). TLS verification, policy_tags, region — all apply.

Open questions

  1. Prompts and resources integration depth. v1 marks both as preview. Full integration with AIP-3 SKILL.md and AIP-54 REF needs more concrete server examples to settle.
  2. MCP protocol versioning. Anthropic ships breaking changes via protocol version. v1 doesn't yet codify how drivers express "supports both v1 and v2"; defer to v2 with field experience.
  3. Multi-tenant stdio servers. Stdio servers are subprocess- per-driver; multi-tenant routing today means subprocess-per- tenant. Scaling needs a connection-pool model — v2.
  4. Streaming on stdio. MCP supports streaming via newline- delimited JSON-RPC notifications; the AIP-31 streaming pattern doesn't quite fit. Defer to a v2 streaming integration.

See also

Resources

Supporting artifacts for AIP-32. Links open the file on GitHub — markdown and JSON render natively in GitHub's viewer. Browse the full resource tree →