MCP Server
Otto provides a first-party MCP (Model Context Protocol) server for programmatic agent access. The server exposes Otto's full command surface as MCP tools over either stdio or Streamable HTTP transport.
Starting the server
# stdio — for local, process-spawned agent clients
otto mcp serve
# Streamable HTTP — for remote / container / ChatGPT-style hosts
otto mcp serve-http --api-key <key> [--port 3001] [--host 127.0.0.1] [--endpoint /mcp]
The serve subcommand runs on stdio transport and is intended to be spawned by an MCP client (agent framework); it does not accept interactive input. The serve-http subcommand serves the same tools over a network endpoint, with each session tracked by an Mcp-Session-Id header.
Transport and scope
serve(stdio): JSON-RPC messages on stdout, logs on stderr; for local process-spawned MCP clients.serve-http(Streamable HTTP): bearer-authenticated network endpoint (Authorization: Bearer <api-key>, or setOTTO_MCP_API_KEY) with allowed-origin validation; for remote / container / ChatGPT hosts. Binds127.0.0.1:3001/mcpby default — pass--host 0.0.0.0for network access.- Protocol: MCP 1.0 with initialize, tools/list, and tools/call
Available tools
The server exposes 25 tools organized by category:
Status tools
| Tool | Description |
|---|---|
otto_status | Show relay daemon status (running, port, pid) |
otto_commands_list | List available commands from a connected node |
Execution tools
| Tool | Description |
|---|---|
otto_cmd | Send a command to a connected node |
otto_test | Run a site command for testing (auto-registers controller if needed) |
otto_screenshot | Capture a screenshot of a URL |
otto_extract_content | Extract content with one tool (markdown, distilled_html, clean_html, raw_html, text) |
Observation tools
| Tool | Description |
|---|---|
otto_logs_list | List historical relay logs with optional filters |
otto_logs_follow | Follow live relay logs for a bounded duration |
otto_logs_export | Export relay logs as structured data |
otto_listener_subscribe_network | Subscribe to network interception updates on a tab |
otto_listener_unsubscribe | Unsubscribe an active listener |
Lifecycle tools
| Tool | Description |
|---|---|
otto_setup | Run Otto setup (configure relay, start daemon, download extension) |
otto_start | Start the relay daemon |
otto_stop | Stop the relay daemon |
otto_extension_update | Download and install the latest extension artifact |
otto_extension_info | Show installed extension metadata |
Identity tools
| Tool | Description |
|---|---|
otto_pair | Approve a pairing code to register a node |
otto_authcode | List pending pairing auth codes |
otto_revoke | Revoke stored refresh token and clear local auth |
otto_client_register | Register a new controller client |
otto_client_login | Exchange client credentials for access/refresh tokens |
otto_client_status | Show local controller client state |
otto_client_forget | Delete stored client secret and clear local auth |
otto_client_remove | Remove registered controller client at relay |
Config tools
| Tool | Description |
|---|---|
otto_config | Read or update Otto configuration |
Example MCP calls
List tools
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
Check status
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "otto_status",
"arguments": {}
}
}
You can also request connected node IDs with nodes: true:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "otto_status",
"arguments": { "nodes": true }
}
}
Run a command
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "otto_cmd",
"arguments": {
"action": "command.run",
"payload": "{\"site\":\"reddit.com\",\"command\":\"getPosts\"}"
}
}
}
Extract content (default markdown)
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "otto_extract_content",
"arguments": {
"url": "https://example.com"
}
}
}
otto_extract_content input highlights:
format:markdown(default),distilled_html,clean_html,raw_html,text- target selection: provide
urlortabSession selector: supported forclean_html,raw_html, andtextdistillMode/fallbackToReadability: formarkdownanddistilled_html
For selector discovery and command authoring flows, use format: "clean_html".
Error handling
All tools return errors in MCP standard format:
{
"content": [{ "type": "text", "text": "Error message" }],
"isError": true
}
Common error codes:
| Error | Meaning |
|---|---|
Missing controllerAccessToken | Run otto client login or otto pair <code> |
Missing targetNodeId | Multiple nodes connected; pass nodeId argument |
manual_login_required | Ask user to log in to the site manually |
acl_missing_node_grant | Ask user to approve controller in extension popup |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Server exits immediately | Stdio transport error | Ensure running in proper MCP client context |
| Tools not appearing in agent | Server not registered | Run otto agent install <runtime> |
| Commands fail with auth error | Controller not authenticated | Run otto client login |
targetNodeId errors | No node connected | Verify extension is loaded and connected |
Related pages
- Agent Setup — register Otto with agent frameworks
- Skills — Otto skill packages for agent workflows
- For Agents — agent constraints and decision flow
- otto mcp command reference