fleetlessfleetlessdocs
Concepts/MCP

🤖 MCP

An AI tool speaking the Model Context Protocol drives a robot on a signed-in person’s behalf: datapoints, services, actions, jobs, snapshots. Two endpoints serve the two populations Fleetless holds.

Two endpoints

Endpoint Who signs in Sign-in Tools
https://mcp.fleetless.dev your team, Fleetless users the Fleetless portal — emailed code or passkey 14 robot tools and 70 console_* tools
https://mcp.fleetless.dev/<app_identifier> the users of that one app your app’s own login page the same 14 robot tools

Both hosts are friendly aliases for the canonical resource identifiers, https://api.fleetless.dev/mcp and https://api.fleetless.dev/mcp/<app_identifier>, and every OAuth issuer, resource and audience is minted from that base URL and checked against it, never against the arriving host. Use them where a tool insists on a full path.

An app user cannot use the central endpoint, and a Fleetless user cannot use an app’s. Neither endpoint does three things:

  • No live camera. A camera is one snapshot tool, never a video session.
  • No MCP resources or prompts. Tools only.
  • No role-shaped catalog. Every session sees the same list, and the refusals do the talking.

Your team: the central endpoint

Paste https://mcp.fleetless.dev. Every Fleetless user has access, unconditionally, and the login is always by emailed code or passkey, never a password, with no federation branch.

There is no client id to configure. The authorization server advertises an RFC 7591 registration_endpoint, so a tool registers itself on first connection; Fleetless registers public clients only. A tool that cannot register itself uses the well-known id fleetless-central-mcp, the one client that skips consent.

Every self-registered client passes a consent screen naming it before a code is minted. Central consent is asked on each connection and nothing is stored — a remembered grant with no withdrawal door would be the worse answer.

Your users: an app’s endpoint

Switch it on per app with mcp_enabled, then hand your users https://mcp.fleetless.dev/<app_identifier>. The authorize step renders nothing: it writes an interaction and redirects the browser to the app’s mcp_login_url, with the interaction id substituted for {interaction}. Switching mcp_enabled on needs the Plus plan or above; see Plans and Limits.

  1. Read the pending request (GET /api/client/mcp/interactions/:id) with the user signed in — the bearer moves one field, already_granted, and an anonymous read always answers false.
  2. Sign the person in with the login form your app already has.
  3. Draw a consent screen and post POST …/:id/approve or POST …/:id/deny with the user’s own access token. The reply carries the URL to send the browser to; a denial redirects too, with error=access_denied.
Field What it is
client_name The client’s own claim, from an unauthenticated registration
client_name_verified The literal false; there is no verified case
scopes Always empty. The user’s role decides, re-read per call
already_granted A record, not a check. Approve succeeds without one

An interaction lives ten minutes, and the approving token’s app_id must match it; anything else answers one 410. A standing consent lets a later authorization skip your consent screen, and two doors withdraw it: the app user’s own (DELETE /api/client/mcp/grants/:clientId) and the developer’s (DELETE /api/apps/:id/users/:userId/mcp-grants/:clientId).

Withdrawal cuts the live session, at that client's very next call. The endpoint reads the standing consent on every request, so a withdrawn client is refused with a 401 and a challenge back to your consent screen, unexpired token and all. It ends one client, not the person.

Both endpoints issue refresh tokens: each refresh consumes the presented token, answers a new pair, and lives ninety days from its last use. A blocked account, a withdrawn consent or a removed team member is re-read at every refresh, and a refused refresh revokes the session. An app user’s password change, or a two-factor reset — an owner’s on Settings › Team for a Fleetless user, an app’s admin for one of its own users — revokes every session the account holds, without waiting to be asked.

The catalogs

Every session is offered the same tools; what differs is what a call answers. These fourteen serve both endpoints.

Tool Input What it does
robots_list — The robots you reach, with bridge state and configuration version
robot_describe robot_id The datasheet for one robot
datapoint_read robot_id, slug The latest value, at the bridge’s capture time
datapoint_history robot_id, slug, from The recorded series, optionally bucketed
service_call robot_id, slug Calls a service, waits for its result
action_start robot_id, slug Starts an action; the result, or the running job
jobs_list robot_id What runs now; with history: true, what has run. That half needs action_history
job_get robot_id, slug The most recent job on one slug
job_cancel robot_id, slug Cancels what runs on a slug, or only job_id; answers with the action server’s verdict
publish robot_id, slug, message One message to a publisher, never a stream
camera_snapshot robot_id, slug The most recent snapshot, with its age
assets_list robot_id Meshes and textures. Needs assets, as do the two below
asset_get robot_id, asset_id One asset’s metadata and a signed link
urdf_get robot_id A signed link to the URDF, mesh references rewritten

robot_describe is the datasheet: every exposure the role grants, with slug, kind and description, a datapoint’s unit and decimals, and the parameter JSON Schema built from the configuration’s own rules — min_value/max_value/enum/regex, as minimum/maximum/enum/pattern. An exposure with no description is listed with description: null rather than hidden.

A Fleetless user is additionally offered one tool per console operation, each calling the service the console’s own route calls, so it audits as that person. An app user is offered none.

Family Tools What for
Robots 13 Create, rename, details, delete with a preview, exposures, asset sync, token rotate, joint-state mapping, clearing the store
Configuration 8 The draft, publish, versions and restore, slug usage and renames
Introspection 4 The cached ROS graph, message types, and refreshing both
Apps 6 List, read, create, update; the auth configuration
Roles 5 Roles, their permissions, the per-role MCP preview
Server keys 4 List, create, rotate, revoke
Team 4 The organisation’s Fleetless users
Team invitations 4 List, invite, re-issue, revoke
App users 8 List, invite, invitations, block, unblock, reset mail, two-factor reset
Org 4 The organisation, its quotas, its metered usage
Audit 1 The organisation’s audit events
Fleet health 6 Alerts, job runs, bridge latency, resource health

console_config_draft_put replaces the whole fleetless.yaml; there are no targeted edits, on purpose.

Secrets come back once. console_robot_create and console_robot_token_rotate return the bridge token, console_server_key_create and console_server_key_rotate the key. No tool reads one back, so a lost secret is replaced by rotating.

Writing a description a model can use

A description lives in the robot’s own fleetless.yaml under description: — one per datapoint, service, action, publisher and camera, plus one per parameter. It is the same text for every app that reaches that robot, so write for the least specialized audience.

The model has never seen your robot. Say what the value or action physically means, what it costs in side effects and irreversibility, and the preconditions an operator knows and a model does not. A datapoint’s unit, a parameter’s min_value/max_value/enum/regex and the kind travel as fields of their own.

The concrete sentence is the perishable one. "Drives to the dock at 0.4 m/s" is actionable today and a lie the moment someone retunes the docking speed, with nothing to notice. Say what a call does and what it costs, and let the datasheet carry the numbers: they are read from the configuration and cannot drift from it.

For the person pasting the URL

Add the endpoint as a remote MCP server. Most tools ask for a URL and nothing else; discovery, registration and the token exchange follow on their own. Sign in, and the tool list appears.

The URL is not a secret, and a tool being in the list is not a promise that it works: the refusals are where a role makes itself felt.

Standing approvals at an app’s endpoint are yours to take back from inside the app that issued them, and the developer can take them back too — see MCP consent.