Spacebot integrates with the Model Context Protocol (MCP) to dynamically discover and use tools from external servers. MCP tools are available to workers, extending their capabilities beyond built-in tools.
What is MCP?
MCP (Model Context Protocol) is a standard for exposing tools to LLM agents. MCP servers:
Expose tools via a JSON-RPC protocol
Run as subprocesses (stdio transport) or HTTP services
Provide schemas for tool arguments
Handle execution and return results
Spacebot acts as an MCP client, discovering tools from configured servers and making them available to workers.
Configuration
Define MCP servers in your agent.toml:
[[ mcp ]]
name = "filesystem"
enabled = true
[ mcp . transport ]
type = "stdio"
command = "npx"
args = [ "-y" , "@modelcontextprotocol/server-filesystem" , "/workspace" ]
env = { HOME = "${HOME}" } # Environment variables
Stdio servers communicate over stdin/stdout. Spacebot spawns the subprocess and manages the lifecycle. [[ mcp ]]
name = "remote-api"
enabled = true
[ mcp . transport ]
type = "http"
url = "https://mcp.example.com/v1"
[ mcp . transport . headers ]
Authorization = "Bearer ${MCP_API_KEY}"
X-Custom-Header = "value"
HTTP servers use SSE (Server-Sent Events) for bidirectional communication.
Unique identifier for the server
Whether to connect to this server at startup (default: true)
Environment Variable Interpolation
Use ${VAR_NAME} in commands, URLs, and headers:
[[ mcp ]]
name = "github"
enabled = true
[ mcp . transport ]
type = "http"
url = "https://api.github.com/mcp"
[ mcp . transport . headers ]
Authorization = "Bearer ${GITHUB_TOKEN}"
Variables are resolved from the environment when the server connects.
Connection Lifecycle
Startup
Enabled MCP servers connect when the agent starts.
Tool Discovery
Spacebot calls tools/list to discover available tools and their schemas.
Registration
Tools are registered with workers via McpToolAdapter.
Retry on Failure
If a server fails to connect, Spacebot retries with exponential backoff (5s → 10s → 20s → 40s → 60s, max 12 attempts).
Runtime Updates
MCP servers can notify Spacebot when their tool list changes via the notifications/tools/list_changed message.
pub async fn connect_with_retry ( self : & Arc < Self >) -> bool {
const MAX_ATTEMPTS : usize = 12 ;
const INITIAL_DELAY_SECS : u64 = 5 ;
const MAX_DELAY_SECS : u64 = 60 ;
let mut delay_secs = INITIAL_DELAY_SECS ;
for attempt in 1 ..= MAX_ATTEMPTS {
match self . connect () . await {
Ok (()) => return true ,
Err ( error ) => {
tokio :: time :: sleep ( Duration :: from_secs ( delay_secs )) . await ;
delay_secs = ( delay_secs * 2 ) . min ( MAX_DELAY_SECS );
}
}
}
false
}
MCP tools appear in workers’ tool lists with the server name as a prefix:
{
"name" : "filesystem__read_file" ,
"description" : "Read a file from the filesystem" ,
"parameters" : {
"type" : "object" ,
"properties" : {
"path" : { "type" : "string" , "description" : "File path" }
},
"required" : [ "path" ]
}
}
Workers call MCP tools like built-in tools:
{
"name" : "filesystem__read_file" ,
"arguments" : { "path" : "/workspace/README.md" }
}
Spacebot routes the call to the MCP server, executes via tools/call, and returns the result.
Connection States
MCP servers have four states:
Connecting Initial connection attempt in progress
Connected Server is active, tools available
Failed Connection failed (includes error message)
Disconnected Server was disconnected or disabled
Query server status via the API:
curl http://localhost:3000/api/agents/spacebot/mcp/status
{
"servers" : [
{
"name" : "filesystem" ,
"enabled" : true ,
"transport" : "stdio" ,
"state" : "connected"
},
{
"name" : "github" ,
"enabled" : true ,
"transport" : "http" ,
"state" : "failed" ,
"error" : "connection refused"
}
]
}
Runtime Management
curl -X POST http://localhost:3000/api/agents/spacebot/mcp/filesystem/reconnect
Disconnects and reconnects a server. Use this after updating server config or credentials. Modify agent.toml and reload: curl -X POST http://localhost:3000/api/agents/spacebot/reload
Spacebot reconciles the new config:
Removed servers are disconnected
New servers are connected
Changed servers are reconnected
Unchanged servers remain active
Security Considerations
Stdio servers inherit Spacebot’s process environment. Only PATH is preserved by default. Set explicit env vars in the [mcp.transport.env] table.
Stdio servers run with:
let mut child_command = tokio :: process :: Command :: new ( & command );
child_command . env_clear ();
if let Ok ( path ) = std :: env :: var ( "PATH" ) {
child_command . env ( "PATH" , path );
}
child_command . envs ( & env );
This prevents leaking secrets from Spacebot’s environment to MCP servers.
HTTP servers are responsible for their own authentication. Use Authorization headers for API tokens.
Blocked Network Access
HTTP transport validates URLs to prevent SSRF (Server-Side Request Forgery):
Private IPs (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are blocked
Loopback (127.0.0.0/8, ::1) is blocked
Link-local (169.254.0.0/16, fe80::/10) is blocked
Cloud metadata endpoints (169.254.169.254, metadata.google.internal) are blocked
If you need to connect to a local MCP server, use stdio transport instead.
Example Servers
[[ mcp ]]
name = "filesystem"
enabled = true
[ mcp . transport ]
type = "stdio"
command = "npx"
args = [ "-y" , "@modelcontextprotocol/server-filesystem" , "/workspace" ]
Provides file read/write tools scoped to /workspace.
[[ mcp ]]
name = "github"
enabled = true
[ mcp . transport ]
type = "http"
url = "https://api.github.com/mcp/v1"
[ mcp . transport . headers ]
Authorization = "Bearer ${GITHUB_TOKEN}"
Accept = "application/json"
Exposes GitHub API operations as tools.
[[ mcp ]]
name = "postgres"
enabled = true
[ mcp . transport ]
type = "stdio"
command = "npx"
args = [ "-y" , "@modelcontextprotocol/server-postgres" ]
env = { DATABASE_URL = "${DATABASE_URL}" }
Provides SQL query tools.
MCP tools are prefixed with the server name to avoid collisions:
filesystem__read_file
filesystem__write_file
github__create_issue
postgres__query
Workers see the full tool name in their tool list. The LLM learns to use the prefix from the tool schema.
Implementation Details
pub struct McpConnection {
name : String ,
config : McpServerConfig ,
state : RwLock < McpConnectionState >,
client : Mutex < Option < McpClientSession >>,
tools : RwLock < Vec < rmcp :: model :: Tool >>,
tool_list_changed : Arc < AtomicBool >,
}
MCP integration uses the rmcp crate for protocol implementation.
Best Practices
One Server Per Service Don’t bundle unrelated tools in one server. Split by domain (filesystem, github, database).
Clear Tool Names Tool names become part of the worker’s tool list. Use descriptive names like read_file, not rf.
Handle Errors Gracefully Return structured errors, not exceptions. The LLM sees the error and can retry or adjust.
Document Tools Provide detailed descriptions and parameter schemas. Workers rely on this to understand what tools do.