MCP Integration
What it does
Section titled “What it does”The MCP (Model Context Protocol) integration connects external MCP-compatible servers and injects their tools into the agent’s ToolRegistry. The LLM can then call these external tools the same way it calls local tools. The SDK uses NativeMcpBackend on native (via rmcp) and WasmMcpBackend on WASM (via mcp_protocol + Spin HTTP) — the correct backend is selected automatically by target architecture.
Enable the feature
Section titled “Enable the feature”[dependencies]agent_sdk = { package = "pf_agent_sdk", path = "../../crates/agent_sdk", features = ["llm-engine", "mcp-client"] }How to use it
Section titled “How to use it”-
Create an MCP servers config file
The config format is compatible with the industry-standard
mcp.jsonused by Cursor, Claude Desktop, VS Code, and other MCP clients.{"mcp_servers": {"tavily": {"command": "npx","args": ["-y", "tavily-mcp@latest"],"env": { "TAVILY_API_KEY": "${TAVILY_API_KEY}" }}}}{"mcp_servers": {"tavily": {"url": "https://mcp.tavily.com/mcp/?tavilyApiKey=...","tool_policy": {"expose": ["search"],"deny": []}}}} -
Load the config and connect
use agent_sdk::mcp_tools::{McpServersConfig, McpToolAdapter, NativeMcpBackend};use agent_sdk::agent::tools::ToolRegistry;use std::sync::Arc;let config = McpServersConfig::from_file("mcp_servers.json")?;let source = NativeMcpBackend::connect(&config).await?;let mut tools = ToolRegistry::new();McpToolAdapter::register_mcp_tools(&mut tools, Arc::new(source)).await?;// tools now contains entries like mcp_tavily_search, mcp_tavily_extract -
Wire into the agent
agent.configure_llm_runtime(llm_client,"gpt-4o",tools,Some("You are a helpful assistant with web search.".into()),None,None,)?;
Complete example
Section titled “Complete example”use agent_sdk::Agent;use agent_sdk::agent::tools::ToolRegistry;use agent_sdk::mcp_tools::{McpServersConfig, McpToolAdapter, NativeMcpBackend};use std::sync::Arc;
async fn build_agent_with_mcp( llm_client: impl agent_sdk::agent::llm_orchestrator::IntoLlmInvoker + agent_sdk::agent::llm_orchestrator::IntoLlmStreamInvoker + Clone,) -> anyhow::Result<Agent> { let mut agent = Agent::new("mcp-demo")?;
agent .add_skill("research") .description("Research topics using web search") .register()?;
// Load MCP servers config let config = McpServersConfig::from_file("mcp_servers.json")?; let source = NativeMcpBackend::connect(&config).await?;
// Register MCP tools into the agent's tool registry let mut tools = ToolRegistry::new(); McpToolAdapter::register_mcp_tools(&mut tools, Arc::new(source)).await?;
agent.configure_llm_runtime( llm_client, "gpt-4o", tools, Some("You are a research assistant with web search capabilities.".into()), None, None, )?;
Ok(agent)}Config fields
Section titled “Config fields”McpServerEntry
Section titled “McpServerEntry”| Field | Type | Description |
|---|---|---|
command | Option<String> | Stdio transport: command to execute (e.g. "npx") |
args | Vec<String> | Arguments to the command |
env | HashMap<String, String> | Environment variables (supports ${VAR} interpolation) |
url | Option<String> | Remote transport: SSE / streamable HTTP URL |
disabled | bool | Skip this server without removing config |
auth_token | Option<String> | Bearer token (supports ${VAR} interpolation) |
forward_caller_auth | bool | Forward inbound Authorization header to MCP server |
tool_policy | Option<ToolPolicy> | Tool exposure filtering |
ToolPolicy
Section titled “ToolPolicy”| Field | Type | Description |
|---|---|---|
expose | Vec<String> | Allowlist of tool names (empty = expose all) |
deny | Vec<String> | Denylist (takes precedence over expose) |
Transport types
Section titled “Transport types”| Transport | Config trigger | WASM | Native | Backend |
|---|---|---|---|---|
| Stdio | command is set | No | Yes | NativeMcpBackend (spawns child process) |
| Remote | url is set | Yes | Yes | WasmMcpBackend (WASM) / NativeMcpBackend (native) |
Tool naming convention
Section titled “Tool naming convention”MCP tools are registered with the naming pattern mcp_<server_id>_<tool_name>. Underscores are used instead of dots because LLM providers like OpenAI restrict tool names to ^[a-zA-Z0-9_-]+$.
Key types
Section titled “Key types”| Type | Module | Purpose |
|---|---|---|
McpToolAdapter | agent_sdk::mcp_tools::adapter | Registers MCP server tools into a ToolRegistry |
McpServersConfig | agent_sdk::mcp_tools::config | Top-level config: from_file(), from_json(), enabled_servers() |
McpServerEntry | agent_sdk::mcp_tools::config | Single server config with transport_type() |
McpTransportType | agent_sdk::mcp_tools::config | Stdio, Remote, or Unknown |
ToolPolicy | agent_sdk::mcp_tools::config | Allowlist / denylist filtering with is_exposed() |
NativeMcpBackend | agent_sdk::mcp_tools::native | Native backend wrapping rmcp |
WasmMcpBackend | agent_sdk::mcp_tools::wasm | WASM backend wrapping mcp_protocol + Spin HTTP |
McpToolSource | agent_sdk::mcp_tools::types | Trait: list_tools(), call_tool(), health_check() |