Connect Claude Code to other tools with MCP
An MCP server gives Claude Code new tools, from a docs search to your own scripts. Add one in a single command, write a small one yourself, and know what a repository's .mcp.json can start on your machine.
Checked against the official documentation on
On this page9 sections
Claude Code comes with tools for files, the shell and the web. MCP (the Model Context Protocol) is how you give it more: a search over some documentation, access to an issue tracker, a database, a browser, or a script of your own. Each of these comes from an MCP server, and once a server is connected, Claude can call its tools the same way it calls the built-in ones.
This guide connects one hosted server, then builds a small server of your own in Python, and ends with the part that matters most on a machine you care about: what a repository's MCP configuration can run on your machine, and when Claude Code asks you first.
Two kinds of server
- Remote servers run somewhere else and Claude Code reaches them over HTTP. Many services offer one, often with a browser sign-in.
- Local servers are programs Claude Code starts on your machine and talks to through standard input and output (stdio). Each one can reach anything your user account can reach: your files, your databases, your commands.
You add both with claude mcp add, from your terminal, not from inside a session.
Your first server
The Claude Code documentation has its own MCP server, with a search over the docs. It needs no account, so it is a good first test.
Add it
Shell · your userclaude mcp add --transport http claude-code-docs https://code.claude.com/docs/mcpCheck that it connects
Shell · your userclaude mcp get claude-code-docsOutput
claude-code-docs: Scope: Local config (private to you in this project) Status: ✔ Connected Type: httpUse it
Claude CodeUse the claude-code-docs server to look up what MCP_TIMEOUT does
claude-code-docs is a name you choose. It labels the server's tools and is how you refer to the server in other commands. You don't usually need to name the server in your request, because Claude picks the right tool on its own. Naming it here makes sure the answer comes from the server and not from a web search.
In our test, Claude called the server's search_claude_code_docs tool and answered from the page it found, with a link to it.
claude mcp list shows every server with its status. To remove one, run claude mcp remove claude-code-docs. Inside a session, /mcp shows the same list and lets you reconnect, sign in or turn a server off.
Where the server is saved
claude mcp add saves the server in one of three scopes:
- Local (the default): only you, only in this project. Saved in
~/.claude.json, under the project's folder. - Project (
--scope project): everyone who works on the project. Saved in a.mcp.jsonfile at the project root, which you commit. - User (
--scope user): only you, in all your projects. Also saved in~/.claude.json.
A server that needs a password or a token belongs in local or user scope, so the secret never ends up in the repository. If a project server needs one, .mcp.json can read it from an environment variable, for example "Authorization": "Bearer ${API_KEY}".
Write a small server of your own
A local server is just a program that reads requests on its standard input and writes answers on its standard output. This one has a single tool that reports disk and memory use. It uses only Python's standard library, so there is nothing to install.
#!/usr/bin/env python3
"""A minimal MCP server over stdio: one tool that reports disk and memory use."""
import json, subprocess, sys
def reply(id, result):
print(json.dumps({"jsonrpc": "2.0", "id": id, "result": result}), flush=True)
TOOLS = [{
"name": "disk_and_memory",
"description": "Disk use of / and memory use of this server",
"inputSchema": {"type": "object", "properties": {}},
}]
for line in sys.stdin:
msg = json.loads(line)
method, id = msg.get("method"), msg.get("id")
if method == "initialize":
reply(id, {
"protocolVersion": "2025-06-18",
"capabilities": {"tools": {}},
"serverInfo": {"name": "server-info", "version": "1.0.0"},
})
elif method == "tools/list":
reply(id, {"tools": TOOLS})
elif method == "tools/call":
out = subprocess.run("df -h /; echo; free -h", shell=True,
capture_output=True, text=True).stdout
reply(id, {"content": [{"type": "text", "text": out}]})
elif id is not None:
err = {"code": -32601, "message": "Method not found"}
print(json.dumps({"jsonrpc": "2.0", "id": id, "error": err}), flush=True)Claude Code sends three kinds of request here: initialize when it connects, tools/list to learn which tools exist, and tools/call when Claude uses one. The tool's description is what Claude reads to decide when the tool fits a request, so write it plainly.
This is enough to learn how MCP works and to wrap a script or two. For anything larger, use one of the official SDKs linked at the end, which handle the parts of the protocol this file skips.
Add it with the full path to the file:
claude mcp add server-info -- python3 /home/you/server-info.pyEverything after -- is the command that starts the server. Without the --, Claude Code reads any flags meant for your program as its own options and stops. In our test, a flag that belonged to the program was rejected with:
Output
Error: unknown option '--verbose'Then ask for it in a session:
Use the server-info server to tell me the disk and memory use of this machinePermissions for MCP tools
Each MCP tool has a name made of the server's name and the tool's name: mcp__server-info__disk_and_memory. In Manual mode (default), Claude Code asks before it runs an MCP tool, the same as for a shell command, unless a rule allows it.
We ran the request above with claude -p in Manual mode, with no rule. The tool was blocked, and the run reported:
Output
Claude requested permissions to use mcp__server-info__disk_and_memory, but you haven't granted it yet.With an allow rule, the same run went through. Put the rule in .claude/settings.json or ~/.claude/settings.json:
{
"permissions": {
"allow": ["mcp__server-info"]
}
}mcp__server-infoallows every tool from that server.mcp__server-info__disk_and_memoryallows just that one tool.mcp__server-info__get_*allows the tools whose names start withget_.
Deny and ask rules work the same way. A deny rule for mcp__* turns off every MCP tool from every server.
Servers don't fill the context
You might expect every connected server to take up space in each conversation. By default, Claude Code loads only the tool names at the start, and Claude looks up a tool's full description when it needs it. In our test runs, Claude's first step was always a ToolSearch call for the MCP tool, and only then the tool itself. That makes adding servers cheap, although removing the ones you no longer use is still good housekeeping.
What can fill the context is a tool's answer. Claude Code warns when one MCP result goes over 10,000 tokens. Above 25,000 tokens, it saves the result to a file and gives Claude the path instead. You can raise the 25,000 limit with the MAX_MCP_OUTPUT_TOKENS environment variable.
MCP servers from someone else's repository
A local server is a program, and a project's .mcp.json names the program to run. If you clone a repository and open Claude Code in it, its .mcp.json can start a program on your machine. That is why Claude Code asks first, in an interactive session.
We tested this with a folder that had never been trusted, holding a .mcp.json whose server writes a small file the moment it starts, so we could see whether it ran.
In an interactive session, Claude Code first shows the usual workspace trust dialog. That dialog says nothing about MCP servers. After you accept it, a second screen names the server:
Output
New MCP server found in this project: repo-info
MCP servers may execute code or access system resources. All tool calls
require approval. Learn more in the MCP documentation.
Use this MCP server
Use this and all future MCP servers in this project
❯ Continue without using this MCP serverThe safe choice is already selected. Pressing Esc also counts as a no, and Claude Code remembers it: in our test it wrote the server into .claude/settings.local.json under disabledMcpjsonServers. To be asked again, run claude mcp reset-project-choices. Until you choose, claude mcp list shows the server as ⏸ Pending approval.
In a claude -p run, there is no one to ask, so Claude Code does not ask. In our test, a plain claude -p in that never-trusted folder started the repository's server at once, and its file appeared. The documentation says the same: in -p runs and Agent SDK sessions, project servers load without a prompt.
Warning
Don't run claude -p in a repository you have not read, because it starts the programs named in its .mcp.json. If you need to, add --setting-sources user, which makes Claude Code ignore the project's settings and its .mcp.json. In our test, the server did not start with that flag. --strict-mcp-config also kept it out, but it drops your own servers too, unless you pass them with --mcp-config.
A server you have already turned down stays off in every mode. In our test, after the rejection above, claude -p in the same folder did not start it.
Two more points from the documentation:
- A repository cannot approve its own servers before you trust the folder. Approval settings committed in the project's
.claude/settings.jsonare ignored until you trust the folder. - When a
.mcp.jsonuses environment variables in a remote server's address or headers, your Claude and cloud credentials, such asANTHROPIC_API_KEY, come through empty. A.mcp.jsoncannot send them to a server it names.
One more risk, for remote and local servers alike: a server that fetches web pages, emails or tickets can hand Claude text written by a stranger, and that text can contain instructions. Connect servers you trust, and keep the ones that read outside content away from sessions that can change things you care about.
When a server does not connect
✘ Failed to connectfor a local server: run its command yourself in the terminal. If it starts and waits for input, the program works, so compare the command withclaude mcp get <name>. A missing--is the usual cause.! Needs authentication: the server wants a sign-in. Open/mcpin a session, pick the server and choose Authenticate, or runclaude mcp login <name>. On a machine with no browser, that command prints a link to open on your own computer.- The server does not appear at all: a local-scope server belongs to the folder where you added it. Run
claude mcp addagain from the right project, or add it with--scope user. - A slow first start: servers launched with
npxdownload a package the first time. Claude Code waits 30 seconds by default.MCP_TIMEOUT=60000 claudegives them a minute. - Changes to
.mcp.jsondo nothing: Claude Code reads the file when a session starts. Exit and start again.
Sources
- Connect Claude Code to tools via MCP, Claude Code documentation
- Connect to MCP servers, Claude Code documentation
- Configure permissions, Claude Code documentation
- Build an MCP server, Model Context Protocol documentation, with the official SDKs
Checked on
Claude Code 2.1.283 on AlmaLinux 9.8, with Python 3 from the system. Tested in throwaway git projects: the hosted docs server, the server-info server above over stdio, the missing -- error, MCP tool permissions in Manual mode with and without an allow rule, tool search, and a never-trusted folder with a .mcp.json server, in an interactive session and with claude -p, --setting-sources user and --strict-mcp-config. The messages above were copied from those runs.