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:- 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.
- 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.
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.
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
--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: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:
--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
flightanddestructive, 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 anadmin 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 MCPtools, 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.