Contents

Sign-in and access

AI clients sign in to LetKite with OAuth 2.1. You approve each client once, choose what it may do and which machines it reaches, and can take that access back at any time.

Discovery

A client that calls https://mcp.letkite.com/mcp without a token gets HTTP 401 with a WWW-Authenticate header that points to the protected resource metadata. From there it finds the authorization server:

Protected resource metadatahttps://mcp.letkite.com/.well-known/oauth-protected-resource/mcp
Authorization server metadatahttps://mcp.letkite.com/.well-known/oauth-authorization-server (also at /.well-known/openid-configuration)
Issuerhttps://mcp.letkite.com
Authorization endpointhttps://mcp.letkite.com/oauth/authorize
Token endpointhttps://mcp.letkite.com/oauth/token
Revocation endpointhttps://mcp.letkite.com/oauth/revoke (refresh tokens)
Registration endpointhttps://mcp.letkite.com/oauth/register

Client registration

LetKite has no pre-registered clients. A client identifies itself in one of two ways:

  • Dynamic client registration (RFC 7591): the client posts its metadata to the registration endpoint and receives a client ID. A client secret is issued only to clients that ask for client_secret_post or client_secret_basic; public clients use none.
  • Client ID metadata document: the client ID is an https:// URL that serves the client's metadata. LetKite fetches it with a 5-second timeout, up to 64 KB, without following redirects, and only from public addresses.

Redirect URIs must be https, or http on a loopback address (localhost, 127.0.0.1) for local clients such as Claude Code, which may use any port (RFC 8252). LetKite recognizes the callbacks of Claude, ChatGPT and other known clients, and the sign-in page warns you when a client's callback is not one of them. Refused callbacks are recorded in the audit log.

The authorization flow

  • Authorization code flow only (response_type=code), with PKCE S256 required on every request.
  • Resource indicators (RFC 8707): the client may name https://mcp.letkite.com/mcp as the resource; tokens are always issued for it.
  • The authorization response carries iss (RFC 9207).
  • Grants: authorization_code and refresh_token.

Sign-in happens on LetKite's own sign-in page (id.letkite.com), with two-step verification where your account requires it. If you belong to several LetKite accounts, you choose one. The consent page then shows:

  • the client's name, how it registered, and where it sends you back;
  • the scopes it asks for, which you can untick (at least one stays);
  • the machines it may reach: every machine, including ones added later, or only machines with chosen tags or IDs;
  • the policy that limits it, chosen from your account's policies.

Approving creates one connection (a grant) for that person, client, scope set, machine set and policy. The account's owners and you receive an email about the new connection, with a link to revoke it if it was not you.

Scopes

ScopeAllowsTools (examples)
fleet.readList machines, read files, view logs and job statuslist_hosts, host_facts, read_file, list_dir, job_status, log_read
fleet.execRun commands, open shells and terminals, start jobs and schedules, wake machinesexec, shell_run, job_start, terminal_open, schedule_create
fleet.writeWrite, upload, copy and delete fileswrite_file, delete_path, transfer_start
fleet.adminPort forwarding and reading the audit log; includes the other threetunnel_open, tunnel_http, audit_tail
offline_accessAccepted; refresh tokens are issued in any case

The scope each tool needs is stated in its description on the Tools page. A call without the scope fails with token lacks fleet.….

Tokens

Access token15 minutes, a signed JWT (RS256) for https://mcp.letkite.com/mcp, sent as Authorization: Bearer
Refresh token30 days, rotated on every use; reusing an old one revokes the whole chain
Authorization code2 minutes, single use; a replayed code revokes the tokens issued from it

Taking access back

LetKite re-reads the connection on every tool call, so a disconnected client is refused within seconds, without waiting for its token to expire. A connection whose refresh tokens have all expired for a day is removed by itself, unless a schedule uses it.