Authentication
Every request to /mcp must carry a Bearer token. Today that's a shared static token stored in AWS Secrets Manager. Per-user OAuth via Auth0 is the MVP target.
Header shape
Every HTTP request to the MCP endpoint (GET, POST, DELETE on /mcp) must include:
Authorization: Bearer <YOUR_TOKEN>
Content-Type: application/json
MCP-Protocol-Version: 2025-03-26For a stateful session, the client also echoes the mcp-session-id header on every follow-up request after initialize. See Transport & endpoints for the full session-lifecycle contract.
Where the token lives
The expected token is read from AWS Secrets Manager at server start. The server expects a JSON secret at the ARN in CONFIG_SECRET_ARN with a mcpBearerToken field:
{
"mcpBearerToken": "GENERATED_RANDOM_TOKEN"
}The token is cached in-process; rotating it requires either an SDK push (production) or a server restart (dev). There is no live reload yet.
Token rotation
Rotate by writing a new value to the same secret ARN and restarting the MCP service. ECS deployments restart on every push, so in production the cycle is:
- Write the new value to Secrets Manager (new version).
- Update all clients with the new token.
- Trigger a no-op deploy of the
cortex-mcp-serverservice to pick up the new value. - Old token continues to work until step 3 completes (no overlap window today).
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
HTTP 401 — {"error":"Unauthorized"} | Missing, malformed, or mismatched Authorization header. | Verify the header literal matches Bearer <TOKEN> exactly (case-sensitive scheme, single space). |
HTTP 401 on a working token | Secrets Manager value rotated; server cache stale. | Restart the MCP service or roll the ECS task. |
| Client never connects, no 401 | Request body has no Authorization header at all. | Some MCP client libraries scope per-server headers separately — confirm they ride POST/GET/DELETE alike. |
OAuth (planned, MVP)
Before public MVP, the bearer model is replaced by per-user OAuth via Auth0. The transport contract stays identical — only the token issuance flow changes:
- User authenticates via the operator dashboard (Auth0) and approves the MCP scopes their client needs.
- Operator dashboard mints a per-(user × client) token. Token carries the user's
subclaim; the server resolves it to a tenant + row-level-security context. - Client config is identical — same
Authorization: Bearer <token>header, just a different token per client.
Quotas and inference budgets become per-(user × client) at the same time. The current per-token rate-limit code already partitions by token, so the wiring is plumbing rather than a protocol break.