LLM App Development #11: Connecting Tools with MCP
In Part 6 and Part 10 we defined tools ourselves and connected them to Claude. But for common targets like GitHub, Slack, or a database, writing the tool definitions by hand every time is tedious. MCP is a protocol that standardizes this connection. In this post we cover what MCP is and why to use it, and how to connect Claude to an MCP server.
What MCP is #
MCP (Model Context Protocol) is a protocol that standardizes how an LLM accesses tools and data. Think of USB. In the days when each device used a different connector, you had to match device and cable one by one, but once the USB standard appeared, you could connect everything in one way. MCP plays that role for connecting tools.
The structure splits into two sides.
- MCP server — the side that exposes some capability or data as tools. There is an MCP server for each target, like GitHub, a file system, or a database.
- MCP client — the side that connects to that server and uses its tools. Our app becomes the client.
The key is this. Once someone builds a GitHub MCP server, every app that connects to it uses the GitHub tools as is, without redefining them by hand.
Why use MCP #
You can build any number of tools the Part 6 way. But MCP has two advantages.
- Reuse — there are many ready-made MCP servers. Servers for GitHub, Slack, file systems, and various databases are published, so you just connect and use them. You do not have to write tool definitions and the actual call code yourself.
- Standard — once you make a tool into an MCP server, any app that supports MCP can use it. The same server works not only in our app but in other tools and environments.
The Part 6 way of defining tools yourself and MCP do not conflict. You build the special capabilities unique to your app yourself as in Part 6, and bring in general capabilities like GitHub via an MCP server, mixing the two.
Connecting Claude to an MCP server #
The SDK provides a helper to fetch an MCP server’s tools and connect them to Claude. It connects to the MCP server, gets the list of tools, and passes them straight to the tool runner from Part 10.
from anthropic import AsyncAnthropic
from anthropic.lib.tools.mcp import async_mcp_tool
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters
client = AsyncAnthropic()
async def main():
# connect to the MCP server
async with stdio_client(StdioServerParameters(command="mcp-server")) as (read, write):
async with ClientSession(read, write) as mcp:
await mcp.initialize()
# get the list of tools the server provides
tools = await mcp.list_tools()
# pass the fetched tools to the tool runner
runner = client.beta.messages.tool_runner(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Handle the task using the available tools."}],
tools=[async_mcp_tool(t, mcp) for t in tools.tools],
)
async for message in runner:
print(message)mcp.list_tools() gets the list of tools the server exposes, and async_mcp_tool converts those tools into a form Claude can use and passes them to the tool runner. We did not write the tools’ names or input schemas ourselves, because the server tells us. The tools definition we wrote by hand in Part 6 is replaced by a server connection in MCP.
Connecting to a remote MCP server #
The example above connected to an MCP server we ran ourselves. But you can also connect Claude directly to a remote MCP server already up on the internet. In that case you tell the call the server address with mcp_servers.
response = client.beta.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
mcp_servers=[
{"type": "url", "name": "my-tools", "url": "https://example.com/mcp"},
],
betas=["mcp-client-2025-11-20"],
messages=[{"role": "user", "content": "Handle the task with the MCP tools."}],
)Pass only the server address and Claude fetches and uses that server’s tools on its own. There is no need to run the server locally or to fetch the tool list yourself. That said, a server that needs authentication requires you to pass credentials too, and how to do so varies by server. This feature is also beta, so check the latest docs before applying it.
mcp package (pip install anthropic[mcp]). Details, such as connecting to a remote MCP server without running one yourself, are evolving quickly, so it is good to check the latest docs before applying it. The goal of this post is to understand the idea that “tools are brought in by a server connection.”Where people commonly trip up #
- Mistaking MCP as replacing tools — MCP is just a standard for connecting tools; it does not get rid of Part 6 tool calling. Internally, the same tool-call flow runs.
- Connecting to an untrusted server — an MCP server exposes tools to our app. Connecting to a server of unclear origin makes it hard to know what that server does. Use only trusted servers.
- Hardcoding authentication — servers like GitHub need authentication. Do not write the token directly in code; handle it with an environment variable or a separate credential store, as in Part 1.
Wrapping up #
In this post we covered MCP, the standard for connecting tools.
- MCP is a protocol that standardizes how an LLM accesses tools and data; the server exposes tools and the client uses them.
- Connecting a ready-made MCP server lets you reuse it as is, without writing tool definitions yourself.
- Build your app’s own special capabilities as tools yourself, and bring in general capabilities via MCP, mixing the two.
Now the core pieces are gathered: tools, retrieval, memory, agents, and MCP. In the next post, “LLM App Development #12: Cost, Evaluation, and Observability,” we will cover what you need to actually operate the app you built — cutting cost, measuring quality, and looking into its behavior.