Tool Protocols

Agents are only as useful as the tools they can reach. The ecosystem converged on a layered stack: MCP for tools, LSP for code, A2A for other agents. Three protocols, three reasons. Tools are heterogeneous and remote — stateless wire format. Code is structured and stateful — project-graph-aware server. Other agents are autonomous — discovery and task lifecycle. One protocol cannot do all three; the runtime should speak all three.

MCP Won

Model Context Protocol is the de facto standard for connecting agents to tools.

The Three-Layer Protocol Stack

LayerProtocolScopeStatus
1MCPAgent-to-ToolStable. Spec rev 2025-11-25. AAIF / Linux Foundation stewardship (Dec 2025).
2A2AAgent-to-Agentv1.2. Linux Foundation stewardship since June 2025. 150+ orgs.
3WebMCPAgent-to-WebW3C Community Group Draft Report — not a W3C Standard. Microsoft + Google editors.

MCP Architecture

MCP uses a client–server architecture with a JSON-RPC 2.0 wire format. Clients live in the agent runtime; servers are standalone processes (stdio) or remote services (Streamable HTTP).

sequenceDiagram
    participant A as Agent (MCP Client)
    participant S as MCP Server (e.g. GitHub)

    A->>S: initialize (capabilities negotiation)
    S-->>A: serverInfo, capabilities

    A->>S: tools/list
    S-->>A: [{name: "create_issue", inputSchema: {...}}, ...]

    A->>S: tools/call {name: "create_issue", arguments: {repo: "org/app", title: "Fix login bug"}}
    S-->>A: {content: [{type: "text", text: "Created issue #42"}]}

Transports: the current spec defines stdio (local processes) and Streamable HTTP (remote servers). The older HTTP+SSE transport is deprecated as of spec revision 2025-03-26; major vendors have SSE sunset deadlines arriving mid-2026. New remote servers should target Streamable HTTP.

Configuration: .mcp.json

Projects declare MCP server dependencies in .mcp.json at the repository root, checked into version control so every developer and agent gets the same tool configuration.

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "POSTGRES_CONNECTION_STRING": "${DATABASE_URL}"
      }
    }
  }
}

Credentials MUST use ${ENV_VAR} references — never inline secrets. This file is committed to git; inline secrets end up in git history.

Tool Discovery

When a client connects, it calls tools/list. The server responds with a JSON Schema per tool — the protocol is self-describing, so no hardcoded tool knowledge is required.

{
  "tools": [{
    "name": "create_issue",
    "description": "Create a new GitHub issue in a repository.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "repo": { "type": "string", "description": "owner/repo" },
        "title": { "type": "string" },
        "body": { "type": "string" }
      },
      "required": ["repo", "title"]
    }
  }]
}

Current MCP Server Ecosystem

The reference server set was trimmed in May 2025. Active reference servers in modelcontextprotocol/servers are now: Filesystem, Git, Fetch, Memory, Sequential Thinking, Time, and Everything. The earlier reference implementations for GitHub, Slack, Postgres, Puppeteer, Redis, Sentry, and SQLite were moved to modelcontextprotocol/servers-archived and are no longer canonical. For production, use vendor-official servers: GitHub’s own MCP server, Microsoft’s playwright-mcp, Sentry’s own server, and so on.

Streaming as a Structural Constraint

Streaming is not a UX feature layered on top of MCP — it is a structural constraint that changes error handling, cancellation, tool execution, rendering, and backpressure throughout the stack. Streamable HTTP supports multi-message streaming within a single response; the server can push notifications and requests back to the client mid-call. Every layer from API client to terminal renderer must handle partial, in-flight state.

ProblemNon-streamingStreaming
Error handlingCheck response statusStream can break mid-token, mid-tool-call, or mid-markdown block
CancellationDon’t send the requestUser hits Ctrl+C during a file write — abort cleanly, discard partial output, leave the conversation in a valid state
Tool callsParse complete JSONTool call JSON arrives incrementally — buffer, detect boundaries, dispatch
RenderingRender complete markdownRender partial markdown that may have unclosed blocks, incomplete tables, or half-written code fences
BackpressureN/AModel produces tokens faster than the terminal renders — buffer without unbounded memory growth

Two design notes carry the weight. Define a StreamEvent enum every layer speaks: TokenDelta, ToolCallStart, ToolCallDelta, ToolCallEnd, Error, Done. Make cancellation a first-class event — every async operation must respond to a cancellation signal within one tick.

Tool Failure

Tools fail. bash returns exit code 1. File writes fail because the path doesn’t exist. MCP servers crash mid-call. Treat failures as structured data the model reasons about, not exceptions that crash the loop.

Failure typeExampleCorrect handling
Expected errorbash exits with code 1Return stderr as tool result. Model adapts.
Transient failureNetwork timeout on web_fetchRetry with backoff. Include attempt count in result.
Permanent failureMCP server process diedMark server unavailable. Remove its tools from the active registry. Inform the model.
Partial resultStreaming tool output cut shortReturn what was received with a truncation marker.
Permission denialUser rejected the tool callReturn denial as a structured result. Model must not retry the same call.

Every tool result is an envelope: { status, content, metadata } where status is ok | error | denied | timeout and metadata carries duration_ms, exit_code, and similar. Track consecutive failures per tool — three in a row means the model is stuck in a retry loop, and the harness should surface this to the user rather than burn tokens.

LSP — The Missing Protocol Layer

Agents that work with code lean overwhelmingly on text tools: grep, glob, regex. These treat code as strings. Code is not strings — it is a structured artifact with types, references, scopes, and semantic relationships text search cannot reliably capture. The result is false positives in reference searches, missing type information on hover, and renames that quietly break string literals. LSP is the mature, decade-old protocol that fixes this — every major language has one, every major IDE uses one, and the MCP spec itself credits LSP as design inspiration.

Text Tools vs. LSP

The difference is not incremental — it is categorical.

TaskText tools (grep, glob)LSP
Find all callers of processPayment()grep -r "processPayment" — finds comments, strings, imports, dead code, and the definition itself. Manual filtering required.textDocument/references — returns only call sites. No false positives.
Check if a refactor broke the typesRun the full compiler. Slow, noisy, reports errors in unrelated files.textDocument/diagnostic — incremental. Reports only new errors in changed files.
Rename userId to accountId everywhereFind-and-replace. Breaks userIdValidator, getUserId(), string literals, and comments.textDocument/rename — renames only the semantic symbol. Strings, comments, substrings untouched.
Understand an unfamiliar functionRead the file. Hope the docstring is accurate.textDocument/hover — returns type signature, documentation, and source location. Always current.

What LSP Provides

LSP MethodWhat It ReturnsAgent Use Case
textDocument/referencesAll locations referencing a symbol”Find every caller” — zero false positives
textDocument/definitionThe definition site of a symbol”Where is this defined?” — instant
textDocument/hoverType information and documentation”What does this return?” — compiler-accurate
textDocument/diagnosticErrors, warnings, hints”Is this code valid?” — incremental, language-aware
textDocument/renameAll locations for a safe rename”Rename everywhere” — semantic, won’t break strings
textDocument/completionValid completions at a cursor”What methods exist here?” — type-aware
textDocument/signatureHelpParameter names and types”What arguments does this take?”
workspace/symbolAll matching symbols across workspace”Find all *Controller” — structured, not regex

Lazy Lifecycle

LSP servers are stateful, memory-intensive processes that hold a project graph and consume hundreds of megabytes on large codebases. Do not start them at session start — most conversations never need semantic code intelligence. Spawn on the first LSP tool call, accept the 2–10 second startup penalty, and serve subsequent calls fast. Kill servers when the session ends, after a configurable idle timeout (typically 5 minutes), or on crash (do not auto-restart — inform the agent that LSP is unavailable).

Server Selection

File ExtensionLanguage Server
.ts, .tsx, .js, .jsxtypescript-language-server
.rsrust-analyzer
.pypyright or python-lsp-server
.gogopls
.javajdtls (Eclipse)
.c, .cpp, .hclangd

When a file type has no configured server, LSP tools should fail gracefully and the agent falls back to text-based tools without error.

MCP and LSP Are Complementary, Not Competing

MCP is stateless tool calls to any service. LSP is stateful, code-specific, project-graph-aware intelligence. Both belong in the runtime: MCP for breadth, LSP for depth on code. Wrapping LSP behind an MCP server is technically possible but architecturally awkward — LSP’s open-document model maps poorly to MCP’s stateless call shape. Expose LSP capabilities directly as tools, managed by a dedicated LSP client in the harness. Claude Code shipped native LSP in v2.0.74 (December 2025) with plugins covering 20+ languages; it remains the only CLI agent that ships LSP as a first-party tool.

A2A

Agent-to-Agent addresses multi-agent coordination: discovery, authentication, and delegation between agents. Each A2A agent publishes an Agent Card at /.well-known/agent-card.json (per RFC 8615) advertising name, version, service endpoint, supported modalities, auth schemes (Basic, Bearer, OAuth 2.0 / OIDC), and skills. Originally Google-led, contributed to the Linux Foundation in June 2025; now under the AAIF umbrella alongside MCP, currently at v1.2 with signed Agent Cards and adoption across 150+ organizations (Microsoft, AWS, Salesforce, SAP, ServiceNow).

A2A defines a stateful task lifecycle: submitted, working, input-required, auth-required, completed, failed, canceled, rejected. MCP and A2A are complementary — MCP connects agents to tools, A2A connects agents to agents.

Security

See Credentials for the full credential lifecycle — OAuth with PKCE, token storage, proactive refresh before 80% of TTL, and revocation.