M3.8
Interop standards: MCP, ACP, and native registries
794 words · about 5 min
Concept
Three questions get confused constantly. How does an agent get more tools? (MCP.) How does an agent talk to an editor? (ACP.) How do agents talk to each other? (A2A.) Memorize the one-liner:
MCP connects an agent to its tools. ACP connects an agent to its editor. A2A connects agents to each other.
Mechanism
MCP (Model Context Protocol) is a JSON-RPC protocol for exposing tools,
resources, and prompts from a server to a model host. A server advertises tools
via tools/list, each with a required inputSchema (JSON Schema draft 2020-12),
and the host invokes tools/call. It was donated to the Agentic AI Foundation on
2025-12-09, so it is now vendor-neutral. Two facts matter operationally.
First, MCP has two distinct error channels, and confusing them is a common
implementation bug. A JSON-RPC protocol error (unknown method, malformed request)
is a transport failure the model never sees. A tool that ran and failed returns a
normal successful JSON-RPC response with isError: true in the result — visible
to the model, so it can retry. Returning a protocol error for a tool failure hides
the failure from the only participant who could correct it. Note the shape
correspondence with ToolExecutionResult: dsh's isError discriminant is the
same idea, and mapping between them is mechanical.
Second, naming is namespaced — a host typically exposes MCP tools as
mcp__<server>__<tool>. That prefix is prefix-token cost and a readability tax on
every request, and it interacts badly with M3.2's naming advice: a namespaced
create_issue is not the short conventional verb a model pattern-matches best.
Be aware the protocol is still moving: a breaking stateless rewrite landed 2026-07-28. Treat MCP as a maturing standard, not a stable one.
On security: "tool poisoning" is not a section of the MCP specification. It is a term coined by Invariant Labs for attacks that hide instructions in tool descriptions, and it appears in the OWASP MCP Top 10 as MCP03:2025. It describes a real risk — a third-party server's tool description is untrusted text that lands in your system prompt — but attributing it to the spec is a factual error.
ACP (Agent Client Protocol) runs in the opposite direction. The agent is a
subprocess of the editor; the editor exposes session/request_permission,
fs/read_text_file, and fs/write_text_file. The agent asks the editor for
permission and for file contents, so the human's approval UI and unsaved buffers
are the editor's business. dsh carries packages/acp/acp plus
packages/subagent/subagent-acp and packages/test-support/acp-snapshot, so ACP
is both an integration surface and a testable one.
Native registries — ctx.tools.register() — are the in-process case. No
serialization, no IPC, full access to host services, exact typed argument
inference, and disposal tied to the plugin fiber.
Worked example — choosing
Worked example| Situation | Choose | Why |
|---|---|---|
A tool needing host services (ctx.fs, ctx.subprocess, session events) | Native | No IPC boundary; typed args; scoped disposal |
| A third-party capability already shipping an MCP server | MCP | Reuse; a stable contract you did not write |
| A tool that must be reusable across several unrelated hosts | MCP | The portability is the point |
| Running inside an editor where a human approves actions | ACP | The editor already owns permission UI and buffers |
| Latency-critical, per-turn, many calls | Native | IPC per call is the dominant cost |
dsh treats MCP as a client concern: packages/mcp/mcp-client, with the
documented integration pattern being one plugin per server, which discovers the
server's tools and calls ctx.tools.register() with the server's schemas. MCP
tools land in the same registry as native ones and get the same pipeline, guards,
permissions, timeouts, and concurrency classification. That is the right
architecture — the interop protocol is an acquisition mechanism, not a parallel
execution path.
Tradeoffs
MCP buys ecosystem reuse and pays in IPC latency, prefix bytes for namespaced names, a moving spec, and a genuine supply-chain surface: every third-party server contributes untrusted text to your prompt and a process to your machine. Native tools are faster and safer and are yours to maintain forever. The honest summary is that the field has not settled — MCP won the "how do I attach tools" question by adoption, ACP is early, and A2A is early enough that its practical value is still unproven.
How others differ
Goose makes every tool an MCP server, accepting uniform IPC cost for uniform
extensibility. Claude Code ships around 45 native built-ins and treats MCP as the
extension mechanism on top. dsh sits with Claude Code: native for the core,
MCP-as-client for extension. All three read readOnlyHint differently, which is a
concrete reminder that MCP standardizes the wire format, not the host's policy
interpretation of it.
Check yourself
2 questions on M3.8