Connect MCP servers
Give an agent the tools of any MCP (Model Context Protocol) server — an external stdio or HTTP server connected per turn, or your own @tool functions bundled into an in-process server.
| External server | In-process server | |
|---|---|---|
| Configured on | HostConfig.mcp_server_resolver | Options.mcp_servers |
| Enabled | per turn, by alias (enabled_mcp=) | always, for that agent |
| Tool names the model sees | mcp__{alias}__{tool} | the bare @tool names |
| Runs in | a subprocess, or over HTTP | your process |
Connect an external server
Hand the host a resolver (alias -> spec | None), then name the aliases to connect on each turn:
from noeta.sdk import Client, HostConfig, McpHttpServerSpec, McpServerSpec, Options
from noeta.sdk.providers import AnthropicProvider
SERVERS = {
"fs": McpServerSpec(
alias="fs",
argv=("npx", "-y", "@modelcontextprotocol/server-filesystem", "/data"),
tool_subset=("read_file", "list_directory"), # None = every tool
),
"search": McpHttpServerSpec(
alias="search",
url="https://mcp.example.com/rpc",
headers=(("Authorization", "Bearer …"),),
),
}
client = Client(
Options(system_prompt="You are a helpful assistant.", name="my-agent"),
provider=AnthropicProvider(),
model="claude-sonnet-5",
host_config=HostConfig(mcp_server_resolver=SERVERS.get),
)
outcome = client.start(goal="List the data directory.", enabled_mcp=("fs",))The model now sees mcp__fs__read_file and mcp__fs__list_directory.
enabled_mcpis per turn and not stored with the task's configuration.start,send_goal,seed_startandseed_send_goalall take it.query()does not — external MCP needs aClient.- Credentials (
headers,env) live only in the spec you build. They never reach the event log or the model. - MCP tools are ordinary tools: guards, permission modes and approvals apply to them unchanged.
Spec fields
| Spec | Field | Notes |
|---|---|---|
| both | alias | must match ^[a-z0-9_-]{1,32}$ |
| both | tool_subset | raw tool names to keep; None keeps every advertised tool |
McpServerSpec | argv | launch command, run directly (never through a shell) |
McpServerSpec | env | extra environment for the process, as (("KEY", "value"), …) |
McpHttpServerSpec | url | the single JSON-RPC endpoint |
McpHttpServerSpec | headers | static headers sent on every request, as (("Name", "value"), …) |
HTTP servers that assign an Mcp-Session-Id (Streamable HTTP) get it echoed on every later request. To use your own HTTP transport, set HostConfig.mcp_http_post.
When a server fails or changes
- A server that cannot connect at turn start is dropped, an
McpServerSkippedevent is recorded, and the turn continues with the other servers. A duplicate alias raisesMcpConfigError. - A tool name longer than 64 characters or colliding with another fails fast; it is never truncated silently.
- Connections are pooled per host and shared across tasks. Idle ones close after
HostConfig.mcp_idle_ttl(default 1800 s). Callclient.reconnect_mcp("fs")(orreconnect_mcp()for all) after you change a server's config. - Serving several tenants? Set
HostConfig.mcp_scope_resolverso a stateful server is never shared between them — see Per-tenant memory.
Share servers with subagents
A subagent inherits the turn's enabled servers only if its definition activates mcp:
AgentDefinition(description="…", prompt="…", plugins=("mcp",))Bundle your own tools in-process
from noeta.sdk import Options, ToolContext, ToolResult, create_sdk_mcp_server, tool
@tool(
name="echo",
version="1",
description="Return the given text unchanged.",
input_schema={
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
"additionalProperties": False,
},
)
def echo(arguments: dict, ctx: ToolContext) -> ToolResult:
return ToolResult(success=True, output=arguments["text"])
my_mcp = create_sdk_mcp_server(name="my-tools", version="1.0.0", tools=(echo,))
options = Options(system_prompt="…", name="my-agent", mcp_servers=(my_mcp,))The tools run in your process with no subprocess or network hop, and keep their bare names — the model sees echo, not mcp__my-tools__echo. Pick names that don't collide with a built-in tool. examples/mcp_server.py is a runnable version.
Next
- Custom tools — write the
@toolfunctions you bundle - Sandbox — MCP stays on the host even when tools run in a container
- ADR: MCP connectors