Integrations: real client, model and transport
Integrations are the real backends that replace each stand-in this course ran on: a client over a real transport, a real model, and a deployed server.
Last updated: 28 Sep, 2026 · MCP 2.2
Every runnable lesson so far used an in-process Client(mcp) and, in part 6, a stand-in that chose tools from the message. Both are keyless and deterministic, which is why they run here. This lesson maps each one to the version you run in production, and shows that the swap changes one line, not the agent.
What you ran on, and its real version
| What you ran on | A real one | The package or transport |
|---|---|---|
Client(mcp), a server object | A client over stdio or Streamable HTTP | StdioServerParameters or a URL, from mcp (the stdio and Streamable HTTP lessons) |
The stand-in choose_tool | A model that reads tool descriptions and returns tool calls | anthropic or openai, fed by to_model_tools (the MCP-tools-to-a-model lesson) |
A local python shop.py | A deployed, remote MCP server | Streamable HTTP behind a host name, with transport_security |
The ORDERS dict | A real database | A driver opened once in the lifespan (the Lifespan lesson) |
Swapping the client to stdio
The agent from the MCP-tools-to-a-model, stand-in-model and agent-loop lessons talks to a client. It never cares how that client connects, so moving off the in-process client changes only the argument you pass to Client:
# in memory, as the earlier lessons ran it:
async with Client(mcp) as client:
...
# the same agent, now over stdio:
server = StdioServerParameters(command=sys.executable, args=["shop.py"])
async with Client(server) as client:
...Here is the agent running unchanged over stdio, against shop.py started as a child process:
python desk_stdio.py- The agent code did not change. Only the object handed to
Clientdid, frommcptoStdioServerParameters. - The server ran as its own process, the way a desktop host launches it, and gave the same answers as in memory.
Swapping in a real model
The stand-in returned a name and arguments. A real model returns the same thing from a tool call, so it drops into choose_tool's place. This needs a key and a current model id, so it is shown, not run:
from anthropic import Anthropic
client_llm = Anthropic() # reads ANTHROPIC_API_KEY
MODEL = "..." # a current model id, read from config, not hardcoded here
def choose_tool(message, model_tools):
# Anthropic takes name, description and input_schema at the top level (lesson 20)
tools = [t["function"] for t in model_tools]
reply = client_llm.messages.create(model=MODEL, max_tokens=512, tools=tools,
messages=[{"role": "user", "content": message}])
for block in reply.content:
if block.type == "tool_use":
return {"name": block.name, "arguments": block.input}
return NoneThe rest of run_agent stays as it is: it still lists the tools, calls the chosen one over MCP, and reads is_error. The model decides which tool to call; the server still decides what each tool is allowed to do.
Where real backends replace the stand-ins
- The client swaps to stdio or Streamable HTTP with no change to the agent loop.
- The stand-in swaps to a real model that reads the same tool list.
- The
ORDERSdict swaps to a database opened once in the lifespan.
Related
- Change
desk_stdio.pyto the HTTP client from the Streamable HTTP lesson, startpython shop.py, and run the same messages. - Add a real model call from LLM Fundamentals, using
to_model_tools, behind anif os.environ.get("ANTHROPIC_API_KEY")guard. - Replace the
ORDERSdict with a small SQLite database opened in a lifespan (the Lifespan lesson).
Every expert started right here.