Skip to main content

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 set OTTO_MCP_API_KEY) with allowed-origin validation; for remote / container / ChatGPT hosts. Binds 127.0.0.1:3001/mcp by default — pass --host 0.0.0.0 for 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​

ToolDescription
otto_statusShow relay daemon status (running, port, pid)
otto_commands_listList available commands from a connected node

Execution tools​

ToolDescription
otto_cmdSend a command to a connected node
otto_testRun a site command for testing (auto-registers controller if needed)
otto_screenshotCapture a screenshot of a URL
otto_extract_contentExtract content with one tool (markdown, distilled_html, clean_html, raw_html, text)

Observation tools​

ToolDescription
otto_logs_listList historical relay logs with optional filters
otto_logs_followFollow live relay logs for a bounded duration
otto_logs_exportExport relay logs as structured data
otto_listener_subscribe_networkSubscribe to network interception updates on a tab
otto_listener_unsubscribeUnsubscribe an active listener

Lifecycle tools​

ToolDescription
otto_setupRun Otto setup (configure relay, start daemon, download extension)
otto_startStart the relay daemon
otto_stopStop the relay daemon
otto_extension_updateDownload and install the latest extension artifact
otto_extension_infoShow installed extension metadata

Identity tools​

ToolDescription
otto_pairApprove a pairing code to register a node
otto_authcodeList pending pairing auth codes
otto_revokeRevoke stored refresh token and clear local auth
otto_client_registerRegister a new controller client
otto_client_loginExchange client credentials for access/refresh tokens
otto_client_statusShow local controller client state
otto_client_forgetDelete stored client secret and clear local auth
otto_client_removeRemove registered controller client at relay

Config tools​

ToolDescription
otto_configRead 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 url or tabSession
  • selector: supported for clean_html, raw_html, and text
  • distillMode / fallbackToReadability: for markdown and distilled_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:

ErrorMeaning
Missing controllerAccessTokenRun otto client login or otto pair <code>
Missing targetNodeIdMultiple nodes connected; pass nodeId argument
manual_login_requiredAsk user to log in to the site manually
acl_missing_node_grantAsk user to approve controller in extension popup

Troubleshooting​

SymptomLikely causeFix
Server exits immediatelyStdio transport errorEnsure running in proper MCP client context
Tools not appearing in agentServer not registeredRun otto agent install <runtime>
Commands fail with auth errorController not authenticatedRun otto client login
targetNodeId errorsNo node connectedVerify extension is loaded and connected