Claude MCP explained: JSON-RPC tools/list and tools/call, a Streamable HTTP client guide, and authorization and security best practices from the spec.

TL;DR: Model Context Protocol (MCP) is an open standard that provides a universal JSON-RPC interface for connecting AI models to external tools, data sources, and services — replacing fragmented per-tool integrations with a single, composable protocol.
Model Context Protocol (MCP) is an open standard published by Anthropic that defines a universal interface for connecting AI assistants to external data sources, tools, and services. Think of it as USB-C for AI integrations — one protocol that works everywhere, replacing dozens of proprietary connectors.
MCP establishes a structured communication layer between AI applications (called "hosts") and capability providers (called "servers"). The protocol uses JSON-RPC 2.0 as its messaging format, supports capability negotiation at connection time, and provides three distinct primitives for different types of interactions: tools for executing actions, resources for reading data, and prompts for reusable templates.
The specification lives at modelcontextprotocol.io/specification and is maintained as an open standard; the current revision is 2026-07-28. Any AI client can implement the protocol, and any developer can build servers that instantly become available to every MCP-compatible tool in the ecosystem.
What makes MCP particularly significant in 2026 is its adoption velocity. Within 18 months of its initial release, MCP support has been integrated into Claude Code, Cursor, Cline, Windsurf, GitHub Copilot, and dozens of VS Code extensions — making it the de facto standard for AI tool integration.
MCP follows a client-server architecture where the AI application acts as a host managing one or more client connections, and each MCP server exposes a specific set of capabilities.

A single MCP host (Claude Code, Cursor) runs one client per connection. Each client speaks to a dedicated MCP server over a transport — stdio for local servers (filesystem, GitHub), Streamable HTTP for remote ones (a hosted database). The host multiplexes across all of them.
For protocol revisions up to 2025-11-25, which the current spec calls the legacy, initialize-based versions, the connection lifecycle works as follows:
The 2026-07-28 revision removes this handshake and makes MCP stateless: every request carries its protocol version and client capabilities in a _meta field, servers expose server/discover instead of initialize, and state that must span calls travels as explicit handles in tool arguments. The wire-level walkthrough below covers both eras and how a client detects which one a server speaks.
Transports define how messages travel between client and server:
All messages follow JSON-RPC 2.0 format — requests carry a method name and params object, responses carry a result or error object.
Everything in MCP is a JSON-RPC 2.0 message: a request with a string or integer id that must never be null, a response carrying result or error for that id, or a notification with no id and no reply. The transport only decides how those messages are framed and delivered; the method names and payloads are identical over stdio and HTTP. This section follows the three questions implementers ask most: how a client lists and calls tools, how the handshake changed between protocol revisions, and how to implement a Streamable HTTP client.
Tool discovery is one request. The client sends tools/list, optionally with a cursor for pagination, and the server answers with the tools currently available to that caller. In the 2026-07-28 revision every request also carries the protocol version and client capabilities in _meta:
The response lists each tool's name, description and JSON Schema inputSchema, plus optional title, outputSchema, annotations and icons, and a nextCursor when there are more pages. Since 2026-07-28 list results also carry resultType, a ttlMs freshness hint and a cacheScope, and servers should return tools in a deterministic order so clients and LLM prompt caches can reuse the list:
The host turns those definitions into the function-calling tools it presents to the model. When the model picks one, the client sends tools/call with the tool name and arguments; the result is a content array (text, image, audio, resource links or embedded resources), optionally structuredContent that conforms to the tool's outputSchema, and an isError flag:
Two error channels matter. A protocol error such as an unknown tool comes back as a JSON-RPC error (code -32602), which the model is unlikely to fix. A tool execution error, such as invalid input or a failed downstream API call, comes back as a normal result with isError: true so the model can read the message and retry. Servers that declare "tools": { "listChanged": true } send notifications/tools/list_changed when their tool set changes; in 2026-07-28 a client receives it by opening a subscriptions/listen stream with toolsListChanged: true, while 2025-11-25 and earlier delivered it on the standalone GET SSE stream. The spec also tells clients to treat tool annotations as untrusted unless the server is trusted, to show tool inputs to the user before calling the server, and to keep a human able to deny any invocation.
Protocol revisions up to 2025-11-25 open every connection with a three-step handshake. The client sends initialize with its protocolVersion, capabilities and clientInfo; the server replies with the version it will speak, its own capabilities (tools, resources, prompts, logging and so on), serverInfo and optional instructions; and the client confirms with a notifications/initialized notification. Apart from pings and server log messages, nothing else should travel before that completes. Over HTTP the server may also mint an Mcp-Session-Id on the InitializeResult response, which the client must echo on every later request; a 404 means the session is gone and the client must initialize again, and an HTTP DELETE ends it.
The 2026-07-28 revision removes all of that. There is no handshake and no protocol-level session: every request carries io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities in _meta (and should carry clientInfo), and the server answers each request independently. Servers must implement server/discover, which returns supportedVersions, capabilities, serverInfo and instructions in one round trip; clients may call it first or simply send tools/list and handle an UnsupportedProtocolVersionError (code -32022) that lists the versions the server supports. Every result now carries resultType, either "complete" or "input_required"; the latter replaces server-initiated sampling, elicitation and roots requests with an inputRequests payload that the client answers by retrying the original request with inputResponses. A client that must talk to both eras tries a modern request first: on stdio it probes with server/discover and falls back to initialize on any error that is not a recognized modern error; on HTTP it inspects the body of a 400 Bad Request and falls back only when no modern JSON-RPC error is present.
Streamable HTTP, introduced in protocol version 2025-03-26, is the transport to implement for remote servers. The 2026-07-28 binding works like this:
A compliant tools/call POST therefore looks like this:
The official TypeScript SDK and Python SDK implement this binding on both sides, and the MCP Inspector lets you watch the raw exchange while you build.
Authorization is optional in MCP and is defined for HTTP transports only; stdio servers should read credentials from their environment instead. When a server is protected, the authorization specification says:
The security best practices document adds the attack patterns to design against: the confused deputy problem, where an MCP proxy server that uses a static third-party client ID must obtain per-client consent before forwarding to the third-party authorization server; SSRF through attacker-controlled discovery URLs, mitigated by requiring HTTPS and blocking private, loopback and link-local ranges; state handle hijacking (and session hijacking on 2025-11-25 servers), mitigated by never treating a handle or session ID as authentication and binding it server-side to the verified user; local server compromise, where one-click installers must show the exact command before executing it and should sandbox the process; authorization URL validation, allowing only http on loopback and https elsewhere and never opening URLs through a shell; and scope minimization, starting with a small scope set and escalating through challenges. Claude Code follows the client side of this flow: a remote server that answers 401 or 403 is marked as needing authentication, and /mcp runs the browser login.
Before MCP, every AI tool built its own integrations from scratch. Claude had its own tool-use format, GPT had function calling, and every IDE extension reimplemented connections to GitHub, databases, and file systems independently.
This created an N x M integration problem: N AI clients each needing custom adapters for M services. If you had 5 AI tools and 20 services, you needed up to 100 separate integrations — each with its own authentication handling, error formats, and maintenance burden.
MCP collapses this to N + M: each AI client implements one protocol, each service exposes one server, and everything connects.
Beyond the integration math, MCP solves several problems that simple function calling cannot address:
Function calling (as implemented in OpenAI, Anthropic, and other APIs) and MCP serve different layers of the stack. Here is a direct comparison:
In practice, MCP and function calling are complementary. Claude Code uses MCP to connect to external servers, and those servers' tools get presented to the model as function calls in the underlying API. MCP is the connectivity layer; function calling is the invocation mechanism within a single model turn.
The key insight: function calling tells the model what tools exist. MCP tells the client where to find tools and how to execute them.
Building an MCP server is straightforward with the official SDKs. Here is a complete TypeScript server that exposes a single tool for checking website status:
To use this server with Claude Code, add it to your project's .mcp.json:
For Python developers, the FastMCP framework provides an even more concise API:
Both examples produce fully spec-compliant MCP servers that work with any client. The SDKs handle JSON-RPC framing, capability negotiation, and transport management automatically.
As of May 2026, MCP has been adopted across the major AI development tools:
The MCP server ecosystem includes thousands of community-built servers available via npm, PyPI, and the official MCP Server Registry. Popular categories include database connectors (PostgreSQL, MongoDB, Redis), cloud service integrations (AWS, GCP, Cloudflare), developer tools (GitHub, Linear, Jira), and browser automation (Playwright, Puppeteer).
MCP defines three primitives that servers can expose, each serving a distinct purpose:
Tools are executable actions that the AI model can invoke. They represent operations with side effects — querying a database, creating a file, sending a message, or calling an API. Tools are the most commonly used primitive.
Each tool has a name, description, and a JSON Schema defining its input parameters. The server validates inputs and returns structured results. Examples: run_query, create_issue, send_email.
Tools are model-controlled — the AI decides when and how to call them based on the user's intent.
Resources represent read-only data that provides context to the AI. They use URI-based addressing (like file:///path/to/doc or postgres://db/table) and can be static or dynamic. Resources let the AI access information without executing actions.
Examples: a file's contents, a database schema, a configuration object, the current user's profile. Resources support subscriptions — the server can notify the client when resource contents change.
Resources are application-controlled — the host application decides which resources to attach to the conversation context.
Prompts are reusable templates that define structured interactions. They can include placeholders for arguments and can reference tools or resources. Think of them as saved workflows that users can invoke by name.
Examples: a code-review prompt that takes a file path argument, or a summarize-pr prompt that fetches PR data and formats a summary. Prompts are user-controlled — they are explicitly selected by the user from a menu or command palette.
The three-primitive model provides clean separation: tools for doing, resources for knowing, prompts for orchestrating.
Building production-quality MCP servers requires attention to several key areas:
1. Keep tools focused and composable. Each tool should do one thing well. Rather than a monolithic manage_database tool, expose run_query, list_tables, and describe_schema as separate tools. This gives the AI model clearer choices and reduces error rates.
2. Write descriptive tool schemas. The model relies entirely on your tool's name, description, and parameter descriptions to decide when and how to use it. Invest time in clear, specific descriptions. Include examples of valid inputs in parameter descriptions.
3. Handle errors gracefully. Return structured error messages in the tool result rather than throwing exceptions. Include enough context for the model to understand what went wrong and suggest a fix. Never expose internal stack traces.
4. Use stateful connections wisely. MCP's persistent sessions enable connection pooling, caching, and incremental operations. A Git server can maintain a working tree reference across multiple tool calls. A browser server can keep a page open between navigation steps.
5. Implement proper security boundaries. Never accept credentials from the AI model. Store secrets in environment variables or secure vaults. Validate all inputs against your schema before execution. Consider rate limiting for servers exposed over HTTP.
6. Test with multiple clients. An MCP server should work identically across Claude Code, Cursor, and Cline. Test with at least two clients during development to catch assumptions about client behavior. The MCP Inspector (npx @modelcontextprotocol/inspector) provides a standalone test UI.
MCP's trajectory in 2026 points toward becoming infrastructure-grade protocol rather than a developer convenience:
Enterprise adoption is accelerating as organizations recognize that MCP servers provide a governed, auditable interface between AI assistants and internal systems. Companies can expose internal APIs through MCP servers with consistent authentication, logging, and access control — without giving AI models direct access to production systems.
The Streamable HTTP transport (replacing the older SSE-based approach) enables better scalability for remote servers, supporting stateless request-response patterns alongside persistent streaming when needed.
Server registries and marketplaces are emerging, making it possible to discover and install MCP servers as easily as npm packages. The official MCP Server Registry, community collections, and IDE-integrated marketplaces reduce the friction of finding the right server for your use case.
Standardization efforts continue to refine the spec. The protocol's governance model ensures backward compatibility while allowing the addition of new capabilities like the OAuth 2.1-based authorization framework, structured tool output validated by outputSchema, and the tasks extension for long-running operations.
Composable AI architectures increasingly rely on MCP as the glue layer. Multi-agent systems use MCP to give each agent access to different tool sets. Orchestration frameworks use MCP servers as capability modules that can be mixed, matched, and swapped without code changes.
The pattern is clear: just as REST became the universal language for web APIs, MCP is becoming the universal language for AI-tool connectivity. Developers who invest in building MCP servers today are creating infrastructure that will serve the entire AI ecosystem for years to come.
An MCP server is a lightweight program that exposes tools, resources, or prompts over the Model Context Protocol. It receives JSON-RPC requests from AI clients, executes the requested operations (like querying a database or calling an API), and returns structured results. Servers can run as local processes (via stdio) or as remote services (via HTTP). Any developer can build one using the official TypeScript or Python SDKs in under 50 lines of code.
Run claude mcp add for a local stdio server or claude mcp add --transport http for a remote one, choosing --scope local (the default, stored in ~/.claude.json), --scope project (written to .mcp.json in the repository so teammates get it) or --scope user (available in all your projects). A .mcp.json entry looks like {"mcpServers": {"my-server": {"command": "node", "args": ["./server.js"]}}}. Claude Code starts configured servers when a session begins, sends tools/list, prompts/list and resources/list to discover what they offer, and asks you to approve project-scoped servers the first time. A remote server that answers 401 triggers an OAuth login through the /mcp command.
Yes. MCP was created by Anthropic and released as an open specification with no licensing restrictions on implementation. The spec is publicly available at modelcontextprotocol.io/specification, the official SDKs are open source (the project is moving its code from the MIT License to Apache-2.0), and any organization can implement clients or servers without permission or royalty. Multiple competing AI tools (Claude Code, Cursor, Cline, GitHub Copilot) have independently implemented MCP support, confirming its vendor-neutral status.
The official SDKs support TypeScript/JavaScript and Python, which cover the majority of MCP servers in the ecosystem. Community SDKs extend support to Rust, Go, Java, Kotlin, C#, Ruby, and Swift. Since MCP uses JSON-RPC 2.0 over standard transports (stdio or HTTP), you can technically implement a server in any language that can read from stdin, write to stdout, and parse JSON — the protocol itself is language-agnostic.
It usually refers to one of three integration points. Claude Code is an MCP client: claude mcp add registers a server, Claude Code discovers its tools with tools/list, and the tools appear to the model next to the built-in ones, with OAuth handled through /mcp. Claude Desktop connects to local MCP servers and claude.ai to remote servers through custom connectors. For your own application, the Messages API MCP connector takes an mcp_servers array (URL, name and optional authorization_token, with the mcp-client-2025-11-20 beta header); it currently supports tool calls only and needs servers reachable over HTTP, so local stdio servers cannot be attached directly.
The client sends a tools/list request and the server returns a tools array in which each entry has a name, a description and a JSON Schema inputSchema, plus a nextCursor when the list is paginated. The client exposes those tools to the model and, when the model selects one, sends tools/call with the name and arguments; the server replies with a content array and isError, or with a JSON-RPC error for an unknown tool. In the 2026-07-28 revision every request also carries io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities in _meta, and list results include ttlMs and cacheScope so clients can cache them.
POST every JSON-RPC request or notification to the server's single MCP endpoint with Accept: application/json, text/event-stream and the MCP-Protocol-Version, Mcp-Method and Mcp-Name headers mirrored from the body. Handle two response types, a single JSON object or a request-scoped SSE stream that ends with the final response, and treat 202 Accepted as the normal reply to a notification. Open a subscriptions/listen stream if you need list-change notifications, cancel by closing a response stream, re-issue a request whose stream broke, and fall back to the 2025-11-25 initialize flow when a server answers 400 without a recognized modern error body.
Protected servers follow OAuth 2.1: publish Protected Resource Metadata (RFC 9728), answer anonymous requests with 401 and a WWW-Authenticate challenge, require the resource parameter (RFC 8707) and PKCE, and accept tokens only in the Authorization: Bearer header. Servers must validate the token audience and must never pass a client's token through to a downstream API. The security best practices document adds per-client consent for proxy servers (the confused deputy problem), SSRF protections during discovery, secure random handles bound to the verified user, explicit consent before a client executes a local server's command, and least-privilege scopes with step-up challenges.
Only for protocol versions up to 2025-11-25, which the current spec calls the legacy versions and still documents how to interoperate with. Those versions require initialize, the notifications/initialized confirmation and, over HTTP, an optional Mcp-Session-Id header that the server mints and the client echoes. The 2026-07-28 revision removes both: each request carries its version and capabilities in _meta, server/discover replaces the handshake, and a client that must support both eras probes first and falls back to initialize when the server does not answer with a recognized modern error.
Aaron is an engineering leader, software architect, and founder with 18 years building distributed systems and cloud infrastructure. Now focused on LLM-powered platforms, agent orchestration, and production AI. He shares hands-on technical guides and framework comparisons at fp8.co.
Cline MCP internals decoded: JSON-RPC 2.0 messaging, tool discovery, security approvals, and spec compliance — all from real source code.
Agentic AIBuild custom Claude Code Skills with 5 ready-to-use examples. Covers SKILL.md spec, security controls, plugin distribution, and team sharing workflows.
AI Development ToolsExplore how Claude Code, Cursor, Aider, and Cline work under the hood. Agent loops, tool dispatch, and edit strategies explained.
AI Engineering