MCP / AI
Authentication

MCP Authentication

The MCP server reuses the web app's Cognito user pool and enforces the same approval gate and workspace RBAC. Authentication happens in four layers.

The four layers

  1. Client → Gateway. The client (Claude Code / Desktop) connects via streamable-HTTP to the AgentCore Gateway URL. It discovers OAuth requirements through RFC 9728 Protected Resource Metadata (PRM), then opens the Cognito Hosted UI in a browser for login (Google or email/password). No mcp-remote proxy is required.
  2. Gateway validates the JWT. AgentCore Gateway validates the Cognito access token — signature, expiry, issuer, and client_id — and serves PRM for OAuth discovery.
  3. Interceptor Lambda. A pass-through Lambda forwards the MCP request body and the Authorization header, unchanged, to the AgentCore Runtime.
  4. MCP container. The FastMCP server extracts the JWT from the header and independently re-verifies its RS256 signature against the issuer's /.well-known/jwks.json (fetched and cached for one hour, keyed by kid), and validates exp, iss, and — when LKWIZ_MCP_COGNITO_CLIENT_ID is set — the token's client_id claim. This happens whenever LKWIZ_MCP_ALLOWED_ISSUER is configured, which it is in both dev.tfvars and prod.tfvars; an unverified decode only happens if that setting is left empty, a dev/test-only escape hatch that isn't reachable in a properly configured deployment. The verified sub claim is then used to read the user's PROFILE item from DynamoDB. See services/mcp/src/lkwiz_mcp/auth/middleware.py for the implementation.

Approval & workspace access

  • Only users with approved: true in DynamoDB can call any tool.
  • Workspace-scoped tools check membership only — a shared _check_workspace_access helper confirms the caller has a MEMBER#{user_id} record in the workspace. It does not read or branch on the member's role, so today any workspace member (owner, editor, or viewer) can call any workspace-scoped tool, including mutating ones like create_idea, generate_post, and create_action. This is coarser than the REST API, which in places enforces role-specific checks.
  • Admin-only tools (admin_list_users, admin_approve_user) require the caller's platform role (USER#{id}/PROFILE.role) to be admin — a separate concept from workspace role.

See specs/features/mcp.md ("Authorization (RBAC)") for the full breakdown, including the one exception, and each domain's specs/mcp/<domain>.md / /mcp/<domain> page for tool-by-tool detail.

Cognito app clients

  • lkwiz-mcp-gateway-m2m — a machine-to-machine client (client_credentials flow) with a resource-server scope (mcp/invoke), used by the Gateway to reach the runtime.
  • lkwiz-mcp-oauth — a user client (authorization code flow) for interactive OAuth login, with a callback URL matching Claude Code's OAuth callback.

A pre-token-generation Lambda (shared with the web app) injects email into the access token so the MCP server can identify callers without a /userinfo roundtrip.