The three credential paths
Grain’s MCP endpoint accepts three kinds of credentials, in priority order. Most users only ever use the first one.- OAuth (recommended)
- Raw Auth0 bearer
- Tenant API key
Authorization: Bearer mcpat_…Issued by Grain’s own OAuth 2.1 server after you click Authorize in the browser. Scopes are enforced — the client sees only the tools it was granted.Used by: Claude.ai, Claude Desktop, Claude Code, Cursor, ChatGPT, and any MCP‑compliant client with OAuth support.Workspace: pinned at consent time, immutable for the lifetime of the token.What scopes actually mean
Scopes are the control plane. When a client asks to authorize, it specifies the scopes it needs. You can always approve less than it asks for — the consent screen shows each scope with a plain‑English description.| Scope | Tools unlocked | Rough question it answers |
|---|---|---|
mcp:read | grain.events.list, grain.dimensions.discover, grain.integration.status | ”What’s in this workspace?” |
mcp:query | grain.query, grain.query.count, grain.query.compare, grain.query.digest | ”What happened?” |
mcp:investigate | grain.correlate_event, grain.track.analyze, grain.segment.compare, grain.sessions.cluster | ”Why did it happen?” |
How tokens are stored
This is the part most people care about when they ask “is this safe?”:Grain‑issued tokens (`mcpat_…`, `mcprt_…`)
Grain‑issued tokens (`mcpat_…`, `mcprt_…`)
Stored only as SHA‑256 hashes. The plaintext values leave Grain’s servers exactly once — in the
/oauth/token response to the client. If someone dumps the database, there is no way to reconstruct a valid token from the rows.Upstream Auth0 tokens
Upstream Auth0 tokens
The access token and refresh token Grain captures from Auth0 during consent are AES‑GCM encrypted application‑side before they’re inserted. The encryption key lives in
MCP_SESSION_ENC_KEY (never in the database). Every write is ciphertext; no plaintext Auth0 token is ever persisted or logged.Authorization codes
Authorization codes
PKCE
PKCE
S256 only. No
plain code_challenge_method is accepted, and no client secrets are ever issued. This is a hard requirement of RFC 8252 / OAuth 2.1 and we lean into it.Row‑level security
Row‑level security
The three OAuth tables (
mcp_oauth_clients, mcp_oauth_codes, mcp_oauth_sessions) are RLS‑locked to service_role. The anon key cannot read a row under any circumstances.Managing your connections
Every active MCP client shows up in one place:Settings → Connectors → MCP clients
See which clients are connected, what scopes they have, when they last called a tool, and revoke any of them with one click.
- Client name (e.g.
Claude Desktop,Cursor) — captured during Dynamic Client Registration. - Workspace the session is pinned to.
- Scopes granted.
- Connected — when you authorized it.
- Last used — when the client last successfully called a tool.
- Session ID — a short opaque handle for support.
Revoking a connection
Click Revoke. A confirmation dialog appears; confirm and the session’srevoked_at timestamp fills immediately. The client’s next tool call returns 401 with a WWW-Authenticate header — compliant clients will either prompt you to re‑authorize or mark the server as disconnected.
Workspace isolation
This is the invariant that matters most in a multi‑tenant product: an MCP session is pinned to exactly one workspace, chosen at consent time.- You can’t switch workspaces mid‑session. If you want the agent to look at a different workspace, authorize a second connection.
- Every tool call re‑checks the tenant against the session. A user who is removed from a workspace loses MCP access to it the next time the client calls a tool.
- The OAuth consent flow re‑verifies your membership on the chosen workspace before issuing an authorization code. Stale memberships can’t sneak through.
What gets logged
- Request‑level: method, tool name, caller’s tenant, elapsed ms, budget consumed. No arguments, no result bodies.
- Errors: normalized error message + HTTP status. Stack traces only in dev.
- Audit: every successful tool call updates
last_used_aton the session row, which is what you see in the Connections panel.
Hardening checklist for teams
Use a test workspace first
Use a test workspace first
Pipe a low‑volume / staging workspace into Grain, connect your AI client to that, and watch a few sessions. Promote to production only once the blast radius feels comfortable.
Grant the minimum scope that works
Grant the minimum scope that works
If the agent only ever does briefings,
mcp:read + mcp:query is enough. Add mcp:investigate once the agent demonstrably needs it.Review the Connections panel weekly
Review the Connections panel weekly
Especially after turnover — revoke anything where “Last used” is “never” or older than your tolerance.
Rotate the per‑tenant API key yearly
Rotate the per‑tenant API key yearly
Only relevant if you’re using the legacy
X-API-Key path. OAuth sessions rotate automatically on refresh.