Skip to main content
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.
name
string
required
Unique identifier for the server
enabled
boolean
Whether to connect to this server at startup (default: true)
transport.type
string
required
Either stdio or http

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

1

Startup

Enabled MCP servers connect when the agent starts.
2

Tool Discovery

Spacebot calls tools/list to discover available tools and their schemas.
3

Registration

Tools are registered with workers via McpToolAdapter.
4

Retry on Failure

If a server fails to connect, Spacebot retries with exponential backoff (5s → 10s → 20s → 40s → 60s, max 12 attempts).
5

Runtime Updates

MCP servers can notify Spacebot when their tool list changes via the notifications/tools/list_changed message.
src/mcp.rs
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
}

Tool Invocation

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.

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:
src/mcp.rs
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.

Tool Naming Convention

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.

Build docs developers (and LLMs) love