MCP Integration
What is MCP?
The Model Context Protocol (MCP) is a JSON-RPC 2.0 protocol that lets AI agents discover and call tools from external servers. It defines a standard way for agents to connect to tool providers over two transports:
- Stdio — spawn a child process, communicate via stdin/stdout (newline-delimited JSON)
- HTTP — POST JSON-RPC requests to an HTTP endpoint
Connecting to MCP Servers
Stdio Transport
Use with_mcp_server_stdio() to spawn an MCP server process and register its tools:
use yoagent::Agent; use yoagent::provider::AnthropicProvider; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let mut agent = Agent::new(AnthropicProvider) .with_system_prompt("You are a helpful assistant with file access.") .with_model("claude-sonnet-4-20250514") .with_api_key(std::env::var("ANTHROPIC_API_KEY")?) .with_mcp_server_stdio( "npx", &["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], None, ) .await?; let rx = agent.prompt("List files in /tmp").await; // handle events... Ok(()) }
You can pass environment variables to the server process:
#![allow(unused)] fn main() { use std::collections::HashMap; let mut env = HashMap::new(); env.insert("API_TOKEN".into(), "secret".into()); let agent = Agent::new(provider) .with_mcp_server_stdio("my-mcp-server", &["--port", "0"], Some(env)) .await?; }
HTTP Transport
For remote MCP servers exposed over HTTP:
#![allow(unused)] fn main() { let agent = Agent::new(provider) .with_mcp_server_http("http://localhost:8080/mcp") .await?; }
How MCP Tools Work
When you call with_mcp_server_stdio() or with_mcp_server_http(), yoagent:
- Connects to the MCP server and performs the
initializehandshake - Calls
tools/listto discover available tools - Wraps each MCP tool as an
AgentToolviaMcpToolAdapter - Adds them to the agent's tool list
MCP tools appear alongside built-in tools. The LLM sees them with their original names, descriptions, and JSON Schema parameters — it can call them just like any other tool.
Mixing Built-in and MCP Tools
#![allow(unused)] fn main() { use yoagent::tools::default_tools; let agent = Agent::new(provider) .with_tools(default_tools()) // bash, read, write, edit, list, search .with_mcp_server_stdio("my-db-server", &[], None) .await?; // Agent now has both built-in coding tools AND MCP database tools }
Using the MCP Client Directly
For lower-level control, use McpClient directly:
#![allow(unused)] fn main() { use yoagent::mcp::{McpClient, McpToolAdapter}; use std::sync::Arc; use tokio::sync::Mutex; let client = McpClient::connect_stdio("my-server", &[], None).await?; let tools = client.list_tools().await?; for tool in &tools { println!("{}: {}", tool.name, tool.description.as_deref().unwrap_or("")); } // Call a tool directly let result = client.call_tool("read_file", serde_json::json!({"path": "/tmp/test.txt"})).await?; // Or wrap as AgentTool adapters let client = Arc::new(Mutex::new(client)); let adapters = McpToolAdapter::from_client(client).await?; }
Error Handling
MCP operations return McpError:
McpError::Transport— connection or I/O failureMcpError::Protocol— unexpected response formatMcpError::JsonRpc— server returned a JSON-RPC errorMcpError::ConnectionClosed— server process exited
When an MCP tool returns isError: true, the adapter converts it to a ToolError::Failed, which the agent loop sends back to the LLM with is_error: true so it can self-correct.