Gaggle has a read-only MCP server, so a pilot can ask their own AI assistant about their flights. This page has a short guide for pilots, then the details for people who build MCP clients.
https://connect.flygaggle.com/mcp.You can change what the assistant sees, or disconnect it, at any time in Gaggle under Settings → Integrations → Connected apps. There is a longer pilot guide in the help center: Connect Gaggle to your AI assistant.
What the assistant can do:
The server is at https://connect.flygaggle.com/mcp (MCP Streamable HTTP, JSON-RPC over POST). It is stateless: GET and DELETE answer 405, and there are no server-initiated streams.
Gaggle uses OAuth 2.1 with PKCE, the same authorization server as the provider API, with /mcp as a separate protected resource.
/mcp without a token. You get 401 with a WWW-Authenticate challenge that points at the resource metadata.https://connect.flygaggle.com/.well-known/oauth-protected-resource/mcp, which names the authorization server, then its metadata at https://connect.flygaggle.com/.well-known/oauth-authorization-server.S256).resource=https://connect.flygaggle.com/mcp on the authorization and token requests. The token is bound to that resource. Without resource the request defaults to the provider API, which self-registered clients cannot use (invalid_target).profile stats flights tracks. The pilot switches off what the assistant must not see, and the tools read the pilot’s current choice on every call.Registration. Assistants are not registered by hand:
https URL as your client_id that serves your client metadata. The authorization server metadata says client_id_metadata_document_supported: true.POST https://connect.flygaggle.com/oauth/register, for clients without CIMD. It is rate limited per address.token_endpoint_auth_method: none) and use PKCE S256. Redirect URIs are https, or loopback for local tools.The pilot sees a consent screen labelled “AI assistant” with your client name and domain, and a note that Gaggle has not reviewed it. Give your client a clear client_name, logo_uri (https), and client_uri, policy_uri and tos_uri.
Access tokens last 1 hour and refresh tokens rotate, as for the provider API. Assistants get no webhooks.
All tools are read-only (readOnlyHint: true). A result is one text item holding compact JSON. The shapes are the ones of the API reference, and the same plan, history and sharing rules apply. Free text such as site and aircraft names is cleaned (control characters removed, at most 80 characters) and is data, never instructions.
| Tool | Scope | Input | Returns |
|---|---|---|---|
get_profile | profile | none | id, displayName, scopes, plan, historyFrom, flightSharing (as /v1/me) |
get_stats | stats | none | the stats for the pilot’s plan (as /v1/stats) |
list_flights | flights | from?, to?, site?, limit?, cursor? | {flights, nextCursor} |
get_flight | flights | flightId | one flight (as /v1/flights/{id}) |
get_track | tracks | flightId, maxPoints? | a track summary and thinned points |
Inputs.
| Tool | Field | Type |
|---|---|---|
list_flights | from, to | date as YYYY-MM-DD; to is inclusive |
list_flights | site | text, 1 to 100 characters, matches part of the site name (not case sensitive) |
list_flights | limit | integer 1 to 50, default 20 |
list_flights | cursor | the nextCursor of the previous page |
get_flight, get_track | flightId | text, an id from list_flights |
get_track | maxPoints | integer 2 to 1000, default 300 |
list_flights returns flights newest first, without deleted flights, and nextCursor is null on the last page.
get_track output. summary describes the whole recorded track; points is thinned evenly to at most maxPoints and always keeps the first and last point.
{
"flightId": "8Vq2kLw0TbXn4YhZ",
"summary": {
"startTime": "2026-10-18T09:42:10.000Z",
"endTime": "2026-10-18T10:18:31.000Z",
"durationSeconds": 2181,
"minAltitude": 374,
"maxAltitude": 1612,
"bounds": { "minLat": -33.98, "maxLat": -33.93, "minLon": 18.41, "maxLon": 18.47 },
"pointCount": 2182
},
"pointFormat": ["t", "lat", "lon", "alt"],
"points": [["2026-10-18T09:42:10.000Z", -33.9722, 18.4361, 412]]
}Altitudes are in metres, rounded; alt is null for a point without altitude.
Failures inside a tool are tool errors (isError: true) whose text is JSON, {"error": code, ...}:
error | Meaning |
|---|---|
insufficient_scope | The pilot has not switched this on. Has scope and a message the assistant can pass on. |
not_found | No such flight, or it is not visible (deleted, a sharing level the assistant cannot see, outside the history window, or no track) |
rate_limited | Too many calls |
invalid_request | For example, a bad cursor |
bridge_unavailable | The track could not be fetched right now; try again shortly |
internal_error | Something went wrong on Gaggle’s side |
Invalid arguments are protocol errors from the schema check. HTTP-level answers: 401 when the token is not valid or the connection is gone, 403 when access has been disabled, 413 for an oversized request and 429 with Retry-After when you send too many requests.
30 tool calls per minute per pilot and client. initialize and tools/list are not counted as tool calls, but every request to /mcp counts towards the per-pilot request limit described in Rate limits and errors.