Connectors SDK
gaia.connectors is GAIA’s external-integration layer. It manages two
kinds of connectors:
- OAuth (type
oauth_pkce) — user-authorized flows (Google, etc.). Stores refresh tokens in the OS keyring. - MCP server (type
mcp_server) — API-key-based connections to third-party MCP servers (GitHub, Brave Search, etc.). Stores keys in the OS keyring as$keyringreferences.
gaia connectors …), and
Agent UI (Settings → Connectors page).
Catalog
The catalog is populated at import time bygaia.connectors.catalog.
Every connector is a ConnectorSpec:
core, productivity,
dev, and search tiers.
SDK use — OAuth
get_access_token raises AuthRequiredError on four failure modes:
AGENT_NOT_GRANTED with agent_id=None is a distinct case: the call came from
inside an agent turn that carries no identity, so no grant could be checked and
none is assumed. Fix the identity, not the grants — see the warning below.
It also raises ConfigurationError when an agent_id resolves but scopes is
empty. An empty list satisfies the grant check vacuously, so it would hand back
a full-capability token with the ledger never consulted; naming nothing is
refused rather than treated as needing nothing.
It raises ConnectionBusyError when another GAIA operation (in this process
or another) is saving the
connection at that moment. That is transient, not a disconnect: retry shortly,
and don’t prompt the user to reconnect. Over HTTP it is a 503 with
{"error": "connection_busy"} and a Retry-After header. Listing connections
at that moment (list_connections(), get_connection(), or
GET /v1/connections[/{provider}]) still returns the row, with
"error": "busy" and empty scopes. Don’t base a scope decision on that row;
ask again shortly.
Forwarded connections (no re-auth)
When a host app has already authenticated the user (its own OAuth flow), it can FORWARD that connection to GAIA instead of triggering a second consent screen. GAIA persists the forwarded grant and refreshes as the host app’s client — credential forwarding, not a GAIA-run OAuth flow. The host app forwards three things: the OAuth client it was issued under (client_id + client_secret) and the user’s long-lived refresh_token.
REST: POST /v1/connections/{provider}
The same ingestion is exposed over REST by the Agent UI server (which binds
localhost by default and gates remote/tunnel access behind a token).
Mutating routes require the X-Gaia-UI: 1 header.
After a successful POST the agent resolves the connection ambiently — the
triage request carries no credentials;
get_access_token reads the
persisted grant and refreshes transparently.
GAIA refuses to persist tokens to an insecure keyring backend (plaintext /
weak file-backed stores). On such a backend the import fails loudly with an
actionable error pointing at the system credential-store setup. Forwarded
secrets are never written to
~/.gaia/ and never echoed in any response.
If a forward fails partway through saving, GAIA puts back the client and
connection that were there before, so a failed forward never costs the user
the connection they already had.SDK use — MCP server
MCP server connectors are configured once and then provide their API keys via theget_credential dispatcher:
CLI use
Agent-author guide
Declare the connectors your agent needs asREQUIRED_CONNECTORS:
reason in plain language. After
the user grants the scopes, subsequent get_access_token_sync calls
return fresh tokens transparently.
If instead your agent loads MCP servers dynamically at runtime (from
~/.gaia/mcp_servers.json) rather than declaring specific connectors, set
CONSUMES_MCP_SERVERS = True so the Settings → Connectors “Active for”
panel lists it for every mcp_server connector:
Per-agent activation (MCP servers only)
Activations are the second axis of the per-agent authorization model (issue #1005) and apply tomcp_server connectors only:
- Grant (
grants.json) — agent X is allowed to use connector Y’s credentials with scopes S. Gates credential access (get_access_token). Applies to every connector type. - Activation (
activations.json) — agent X currently has MCP connector Y’s tools surfaced in its toolset. Gates MCP tool visibility (MCPClientManager.tools_for_agent).
true/false entries; an unknown pair returns
is_agent_active == False.
One-click activation: activating an agent with no prior grant
auto-creates a grant using the agent’s declared REQUIRED_CONNECTORS
scopes. This is the path Settings → Connectors → “Active for” takes when
the user ticks an agent that has never been granted access:
Which agents appear in the “Active for” panel
For anmcp_server connector the panel lists agents from two sources:
- Agents that declare the connector in
REQUIRED_CONNECTORS(static). - Agents that set
CONSUMES_MCP_SERVERS = True— they load MCP servers dynamically at runtime (from~/.gaia/mcp_servers.json) and gate the tools through this same activation ledger, so they can use any MCP connector once activated.builtin:chatis the canonical example.
use scope. The registry surfaces the flag on
each agent as consumes_mcp_servers. OAuth connectors have no activatable
agents — the panel is hidden for them entirely.
The HTTP router exposes:
mcp_server connectors with 400 Bad Request (after the standard X-Gaia-UI CSRF check).
Every activation change emits the SSE event connector.activation.changed
({connector_id, agent_id, active}) so an open Agent UI Settings tab
refreshes the “Active for” state without a manual reload. The emit is
centralized in api.activate / api.deactivate, so HTTP, SDK, and CLI
callers all notify through one path. Because the CLI runs in a separate
process from the UI server, the server also runs a background watcher on
activations.json that emits the same event (within ~1 s) when an
out-of-process writer changes the ledger.
Disconnecting a connector wipes both grants and activations for that
connector — re-adding the same connector_id must not silently inherit
the previous user’s tool-visibility decisions.
Where things live
Adding a new OAuth provider
- Create
src/gaia/connectors/providers/<name>.pysatisfying theOAuthProviderprotocol (auth/token URLs, client env vars, etc.). - Register it in
src/gaia/connectors/providers/__init__.py:get. - Add a
ConnectorSpecfor it in a module undersrc/gaia/connectors/catalog/(seegoogle.py,microsoft.py) and import that module insrc/gaia/connectors/catalog/__init__.py. - Add unit tests under
tests/unit/connectors/test_providers.py.
Adding a new MCP server connector
Add aConnectorSpec to _ALL_SPECS in
src/gaia/connectors/catalog/mcp_servers.py. Pin the package version:
MY_SERVICE_API_KEY from the keyring as an
environment variable when launching the server process.