跳到主要内容
版本:0.4

Loading MCP Tools

The Model Context Protocol defines a standard way for tool servers to expose capabilities to agents. CubePi ships two loaders that connect to an MCP server, enumerate its tools, and turn each one into a regular AgentTool you can hand to Agent(tools=…).

Install the extra:

pip install "cubepi[mcp]"

This pulls in the mcp SDK.

stdio transport: local subprocess

For tools that run as a local process (e.g. npm-published servers, a Python module, an internal CLI):

import asyncio
import sys
from cubepi import Agent, Model
from cubepi.mcp import load_mcp_tools_stdio
from cubepi.providers.anthropic import AnthropicProvider


async def main():
# Spawn a stdio MCP server and discover its tools.
tools = await load_mcp_tools_stdio(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp/sandbox"],
timeout=30.0,
)

agent = Agent(
provider=AnthropicProvider(api_key="…"),
model=Model(id="claude-sonnet-4-5-20250929", provider="anthropic"),
tools=tools, # all server tools, ready to use
)
agent.subscribe(lambda e, s=None: None)
await agent.prompt("List files in /tmp/sandbox.")


asyncio.run(main())

load_mcp_tools_stdio connects, calls list_tools, disconnects, and returns the AgentTool list. Each tool call spawns a fresh subprocess — v0.3 keeps things simple, no process pool.

Arguments:

ParameterPurpose
commandThe executable (e.g. "npx", sys.executable, /usr/bin/uvx)
argsargv for the server
envEnvironment variables (optional)
cwdWorking directory (optional)
timeoutPer-call wall-clock timeout for initialize / list / call

HTTP/SSE transport: remote server

For hosted MCP servers (Sentry, GitHub, internal services):

from cubepi.mcp import load_mcp_tools_http

tools = await load_mcp_tools_http(
server_url="https://mcp.example.com/sse",
headers={"Authorization": "Bearer <token>"},
timeout=30.0,
)

load_mcp_tools_http uses the MCP SDK's SSE client. Same model as stdio: one connection to enumerate, then a fresh connection per tool call.

See MCP Auth for the auth patterns you'll likely need with HTTP servers.

What you get back

Each entry in the returned list is an AgentTool:

  • name — the MCP tool name.
  • description — straight from the server (no rewrites).
  • parameters — a Pydantic model synthesised from the MCP inputSchema (JSON Schema → Pydantic via pydantic.create_model).
  • execute — closure that calls tools/call over the same transport.

The synthesised Pydantic model covers: string, integer, number, boolean, array, object (as dict[str, Any]), and enum (via Literal). Top-level constraints are preserved: description, pattern, minLength/maxLength, minimum/maximum (incl. exclusive variants), minItems/maxItems.

Mixing MCP tools with hand-written tools

They're the same type. Just concatenate the lists:

mcp_tools = await load_mcp_tools_stdio(command="…", args=[])
my_tools = [weather_tool, search_tool]

agent = Agent(
provider=provider,
model=model,
tools=my_tools + mcp_tools,
)

The model sees one combined JSON Schema; the loop dispatches each call to the right implementation.

Per-call vs reusable connections

CubePi opens a new transport per execute call. That's:

  • ✅ Simple — no pool lifecycle to manage.
  • ✅ Robust — a hung connection can't poison other tools.
  • ⚠️ Slower for stdio servers with heavy startup (a npx server can add seconds per call).

For high-throughput stdio servers, run the server as a persistent HTTP service instead and use load_mcp_tools_http.

Image and structured content

If an MCP tool returns image content blocks, CubePi maps them to ImageContent and includes them in the AgentToolResult.content. Anthropic provider relays these as image blocks in tool results; OpenAI providers currently strip them (the wire format doesn't support image-bearing tool results).

If the server returns structuredContent, it's exposed under AgentToolResult.details["structuredContent"] — useful for downstream programmatic access, but not shown to the model.

Common pitfalls

  • asyncio.TimeoutError immediately on first call — Server didn't finish initialize within timeout. Bump timeout=60 or higher; some servers do heavy setup.
  • Each tool call is slow — stdio subprocess spawn overhead. Run the server as HTTP, or write a custom adapter that keeps the subprocess alive.
  • Tools missing from the list — Server failed to advertise them. Run the server in isolation and call list_tools manually with the MCP CLI to inspect.
  • Pydantic validation rejects model output — The MCP inputSchema has constraints the model isn't honouring. Either loosen the schema on the server side or add a before_tool_call middleware that coerces.

See also