Connecting tools with MCP
Customizing agents · topic customizing-agents/mcp
The Model Context Protocol is a standard way to expose tools and data to an agent through a server the agent's client connects to. This topic covers the server and client roles, the transports that connect them, the primitives a server or a client can offer, when a plain command-line tool is the better choice, what a tool costs in tokens, and the security consequences of connecting one.
Concepts
- Server
- The process that exposes tools, resources and prompts over the protocol. A server wraps something, a database, an API, a file system, a browser, and describes each tool with a name, a purpose and a parameter schema the model can read. Write or install a server once and any compliant agent can use it, which is the protocol's point. glossary
- Client
- The side inside the agent application that connects to servers, lists the tools they offer, passes those descriptions to the model, executes the model's tool calls against the right server and returns results. The client and the host application around it hold the connection configuration, the approval prompts and the permission checks, so they are where a user controls what a server may do. glossary
- Transport
- How client and server exchange messages. A local server usually runs as a child process talking over standard input and output. A remote one is reached over HTTP, often with streaming. The transport decides where the server's code runs and what credentials it holds, which makes it a security decision as much as a plumbing one. glossary
- MCP primitives
- The features a server or a client can offer. In revision 2026-07-28, a server offers tools the model calls, resources with a URI that the user or the model reads as context, and prompts the user picks by name. A client can offer elicitation, so a server can ask the user for more input. That revision deprecates roots and sampling. Roots name the directories a client finds relevant, and the protocol doesn't enforce them. Sampling lets a server ask the client's model for a completion. glossary
- MCP vs CLI
- An agent can often reach the same system through a protocol server or by running a command-line tool it already knows. A server gives typed tools, discoverability and a stable interface across agents. A CLI is already installed, costs no tool descriptions in context and is easy to audit. Prefer the CLI when it exists and is good. Add a server when the interface needs shaping. glossary
- Tool cost
- Every connected tool adds to the context on every turn. Without tool search that is its full description. With tool search, which Claude Code uses by default, only the tool's name and its server's instructions load until the model searches for the tool. Every call adds its result, and verbose results crowd out the conversation. Connect only the servers a project needs, prefer servers with few well-described tools, and watch result sizes. glossary
- MCP security
- Connecting a server lets the agent reach everything the server can reach. Risks include servers from untrusted sources, tool descriptions that carry injected instructions, tools that return attacker-controlled content, and tools that can send data outward. Vet the source, run with least privilege, approve risky tools per call and review what leaves. Connect a server with your own identity, never with a shared service account, so it can do only what you may do and its actions trace back to you. glossary
- Stdio logging
- On the transport over standard input and output, the server's standard output carries protocol messages only, and the specification forbids writing anything else there. A stray print statement or a library's log line corrupts the message stream, and the client fails in ways that are hard to trace. Logs go to standard error, which the client may capture, show or ignore, and which it does not treat as a sign of failure. glossary
Links
- Builds on: Project instructions, What an agent is
- Leads to: Guardrails and production
- Competencies drawing on it: Runs an agent in production with guardrails, Connects an agent to tools and data safely
Lessons
- Logging from a stdio server (tutorial)
- Connecting an agent to your systems with MCP (explanation)
- Connecting your first tool server (tutorial)
- Hardening a tool connection (tutorial)
- Measuring what a tool server costs (tutorial)
Your reference
Each lesson above adds its takeaways and its example here once you finish it. Your reference lists every lesson you have finished.
Logging from a stdio server
Unlocks when you finish Logging from a stdio server.
Takeaways
- On the stdio transport, standard output carries one JSON-RPC message per line and nothing else. A server logs to standard error, which the client may show, keep or drop, and which isn't a sign of failure.
- A
printwrites to standard output by default, so one debug line arrives where the client expects a message. What happens next is up to the client: the test client here stops, and another may drop the line and report an error. The rule is broken in either case. - The fix is to write the line to standard error, which keeps the information. MCP revision 2026-07-28 deprecates the in-protocol logging feature and points stdio servers to standard error.
- To read a server's log with any agent, start it through
sh -cand append its standard error to a file, then read the file in a second terminal.
Example
Call the server · open in the lesson
Run call_server.py in the fixture directory, and compare with the output below.
python3 call_server.pyPrints the lines below (verified in CI from site/examples/customizing-agents/logging-from-a-stdio-server/call_server.py)
stdout line: debug: tools/call list_directory parsed: no (Expecting value) stderr: (empty)
Connecting an agent to your systems with MCP
Unlocks when you finish Connecting an agent to your systems with MCP.
Takeaways
- Every tool call through a remote server should run as the person using the agent, through an interactive login with a short-lived token, so the user's permissions bound the agent and the audit log names a person.
- Inventory a server by read, write and can't before connecting it. The can't list bounds the worst case, and the description count is a context cost you pay on every turn.
- The server's rate limit protects the platform. A write cap in a proxy and confirmation before each write protect your data, and version history only makes recovery possible.
- The five-rule prompt block stops accidents and naive attacks. It can fail, so it is one layer and never the only one.
- Rate the residual risks for your own context, read every tool description you install, and prefer a CLI with command-pattern permissions when one exists and does the job.
Example
Count the inventory · open in the lesson
What does this print?
reads = [t for t in TOOLS if t[1] == "read"]writes = [t for t in TOOLS if t[1] == "write"]print(f"{len(reads)} read, {len(writes)} write, {len(CANNOT)} cannot")Prints: 8 read, 7 write, 5 cannot (verified in CI from site/examples/customizing-agents/mcp/inventory.py)
Connecting your first tool server
Unlocks when you finish Connecting your first tool server.
Takeaways
- A local server runs over stdio as a child process of the agent. The client asks
tools/listonce and sendstools/callper use, and a refusal comes back as a result withisErrorset. - Scope the server before it runs. The reference file-system server allows the roots its client sends, or its command-line directories when the client sends none, and Claude Code's roots are the directory you started it in plus what you add with
--add-dir. Roots are deprecated in MCP revision 2026-07-28. Give the copy as the argument too, so a client that stops sending roots still leaves the server on the copy. Start the agent inside the copy, and readlist_allowed_directoriesfirst. - In Claude Code,
claude mcp add <name> -- <command>adds a server at local scope by default, and a project-scoped.mcp.jsonserver is approved by you before its first use in an interactive session. - The first task for a new server is one whose answer you know. Read the transcript for the tool name, the arguments and the bucket, read or write, of every call.
- A remote server over Streamable HTTP does the same work with code you didn't start, and it holds an OAuth token issued for that one server, which the specification wants short-lived.
Example
List the tools · open in the lesson
Run list_tools.py in the fixture directory, and compare with the output below.
server = client.start(client.HANDBOOK)response = client.request(server, "tools/list")for tool in response["result"]["tools"]: print(tool["name"])Prints the lines below (verified in CI from site/examples/customizing-agents/mcp-first-server/list_tools.py)
list_allowed_directories list_directory read_text_file
Hardening a tool connection
Unlocks when you finish Hardening a tool connection.
Takeaways
- Give a tool connection the smallest token that does the task, with a short life. The specification asks for short-lived tokens so a leaked one does little, and a scope without a send removes the send for every model and every prompt.
- Turn on a log of tool calls with their arguments and outcomes, keep it outside the folder the agent can reach, and read it in the server's first weeks. The specification asks clients to log tool usage for audit.
- A planted instruction shows up in the log as a call the task didn't need. The line tells you which control acted: the token, the folder, or nothing, when the call was allowed.
- Claude Code v2.1.199 or later asks before every call of a tool its server marks with
anthropic/requiresUserInteraction, and no permission mode approves it for you. A hostile server can leave that flag off. The token is checked by the service, whatever the server declares. - Start the agent inside the folder it may touch, in Manual mode. There it asks before every edit, and a change above that folder needs your explicit permission. A server that takes no roots holds the folder you passed it, whatever the client sends.
Example
Predict the three tokens · open in the lesson
What does this print? The server checks the expiry first and the scope second.
TOKENS = [ ("read+share, 720 hours", notes_token.issue("read+share", 720, "you")), ("read, 1 hour", notes_token.issue("read", 1, "you")), ("read, 0 hours", notes_token.issue("read", 0, "you")),]
for label, token in TOKENS: session = client.Session(token) text, is_error = session.call("share_note", {"name": "retro.md", "to": "archive@example.com"}) session.close() print(f"{label}: {text}")Prints the lines below (verified in CI from site/examples/customizing-agents/mcp-hardening/scopes.py)
read+share, 720 hours: Sent retro.md to archive@example.com read, 1 hour: Refused: token scope is read, and share_note needs share read, 0 hours: Refused: token expired
Measuring what a tool server costs
Unlocks when you finish Measuring what a tool server costs.
Takeaways
- A connected server's tool definitions are part of the model's input. Without on-demand loading, the model reads all of them on every turn, and twenty tools of a normal size come to a few thousand tokens.
- A query costs its call and its result, and both stay in the conversation. A verbose result is read again on every later turn, so measure what your server returns and not only what it lists.
- Claude Code's tool search is on by default, and until the model searches, only the names and server instructions are in the context. It lowers the per-turn cost, and the names, the upfront fallbacks and the reach of every tool remain.
- Keep only the tools a task uses, use the CLI for occasional queries and the server for constant structured calls, and turn off the servers the project doesn't use.
Example
Measure the definitions · open in the lesson
Run definitions.py in the fixture directory, and compare with the output below.
tools = measure.list_tools()definitions = measure.definitions_text(tools)names = measure.names_text(tools)print(f"tools: {len(tools)}")size = f"{len(definitions):,} characters, about {measure.tokens(definitions):,} tokens"print(f"definitions: {size}")print(f"names only: {len(names):,} characters, about {measure.tokens(names):,} tokens")Prints the lines below (verified in CI from site/examples/customizing-agents/mcp-tool-cost/definitions.py)
tools: 20 definitions: 13,788 characters, about 3,447 tokens names only: 553 characters, about 138 tokens
Sources
AEC-14Agent protocols, MCP and A2A, Agent Engineer Course (course)AEC-16MCP deep dive: MCP versus CLI, security failure modes, token cost, Agent Engineer Course (course)DLAI-9MCP: Build Rich-Context AI Apps with Anthropic, DeepLearning.AI (course)Academy introduction-to-model-context-protocolIntroduction to Model Context Protocol, Claude Academy (course)Academy model-context-protocol-advanced-topicsModel Context Protocol: Advanced topics, Claude Academy (course)Academy claude-code-101Claude Code 101, Claude Academy (course)Invariant tool poisoningMCP Security Notification: Tool Poisoning Attacks, Invariant Labs blog (reference)MCP specificationModel Context Protocol specification 2026-07-28, modelcontextprotocol.io (reference)MCP deprecated featuresDeprecated Features, Model Context Protocol specification 2026-07-28, modelcontextprotocol.io (reference)Claude Code mcpConnect Claude Code to tools via MCP, Claude Code documentation (reference)Claude Code costsManage costs effectively, Claude Code documentation (reference)