Skip to main content
Version: Next 🚧

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​

  • MCP Auth — bearer tokens, headers, env-based credentials.
  • Tool Use — how tools (MCP or otherwise) are dispatched.
  • make_mcp_agent_tool source — the schema → Pydantic adapter, if you need to customise.