MCP Servers: Give Claude Code New Powers
Databases, browsers, design tools: plug external systems straight into your agent.
In Part 4 we made Claude Code obey our rules on every action. Hooks now block edits to .env files and format our code automatically. But our side project still lives in a bubble. Claude Code can read our files and run our scripts, yet it cannot query our database, browse our docs, or check our deployment status. It knows only what is on disk or what we paste into the chat.
MCP servers change that. They are programs that speak a standard protocol, the Model Context Protocol, and each one gives Claude Code a specific new capability. Connect a database server and Claude Code can query your schema. Connect a browser server and it can inspect rendered pages. Connect a GitHub server and it can open pull requests. Under the hood, all of this runs over JSON-RPC 2.0 messages carried either through a local subprocess's pipes or over HTTP.
If MCP itself is new to you, MCP in Plain English: AI's USB-C Moment covers the idea; here we go deeper into how Claude Code uses it. The key point is that the LLM (large language model) never talks to these servers directly. Claude Code sits in the middle as the host. It finds out what each server can do, presents those capabilities to the model as tool descriptions, and routes the model's tool choices back to the right server. The model sees a menu of typed operations. It has no idea whether the database lives on your laptop or in a cloud network. That separation is the entire point.
What exactly is the Model Context Protocol?
The Model Context Protocol is a wire standard for connecting AI applications to external tools and data sources. Think of it as USB-C for agents. Before MCP, every AI tool had to build its own integration for every external system: custom code for each database, different custom code for GitHub, and more for a file system, each with its own authentication, data formats and error handling.
MCP replaces that with one protocol that every server implements and every host understands. Write a server once, and any MCP-compatible host can use it. Claude Code, Claude Desktop, Cursor, and other tools all speak the same language.
The MCP architecture defines three roles. The host is the application the human interacts with; in our case, Claude Code. Inside the host, one client per server manages that connection: each client talks to exactly one server. The server is the program that exposes capabilities: tools for actions, resources for data, and prompts for reusable instruction templates.
When you add an MCP server, Claude Code creates a client for it, connects, finds out what the server offers, and makes those offerings available to the language model. You never write glue code. You configure a server, and Claude Code gains a new power.
How does Claude Code discover what an MCP server can do?
How a connection starts depends on which revision of the protocol the server speaks. Up to the 2025-11-25 revision, every connection began with a handshake: the client sent an initialize request declaring its protocol version and capabilities, and the server answered with its own version and a capabilities object listing what it provides. The current revision, 2026-07-28, made MCP stateless and removed that handshake. Every request now carries the protocol version and the client's capabilities in a _meta field, and a new server/discover request lets a client ask a server, up front, which versions it supports and what it offers, according to the specification's changelog.
Claude Code handles both. Its newer MCP client asks each server whether it supports the 2026-07-28 revision and uses it with those that do, and it connects to every other server with the older handshake, as the Claude Code MCP documentation explains. Either way, the client then asks for specifics. It sends tools/list to get every tool the server exposes, each with a name, a natural language description, and a JSON (JavaScript Object Notation) Schema for its parameters. It sends resources/list for the data the server can provide and prompts/list for its instruction templates.
What happens to those tool definitions next has changed. Claude Code does not paste every definition into the model's context at the start. With MCP tool search, which is on by default, only the tool names and each server's instructions load when the session starts. When Claude needs a tool, it uses its built-in ToolSearch tool to load that tool's full definition on demand. This keeps the context small even with many servers connected; setting ENABLE_TOOL_SEARCH=false turns it off and loads everything up front. Once loaded, the model sees something like: "a tool called run_sql_query that accepts a query string and returns rows." It never sees the server. It sees a tool description, exactly like the tool definitions you would write for any LLM tool-calling API (application programming interface).
This discovery step is what makes MCP plug-and-play. Add a server, start Claude Code, and the model can find the new capabilities. No code changes. No prompt engineering.
What happens on the wire when Claude Code invokes a tool?
Let us trace a call. You ask Claude Code: "Find all tables that reference the users table." The model decides to use a query tool from your database MCP server. We will call it run_sql_query here; real servers name their tools differently (DBHub, used below, calls its version execute_sql). Our side project's development database is SQLite, so the query reads SQLite's own catalog. Claude Code constructs a JSON-RPC request like this:
This is standard JSON-RPC 2.0: a method name, an ID that pairs the request with its response, and a params object. On the current protocol revision the request also carries the version metadata described above.
The client serializes this to UTF-8 and sends it over the transport. If the server runs locally over stdio, the message is written to the server's standard input as one line of JSON, ended by a newline. The server reads it, checks the arguments against the tool's input schema, and runs the query.
The server responds with a JSON-RPC result:
The resultType field is new in the 2026-07-28 revision; servers on earlier revisions leave it out, and clients treat its absence as "complete". Claude Code matches the response to the request by its id and adds the result to the model's context as a tool result. The model then continues reasoning with the new information. It might say: "The orders, profiles, and subscriptions tables reference users, so any change to users will affect these three tables."
The loop looks synchronous from the model's point of view. It picks a tool, waits for the result, and continues. Under the hood, the transport can stream: over Streamable HTTP the server can answer a request with a stream of Server-Sent Events, so a long-running tool can send progress updates before the final result arrives.
How do the two transports differ, and when does each matter?
MCP defines two standard transports: stdio and Streamable HTTP. They carry the same JSON-RPC messages. The difference is where the server lives and how the connection is made.
In stdio mode, Claude Code launches the server as a child process. The server reads JSON-RPC from stdin and writes responses to stdout, and it may write log lines to stderr. This is the simplest model, and it suits local tools like a filesystem server that needs access to a folder on your machine:
The -- separates Claude Code's own options from the command that starts the server, and -y stops npx from waiting on an install prompt that nobody would see. Claude Code spawns the process and runs the protocol over its pipes. No network, and no authentication beyond the operating system's process boundary.
Streamable HTTP is for remote servers. The server runs as an independent process, often on another machine, and exposes an HTTP endpoint. Each JSON-RPC message from the client is a separate HTTP POST to that endpoint, and the server answers with a plain JSON response or, for longer work, a stream of events. An older transport based only on Server-Sent Events is deprecated.
This is how Claude Code connects to hosted services. A GitHub MCP server might run as a cloud service. You configure it with a URL and either an access token in a header or an OAuth sign-in, and the protocol messages inside those HTTP requests are the same as in the stdio case.
For our side project, we will use stdio for a local database server and Streamable HTTP for a hosted browser automation server. The model does not know or care which transport each server uses. That is the beauty of the abstraction.
What primitives can a server expose, and what is each one for?
The MCP server specification defines three primitives, and each has a different owner.
Tools are executable functions, often with side effects. They are model-controlled, meaning the LLM decides when to call them. Each tool has a name, a description, and an inputSchema written in JSON Schema. Tools return text or structured data in a content array. If something goes wrong in the tool itself, the server sets isError: true in the result rather than returning a protocol-level error, so the model can see the error and try to recover.
Resources are read-only data: files, database records, API responses, documentation pages. The specification calls them application-controlled, because the host decides how to use them. In Claude Code, you can pull one into a prompt by typing @ and picking it, much like a file, and Claude can also list and read resources with its built-in resource tools. A server might expose every markdown file in your project docs as a resource.
Prompts are reusable instruction templates. They are user-controlled: a person picks them, rather than the model deciding to use them. A prompt has a name, a description, a list of arguments, and the messages that make up the prompt. In Claude Code, a server's prompts show up as commands, such as /mcp__github__pr_review 456. A design tool's server might offer an "Audit accessibility" prompt that asks the model to check color contrast and ARIA labels.
What about long-running work? Earlier revisions added experimental "tasks" for operations that take minutes or hours. In the 2026-07-28 revision, tasks moved out of the core protocol into an official extension, so treat them as optional and check whether a given server and host support them.
For our side project, we will lean on tools the most. A database server exposes tools for querying the schema. A browser server exposes tools for navigating pages and taking screenshots. Resources can serve our project documentation, and prompts can capture common workflows, like "Generate a new CRUD endpoint."
How does Claude Code handle permissions and safety?
MCP itself does not enforce permissions. The specification lists principles such as user consent before a tool runs, but states that MCP "cannot enforce these security principles at the protocol level"; that is the host's job.
Claude Code applies its own permission system to MCP tools, the same one you met in Part 1. It cannot know whether a given tool changes anything, so it treats the call by your permission mode. In Manual mode, it asks before an MCP tool call unless an allow rule covers it; MCP tools are named mcp__<server>__<tool>, so a rule like mcp__database__run_sql_query pre-approves one tool and mcp__database__* covers a whole server. In auto mode, the classifier reviews the call instead of you. This gate sits between the model's tool choice and the actual JSON-RPC call, and the server never knows it exists.
Project servers get one more check. Servers defined in a project's .mcp.json come from whoever committed that file, so Claude Code asks you to approve them the first time an interactive session meets them; claude mcp reset-project-choices clears those answers. A claude -p run cannot show that prompt and loads them without asking, which matters when you script Claude Code over a repository you did not write.
One common mistake is treating the server's inputSchema as a security boundary. It is not. The schema checks that arguments are well-formed, but it does not enforce authorization. A server that exposes a SQL tool should still apply its own access controls, such as a read-only database connection or a least-privilege user. The schema says "this parameter must be a string." It does not say "this user may only read the public schema."
Why JSON-RPC instead of gRPC or REST?
The specification does not give a rationale, but the trade-offs are easy to see. gRPC offers stronger typing and better performance, and REST fits existing HTTP infrastructure, yet JSON-RPC 2.0 has some likely advantages for this job.
It is simple to implement. A minimal stdio server reads newline-delimited JSON from stdin, dispatches on the method name, and writes JSON back, with no code generator or routing framework, which lowers the barrier for new servers.
It is independent of the transport. JSON-RPC does not assume HTTP, so the same message format works through a subprocess pipe and over the network. gRPC ties you to HTTP/2 and protobuf.
And it matches how tools are already described. LLM tool-calling APIs from Anthropic, OpenAI and others describe tool parameters with JSON Schema, and MCP tools use JSON Schema too, so turning an MCP tool into a model tool definition is nearly one-to-one.
The trade-off is that JSON-RPC lacks built-in streaming, typing and service discovery. MCP adds them on top: event streams for progress over HTTP, notifications such as notifications/tools/list_changed when a server's tools change, the list methods (and now server/discover) for discovery, and JSON Schema for typing.
How do I add an MCP server to my Claude Code project?
The fastest path is the claude mcp add command. For a local stdio server, everything after -- is the command that starts it. Here is a real database server, DBHub, which Claude Code's documentation also uses as its database example, pointed at our SQLite development database (use the full path to your own file):
For a remote server, name the transport and the URL. GitHub's hosted server, for example, takes a personal access token in a header:
Where the server is saved depends on its scope, which --scope sets:
| Scope | Loads in | Stored in |
|---|---|---|
local (the default) | This project, only for you | ~/.claude.json |
project | This project, for everyone who clones it | .mcp.json at the project root |
user | All your projects | ~/.claude.json |
A project-scoped server lives in .mcp.json at the project root, not inside the .claude/ folder. You can write that file by hand and commit it, so the whole team gets the same servers:
The command form uses stdio. The "type": "http" form uses Streamable HTTP; the browser URL here is a placeholder for whichever hosted server you use. Claude Code expands ${VAR} references in .mcp.json from your environment, so secrets stay out of the committed file, and a hard-coded machine path like the one above is better replaced with a variable in a shared file.
Start Claude Code, approve the project servers when asked, and run /mcp to see each server's status and tool count. From the shell, claude mcp list shows the same health check.
For our side project, the database server lets Claude Code inspect the schema and run queries against our development database without us leaving the terminal. Point such a server at a development copy, never at production.
Quick Reference
| Property | Value |
|---|---|
| Protocol | JSON-RPC 2.0 |
| Current protocol revision | 2026-07-28 (stateless; no initialize handshake), as of October 2026 |
| Standard transports | stdio, Streamable HTTP |
| Server primitives | tools (model-controlled), resources (application-controlled), prompts (user-controlled) |
| Discovery | server/discover on the current revision, initialize on older ones; then tools/list, resources/list, prompts/list |
| Tool invocation | tools/call with a name and arguments matching inputSchema |
| Project config file | .mcp.json at the project root |
| Add a server | claude mcp add <name> -- <command> [args...], plus --scope project or --scope user |
| Add a remote server | claude mcp add --transport http <name> <url> |
| Check servers | /mcp in a session, claude mcp list in the shell |
| Tool search | On by default; ENABLE_TOOL_SEARCH=false loads all tools up front |
| Permission model | Host-enforced by Claude Code, not by the protocol |
Frequently Asked Questions
Q: Can one MCP server expose multiple tools, or should I create one server per tool?
A single server can and should expose multiple related tools. A database server might expose separate tools for running a query, listing tables and describing a table. Grouping related capabilities into one server reduces connection overhead and lets the server manage shared resources like connection pools internally. Create separate servers only when the capabilities belong to entirely different domains or need different credentials.
Q: What happens if the MCP server crashes mid-session?
For a remote server, Claude Code tries to reconnect up to five times, waiting longer each time, and then marks the server as failed with a notice pointing at /mcp. A local stdio server is not restarted automatically. In either case you can retry from /mcp without restarting Claude Code, or run /mcp reconnect all.
Q: Can I use MCP servers that require OAuth or API keys?
Yes. For a key, pass a header when you add the server (--header "Authorization: Bearer ...") or use a headers entry with a ${VAR} reference in .mcp.json. For OAuth, the MCP specification defines an optional authorization framework built on OAuth 2.1 for HTTP servers, and Claude Code supports it: run /mcp in a session, pick the server, and sign in through your browser. A claude -p run cannot do that sign-in, so complete it in an interactive session first.
Q: Does the model see raw tool results, or does Claude Code process them first?
Mostly raw. The content array from the server's response becomes part of the conversation, which is why tool descriptions and output shapes matter. Claude Code does limit very large outputs: it warns above 10,000 tokens and, by default, saves a result over 25,000 tokens to a file and gives Claude the path instead (MAX_MCP_OUTPUT_TOKENS changes the limit). A PostToolUse hook can also replace what Claude sees with updatedToolOutput.
Q: Can Claude Code itself act as an MCP server for other tools?
Yes. Running claude mcp serve starts Claude Code as a stdio MCP server. Other MCP-compatible hosts, such as Claude Desktop, can connect to it and use its tools for reading, searching and editing code. That is a way to compose agents: one host might use Claude Code as a coding server alongside a database server and a browser server.
Test yourself
You are building an MCP server that wraps your company's internal issue tracker. The server exposes a tool called create_ticket that accepts a title, description, and priority. A teammate argues that the inputSchema should mark priority as an enum of ["low", "medium", "high", "critical"] with a default of "medium". Another teammate says JSON Schema validation is unnecessary because the LLM will always pass valid values. Who is right, and what else should you consider?
Answer: The first teammate is right. The schema does two jobs. It gives the model a precise contract: with priority declared as an enum, the model sees the allowed values and is far less likely to invent something like "urgent" or "p1". And it protects the server from malformed input whatever the source, whether a buggy host or a hand-written tools/call. The default of "medium" lets the model leave priority out of routine tickets and still make a valid call. Beyond the schema, enforce authorization inside the server: the schema checks shape, not whether this caller may create tickets in that project. On the Claude Code side, remember that the user approves calls to create_ticket unless an allow rule such as mcp__tracker__create_ticket pre-approves it. Never confuse schema validation with access control.
Next: Subagents and Parallel Work. It shows how Claude Code hands focused jobs to helper agents with their own context, and how to run them side by side safely.
If you want this kind of breakdown every week, how real systems actually work under the hood, subscribe to Internals Decoded at internalsdecoded.com.
Sources
- Connect Claude Code to tools via MCP
- MCP specification 2026-07-28: Key changes
- MCP specification 2026-07-28: Overview
- MCP specification 2026-07-28: Architecture
- MCP specification 2026-07-28: Base protocol
- MCP specification 2026-07-28: Discovery
- MCP specification 2026-07-28: Server features
- MCP specification 2026-07-28: Tools
- MCP specification 2026-07-28: Resources
- MCP specification 2026-07-28: Prompts
- MCP specification 2026-07-28: Authorization
- DBHub
- Filesystem MCP server