Skip to main content

MCP Server and AI Control

ADOS ships a Model Context Protocol connector so an MCP-capable client (Claude Code, Claude Desktop, Cursor, VS Code, or a plain HTTP client) can read a node, administer it, and, when you explicitly grant it, fly it. Two halves, and they are separate decisions:
  1. The connector is a Node server you run on your own machine, from the ADOS-MCP repo. It is the MCP server your AI client talks to, and a client of the agent’s HTTP API.
  2. The agent-side token edge is the scoped credential an agent will accept in place of the full pairing key. It is off by default and stays inert until you set mcp.token_accept_enabled.
No model runs on the drone. The connector holds no telemetry buffers and runs no inference; it calls interfaces that already exist and presents them through MCP.
Token acceptance ships off. mcp.token_accept_enabled defaults to false, and ados mcp status reports the posture. A token you mint before flipping the flag is a valid token the auth edge will not honour yet.

The trust boundary

The agent’s auth edge accepts three credentials on the LAN: the pairing key (X-ADOS-Key), a dashboard-PIN session, and, behind the accept flag, a scoped MCP token on the x-ados-mcp-token header. A new agent route is unreachable by token until it is classified, which is the intended direction: an unclassified route is denied rather than silently over-granted.

Scopes

Six scope groups. A token carries one or more; a route carries exactly one required class. flight and destructive are never granted implicitly. On the connector side, a tool whose class is flight (or that otherwise affects flight) is refused outright while the MAVLink proxy enforce flag is off.

Enable the token edge on a node

ados mcp enable writes the config key and restarts the control front so the new posture takes effect. It is a local root operation, not a REST call, so it needs sudo. Never hand-edit /etc/ados/config.yaml for this; the command is the sanctioned path.
Check the posture and the minted tokens at any time:
ados mcp status --json prints the raw /api/mcp/status body, which carries accept_enabled and a tokens array. Each token entry carries token_id, label, scopes, and the revoked / expired flags.

Mint a scoped token

The command prints a “shown once” banner followed by the token on its own line. The secret is never written to the store, so if you lose it you mint a new one. Store it in your MCP client’s environment, not in your shell history. On-box, the mint / status / revoke routes are admitted without a key because loopback is inside the trust boundary; off-box they take the front’s normal auth lane.

Revoke

A single revoke adds the id to the denylist. --all rotates the 128-bit salt folded into every derived token key, so every previously-minted token fails verification at once. That is the incident response: rotate, then re-mint the tokens you still want. See Revocation and incidents for the wider playbook.

Agent routes

Run the connector

The connector is not published as a package. Clone and build it:
Prerequisites: Node 20 or newer, git, and pnpm. pnpm install && pnpm build is the manual equivalent. Three deployment modes, selected with --target. All three expose the identical tool set; only the reach differs. Add one LAN node to Claude Code, with the key in the client environment rather than your shell:
Check reach without starting a server. --verify connects, checks auth and reachability, prints the result, and exits 0 or 1:

Connector flags

Environment equivalents include ADOS_MCP_AGENT_KEY, ADOS_AGENT_HOST, ADOS_NODE_ID, ADOS_MCP_HTTP_PORT, ADOS_MCP_FLEET, ADOS_MCP_FLEET_PATH, ADOS_MCP_DISCOVER, ADOS_MCP_OWNER, ADOS_MCP_TOKEN, ADOS_CONVEX_URL, ADOS_MQTT_URL, ADOS_MCP_AUDIT_PATH and ADOS_MCP_LOG_LEVEL.

Transports

  • Streamable HTTP: one endpoint, POST /mcp, with an SSE upgrade for streams. The primary networked transport.
  • stdio: the local one-liner a client launches. Its principal holds every scope except flight and destructive, passes the scope check like any other token, and declares that a human must be present. Presence on your laptop is not presence on the aircraft, so reaching the flight tools needs a flight-scoped --token.
  • Unix socket: /run/ados/mcp.sock, on-box in agent and local-fleet modes. Presence on that socket is the credential, so this principal skips the scope check. It does not skip the safety gate.

The safety gate

Every principal passes the class-specific safety gate, including the on-box socket principal. Presence on a socket is not a human confirming an action, so an admin call still needs its confirmation and a flight call still needs a fresh operator-present signal or a signed confirm bound to that call. Every call, allowed or denied, produces one redacted audit event. --sim only opts in to the SITL waiver. The server asks the target whether its flight controller is a simulator and refuses to start if it is not, so the waiver cannot be asserted onto real hardware. readOnlyHint and destructiveHint annotations are advertised honestly, but they are hints. The server is the enforcement point.

Extensions that contribute MCP tools

An extension can contribute MCP tools, resources and prompts on both its agent and GCS halves. That contribution class needs schema_version: 3 in manifest.yaml; 1 is the baseline and 2 unlocks the additional agent fields. See the manifest reference.