Documentation Index Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt
Use this file to discover all available pages before exploring further.
IronClaw supports the Model Context Protocol (MCP), allowing you to connect to external tool servers that provide additional capabilities through a standardized JSON-RPC interface.
Location : src/tools/mcp/
What is MCP?
MCP (Model Context Protocol) is a standardized protocol for connecting AI agents to external tool servers. It provides:
JSON-RPC communication : Standard request/response protocol
Tool discovery : Dynamic tool listing via tools/list
Tool execution : Invoke tools via tools/call
Authentication : Support for OAuth and custom auth
Ecosystem : Pre-built servers for GitHub, Notion, Postgres, etc.
Protocol version : 2024-11-05
Location : src/tools/mcp/protocol.rs:6
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ MCP Server Integration │
│ │
│ IronClaw Agent ──▶ McpClient ──▶ HTTP/JSON-RPC ──▶ MCP Server │
│ │ (external process) │
│ │ │
│ ▼ │
│ Tool Wrapper ──▶ ToolRegistry │
│ (implements Tool trait) │
└─────────────────────────────────────────────────────────────────────────────┘
MCP Protocol Types
Location : src/tools/mcp/protocol.rs:8-28
pub struct McpTool {
pub name : String ,
pub description : String ,
pub input_schema : serde_json :: Value ,
pub annotations : Option < McpToolAnnotations >,
}
pub struct McpToolAnnotations {
pub destructive_hint : bool ,
pub side_effects_hint : bool ,
pub read_only_hint : bool ,
pub execution_time_hint : Option < ExecutionTimeHint >,
}
pub enum ExecutionTimeHint {
Fast , // < 1 second
Medium , // 1-10 seconds
Slow , // > 10 seconds
}
Example tool from MCP server :
{
"name" : "github-copilot_list_issues" ,
"description" : "List issues for a repository" ,
"inputSchema" : {
"type" : "object" ,
"properties" : {
"owner" : { "type" : "string" , "description" : "Repository owner" },
"repo" : { "type" : "string" , "description" : "Repository name" },
"state" : { "type" : "string" , "enum" : [ "open" , "closed" , "all" ] }
},
"required" : [ "owner" , "repo" ]
},
"annotations" : {
"destructive_hint" : false ,
"read_only_hint" : true
}
}
Requests
Location : src/tools/mcp/protocol.rs:78-148
pub struct McpRequest {
pub jsonrpc : String , // "2.0"
pub id : u64 ,
pub method : String ,
pub params : Option < serde_json :: Value >,
}
impl McpRequest {
// Initialize connection
pub fn initialize ( id : u64 ) -> Self ;
// List available tools
pub fn list_tools ( id : u64 ) -> Self ;
// Call a tool
pub fn call_tool ( id : u64 , name : & str , arguments : serde_json :: Value ) -> Self ;
}
Responses
Location : src/tools/mcp/protocol.rs:150-176
pub struct McpResponse {
pub jsonrpc : String ,
pub id : u64 ,
pub result : Option < serde_json :: Value >,
pub error : Option < McpError >,
}
pub struct McpError {
pub code : i32 ,
pub message : String ,
pub data : Option < serde_json :: Value >,
}
Content Blocks
Location : src/tools/mcp/protocol.rs:268-292
pub enum ContentBlock {
Text { text : String },
Image { data : String , mime_type : String },
Resource { uri : String , mime_type : Option < String >, text : Option < String > },
}
MCP Client
Location : src/tools/mcp/client.rs
pub struct McpClient {
base_url : String ,
http_client : reqwest :: Client ,
request_id : AtomicU64 ,
config : Option < McpServerConfig >,
session_manager : Option < Arc < McpSessionManager >>,
secrets : Option < Arc < dyn SecretsStore >>,
user_id : Option < String >,
}
impl McpClient {
// Create client for unauthenticated server
pub fn new ( base_url : impl Into < String >) -> Self ;
// Create client for authenticated server
pub fn new_authenticated (
config : McpServerConfig ,
session_manager : Arc < McpSessionManager >,
secrets : Arc < dyn SecretsStore >,
user_id : impl Into < String >,
) -> Self ;
// Initialize connection
pub async fn initialize ( & self ) -> Result < InitializeResult , McpError >;
// List available tools
pub async fn list_tools ( & self ) -> Result < Vec < McpTool >, McpError >;
// Call a tool
pub async fn call_tool (
& self ,
name : & str ,
arguments : serde_json :: Value ,
) -> Result < CallToolResult , McpError >;
// Create Tool wrappers for registry
pub async fn create_tools ( & self ) -> Result < Vec < Arc < dyn Tool >>, McpError >;
}
Configuration
Location : src/tools/mcp/config.rs
Server Config
pub struct McpServerConfig {
pub name : String ,
pub url : String ,
pub auth : Option < OAuthConfig >,
pub metadata : HashMap < String , String >,
}
pub struct OAuthConfig {
pub authorization_url : String ,
pub token_url : String ,
pub client_id : String ,
pub client_secret : Option < String >,
pub scopes : Vec < String >,
pub redirect_uri : Option < String >,
}
Config File
mcp_servers.json:
{
"servers" : {
"github" : {
"url" : "https://mcp.github.com" ,
"auth" : {
"type" : "oauth" ,
"authorization_url" : "https://github.com/login/oauth/authorize" ,
"token_url" : "https://github.com/login/oauth/access_token" ,
"client_id" : "${GITHUB_OAUTH_CLIENT_ID}" ,
"client_secret" : "${GITHUB_OAUTH_CLIENT_SECRET}" ,
"scopes" : [ "repo" , "read:org" ]
}
},
"notion" : {
"url" : "https://mcp.notion.so" ,
"auth" : {
"type" : "oauth" ,
"authorization_url" : "https://api.notion.com/v1/oauth/authorize" ,
"token_url" : "https://api.notion.com/v1/oauth/token" ,
"client_id" : "${NOTION_OAUTH_CLIENT_ID}" ,
"scopes" : []
}
},
"postgres" : {
"url" : "http://localhost:3000" ,
"metadata" : {
"description" : "Local PostgreSQL MCP server"
}
}
}
}
Authentication
OAuth Flow
Location : src/tools/mcp/auth.rs
// Check if user has valid access token
pub async fn is_authenticated (
config : & McpServerConfig ,
session_manager : & McpSessionManager ,
user_id : & str ,
) -> bool ;
// Refresh expired access token
pub async fn refresh_access_token (
config : & McpServerConfig ,
session_manager : & McpSessionManager ,
secrets : & dyn SecretsStore ,
user_id : & str ,
) -> Result < String , AuthError >;
OAuth flow steps :
Check if access token exists and is valid
If expired, use refresh token to get new access token
If no refresh token, initiate new OAuth flow
Store new tokens securely
Session Management
Location : src/tools/mcp/session.rs
pub struct McpSessionManager {
sessions : RwLock < HashMap < String , McpSession >>,
}
pub struct McpSession {
pub user_id : String ,
pub server_name : String ,
pub access_token : String ,
pub refresh_token : Option < String >,
pub expires_at : Option < SystemTime >,
}
impl McpSessionManager {
pub fn new () -> Self ;
pub async fn get_session (
& self ,
user_id : & str ,
server_name : & str ,
) -> Option < McpSession >;
pub async fn store_session ( & self , session : McpSession );
pub async fn remove_session ( & self , user_id : & str , server_name : & str );
}
Usage Examples
Connect to Unauthenticated Server
use ironclaw :: tools :: mcp :: McpClient ;
// Connect to local server
let client = McpClient :: new ( "http://localhost:3000" );
// Initialize connection
let init = client . initialize () . await ? ;
println! ( "Connected to: {:?}" , init . server_info);
// List tools
let tools = client . list_tools () . await ? ;
for tool in tools {
println! ( "- {}: {}" , tool . name, tool . description);
}
// Call a tool
let result = client . call_tool (
"postgres_query" ,
serde_json :: json! ({
"query" : "SELECT * FROM users LIMIT 10"
}),
) . await ? ;
println! ( "Result: {:?}" , result );
Connect to OAuth Server
use ironclaw :: tools :: mcp :: { McpClient , McpServerConfig , OAuthConfig , McpSessionManager };
use std :: sync :: Arc ;
// Load config
let config = McpServerConfig {
name : "github" . to_string (),
url : "https://mcp.github.com" . to_string (),
auth : Some ( OAuthConfig {
authorization_url : "https://github.com/login/oauth/authorize" . to_string (),
token_url : "https://github.com/login/oauth/access_token" . to_string (),
client_id : std :: env :: var ( "GITHUB_OAUTH_CLIENT_ID" ) ? ,
client_secret : Some ( std :: env :: var ( "GITHUB_OAUTH_CLIENT_SECRET" ) ? ),
scopes : vec! [ "repo" . to_string ()],
redirect_uri : None ,
}),
metadata : HashMap :: new (),
};
let session_manager = Arc :: new ( McpSessionManager :: new ());
let secrets = Arc :: new ( my_secrets_store );
// Create authenticated client
let client = McpClient :: new_authenticated (
config ,
session_manager ,
secrets ,
"user_123" ,
);
// Use as normal
let tools = client . list_tools () . await ? ;
use ironclaw :: tools :: ToolRegistry ;
// Create client and initialize
let client = McpClient :: new ( "http://localhost:3000" );
client . initialize () . await ? ;
// Create tool wrappers
let tools = client . create_tools () . await ? ;
// Register with tool registry
let registry = Arc :: new ( ToolRegistry :: new ());
for tool in tools {
registry . register ( tool ) . await ;
}
println! ( "Registered {} MCP tools" , registry . count ());
MCP tools can provide hints about their behavior:
impl McpTool {
pub fn requires_approval ( & self ) -> bool {
self . annotations
. as_ref ()
. map ( | a | a . destructive_hint)
. unwrap_or ( false )
}
}
Location : src/tools/mcp/protocol.rs:68-76
Annotation types :
destructive_hint: Tool performs destructive operations (requires approval)
side_effects_hint: Tool has side effects beyond return value
read_only_hint: Tool only reads data
execution_time_hint: Expected execution time (fast/medium/slow)
Both are first-class in the extension system (ironclaw tool install handles both), but they have different strengths.
WASM Tools (IronClaw native):
✅ Sandboxed: fuel metering, memory limits, no access except allowlist
✅ Credentials injected by host, tool code never sees actual token
✅ Output scanned for secret leakage before returning to LLM
✅ Auth (OAuth/manual) declared in capabilities, agent handles flow
✅ Single binary, no process management, works offline
❌ Must build yourself in Rust, no ecosystem, synchronous only
MCP Servers (Model Context Protocol):
✅ Growing ecosystem of pre-built servers (GitHub, Notion, Postgres, etc.)
✅ Any language (TypeScript/Python most common)
✅ Can do websockets, streaming, background polling
❌ External process with full system access (no sandbox)
❌ Manages own credentials, IronClaw can’t prevent leaks
Decision guide :
Scenario Use Good MCP server already exists MCP Handles sensitive credentials (email send, banking) WASM Quick prototype or one-off integration MCP Core capability you’ll maintain long-term WASM Needs background connections (websockets, polling) MCP Multiple tools share one OAuth token (e.g., Google suite) WASM
Source : src/tools/README.md:105-136
MCP Server Examples
Popular MCP servers:
@modelcontextprotocol/server-github - GitHub API access
@modelcontextprotocol/server-postgres - PostgreSQL queries
@modelcontextprotocol/server-filesystem - File operations
@modelcontextprotocol/server-slack - Slack integration
notion-mcp-server - Notion workspace access
mcp-server-sqlite - SQLite database access
Find more at: https://github.com/modelcontextprotocol
Running MCP Servers
Node.js Server
# Install MCP server
npm install -g @modelcontextprotocol/server-postgres
# Run server
PORT = 3000 DATABASE_URL = postgresql://... npx mcp-server-postgres
Python Server
# Install MCP server
pip install mcp-server-sqlite
# Run server
mcp-server-sqlite --port 3000 --db-path ./data.db
Docker Server
docker run -p 3000:3000 \
-e DATABASE_URL=postgresql://... \
modelcontextprotocol/server-postgres
Protocol Details
Initialize Handshake
// Request
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"method" : "initialize" ,
"params" : {
"protocolVersion" : "2024-11-05" ,
"capabilities" : {
"roots" : { "listChanged" : false },
"sampling" : {}
},
"clientInfo" : {
"name" : "ironclaw" ,
"version" : "0.1.0"
}
}
}
// Response
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : {
"protocolVersion" : "2024-11-05" ,
"capabilities" : {
"tools" : { "listChanged" : false }
},
"serverInfo" : {
"name" : "postgres-mcp-server" ,
"version" : "1.0.0"
}
}
}
// Request
{
"jsonrpc" : "2.0" ,
"id" : 2 ,
"method" : "tools/list"
}
// Response
{
"jsonrpc" : "2.0" ,
"id" : 2 ,
"result" : {
"tools" : [
{
"name" : "postgres_query" ,
"description" : "Execute a SQL query" ,
"inputSchema" : {
"type" : "object" ,
"properties" : {
"query" : { "type" : "string" , "description" : "SQL query to execute" }
},
"required" : [ "query" ]
},
"annotations" : {
"destructive_hint" : false ,
"read_only_hint" : true
}
}
]
}
}
// Request
{
"jsonrpc" : "2.0" ,
"id" : 3 ,
"method" : "tools/call" ,
"params" : {
"name" : "postgres_query" ,
"arguments" : {
"query" : "SELECT * FROM users LIMIT 5"
}
}
}
// Response
{
"jsonrpc" : "2.0" ,
"id" : 3 ,
"result" : {
"content" : [
{
"type" : "text" ,
"text" : "id | name | email \n 1 | Alice | alice@example.com \n 2 | Bob | bob@example.com"
}
],
"is_error" : false
}
}
Next Steps
Building Tools Create custom tools using the builder
WASM Tools Build sandboxed WASM tools