MCP API Key Authentication
MCP Server API Key Authentication
Section titled “MCP Server API Key Authentication”Overview
Section titled “Overview”LMA’s MCP server supports two authentication methods, both deployed automatically when MCP is enabled:
- 3LO (OAuth) via Cognito + BedrockAgentCore Gateway — for interactive clients like Amazon Quick Suite and Claude Desktop
- API Key via REST API Gateway + Lambda authorizer — for headless/programmatic MCP clients, or Quick Suite via bearer token
Users generate personal API keys from the LMA UI (MCP Servers Configuration page). The API key endpoint speaks the full MCP JSON-RPC 2.0 protocol (streamable HTTP), so standard MCP clients can connect directly.
Architecture
Section titled “Architecture”┌─────────────────────────────────────────────────────────────┐│ MCP Clients ││ (Quick Suite, Claude Desktop, curl, custom clients) │└──────────┬──────────────────────────┬───────────────────────┘ │ OAuth (3LO) │ API Key (Bearer / x-api-key) ▼ ▼┌─────────────────────┐ ┌──────────────────────────────┐│ BedrockAgentCore │ │ REST API Gateway ││ Gateway (CUSTOM_JWT)│ │ (REQUEST authorizer) │└─────────┬───────────┘ └──────────┬───────────────────┘ │ │ │ ┌─────────▼──────────────┐ │ │ MCPApiKeyAuthorizer │ │ │ Lambda │ │ │ - Checks Authorization: │ │ │ Bearer or x-api-key │ │ │ - SHA-256 hash → DynamoDB│ │ │ - Returns user context │ │ └─────────┬───────────────┘ │ │ ▼ ▼┌─────────────────────────────────────────────────────────────┐│ MCPServerAnalyticsFunction ││ - BedrockAgentCore path: raw tool input from gateway ││ - API Gateway path: full MCP JSON-RPC 2.0 protocol ││ (initialize, tools/list, tools/call, ping) ││ - UBAC enforced on both paths │└─────────────────────────────────────────────────────────────┘Connecting with a User Identity
Section titled “Connecting with a User Identity”Every tool the MCP server exposes is scoped to the calling user: a request returns that user’s meetings, and the scope widens to all meetings in the account only for a member of the Admin group. MCPServerAnalyticsFunction therefore resolves a caller from each request and refuses the request when it cannot. What it reads from the event:
- API key path —
requestContext.authorizer.userId,usernameandisAdmin, which the REQUEST authorizer put there after resolving the API key. - OAuth (3LO) path —
requestContext.authorizer.claims, taking the user fromsub, the display name fromcognito:username(oremail) and group membership fromcognito:groups.
A request on either path that does not resolve to a user is answered with 403 and a message naming the API key endpoint. A client that receives that response should connect to the MCPServerApiKeyEndpoint REST endpoint with a personal API key, which supplies a caller on every request. See Key Generation Flow and Connecting from Quick Suite.
To confirm on a deployed stack: whether the BedrockAgentCore Gateway (
CUSTOM_JWTauthorizer,GATEWAY_IAM_ROLEcredential provider) includes the caller’s Cognito claims in the event it sends to its Lambda target has not been verified against a live deployment. Where it does not, the OAuth path returns the403described above and the API key endpoint is the route that works for those clients. Worth confirming before a release.
Group membership is read from the cognito:groups claim, which Cognito supplies either as a list or as a comma-separated string; both are accepted, and the comparison is an exact match on a whole group name (a group called Administrators, for instance, is not the Admin group).
Request Flow (API Key Path)
Section titled “Request Flow (API Key Path)”MCP client connects to REST API Gateway endpoint → POST with Authorization: Bearer <key> (or x-api-key: <key>) → REQUEST authorizer Lambda invoked (no IdentitySource — always invoked) → Extracts token from Authorization header (strips "Bearer ") or x-api-key header → SHA-256 hashes the token → Looks up hash in MCPApiKeysTable (DynamoDB) → Returns IAM Allow policy + { userId, username, isAdmin } in context → MCPServerAnalyticsFunction receives API Gateway proxy event → Detects JSON-RPC 2.0 message (jsonrpc + method fields in body) → Routes to MCP protocol handler: - initialize → returns server capabilities + protocolVersion - notifications/initialized → empty 200 acknowledgment - tools/list → returns 6 tool definitions with inputSchema - tools/call → routes to tool implementation, returns MCP result - ping → returns empty result → User context from authorizer enforces UBAC on tool callsKey Generation Flow
Section titled “Key Generation Flow”User clicks "Generate API Key" on MCP Servers Configuration page → AppSync mutation: generateMCPApiKey → MCPApiKeyManagerFunction Lambda → Verifies user doesn't already have a key (queries UserIdIndex GSI) → Generates key: "lma_" + uuid4 → SHA-256 hashes the key → Stores in DynamoDB: { KeyHash (PK), UserId, Username, IsAdmin, CreatedAt, KeyPrefix, Enabled } → Returns plaintext key to user (shown once in modal with copy-to-clipboard)| File | Purpose |
|---|---|
lma-ai-stack/deployment/lma-ai-stack.yaml | All CF resources: MCPApiKeysTable, MCPApiKeyAuthorizerFunction, MCPApiKeyManagerFunction, MCPApiKeyRestApi (REQUEST authorizer, no IdentitySource), AppSync data source + 3 resolvers, MCPServerApiKeyEndpoint output |
lma-main.yaml | MCPServerApiKeyEndpoint output (pass-through from nested stack) |
lma-ai-stack/source/appsync/schema.graphql | MCPApiKey type, GenerateMCPApiKeyOutput type, generateMCPApiKey/revokeMCPApiKey mutations, listMCPApiKeys query |
lma-ai-stack/source/lambda_functions/mcp_api_key_authorizer/index.py | REQUEST authorizer — accepts both Authorization: Bearer and x-api-key headers, hashes token, DynamoDB lookup, returns IAM policy + user context |
lma-ai-stack/source/lambda_functions/mcp_api_key_manager/index.py | AppSync resolver — generate/list/revoke per-user API keys, one key per user enforced |
lma-ai-stack/source/lambda_functions/mcp_analytics/index.py | MCP JSON-RPC 2.0 protocol handler (initialize, tools/list, tools/call, ping) for the API Gateway path; on the BedrockAgentCore path, resolves the caller from the forwarded claims (see Connecting with a User Identity) |
lma-ai-stack/source/ui/src/graphql/mutations.js | generateMCPApiKey, revokeMCPApiKey, listMCPApiKeys GraphQL operations |
lma-ai-stack/source/ui/src/components/mcp-servers-page/MCPApiKeySection.jsx | Cloudscape UI component for key management |
lma-ai-stack/source/ui/src/components/mcp-servers-page/MCPServersPage.jsx | Integrated MCPApiKeySection at top of page |
utilities/test-mcp-api-key.sh | End-to-end test script for the API key endpoint |
docs/mcp-api-key-auth.md | This document |
MCP Protocol Support
Section titled “MCP Protocol Support”The API key endpoint implements the MCP streamable HTTP transport (JSON-RPC 2.0 over HTTP POST). Supported methods:
| Method | Description |
|---|---|
initialize | Returns server info (name: lma-mcp-server) and capabilities |
notifications/initialized | Client acknowledgment, returns empty 200 |
tools/list | Returns all 6 tool definitions with inputSchema |
tools/call | Executes a tool by name with arguments, returns MCP content result |
ping | Health check, returns empty result |
Tools Exposed
Section titled “Tools Exposed”| Tool | Description |
|---|---|
search_lma_meetings | Search transcripts/summaries with natural language |
get_meeting_transcript | Get full transcript (json, text, or srt format) |
get_meeting_summary | Get AI summary with action items and topics |
list_meetings | List meetings with date/participant/status filters |
schedule_meeting | Schedule a future meeting with virtual participant |
start_meeting_now | Start a meeting immediately with virtual participant |
DynamoDB Table Schema
Section titled “DynamoDB Table Schema”Table: ${LMAStackName}-MCPApiKeys
| Attribute | Type | Description |
|---|---|---|
KeyHash | S (PK) | SHA-256 hash of the API key |
UserId | S | Cognito username |
Username | S | Cognito username/email |
IsAdmin | S | "true" or "false" |
KeyPrefix | S | First 12 chars of key (e.g. lma_a1b2c3d4) for UI display |
CreatedAt | S | ISO 8601 timestamp |
Enabled | S | "true" or "false" |
GSI: UserIdIndex — Partition key: UserId, projects all attributes. Used for listing a user’s keys and enforcing one-key-per-user.
API Gateway Authorizer Details
Section titled “API Gateway Authorizer Details”- Type:
REQUEST(not TOKEN) — receives all headers, no IdentitySource filter - TTL: 0 (no caching — since the identity source varies between headers)
- Token extraction order:
authorizationToken(TOKEN mode compat) →headers.x-api-key→headers.Authorization(stripsBearerprefix) - On success: returns IAM Allow policy with
context: { userId, username, isAdmin } - On failure: returns IAM Deny policy
Located on the MCP Servers Configuration page (/configuration/mcp-servers):
- MCP API Key container at the top of the page
- Shows key prefix with masked suffix + creation date if a key exists
- “Generate API Key” button (disabled when a key already exists)
- Generated key shown in a modal with CopyToClipboard and warning it won’t be shown again
- “Revoke” button with confirmation modal
- The Hosted MCP Access tab, including this API key container, is available to every signed-in user; the External MCP Servers tab on the same page is read-only for users outside the Admin group (see MCP Servers › Admin UI)
- The left navigation still lists Configuration › MCP Servers for Admin users only, so a non-admin user needs the direct link (
/#/configuration/mcp-servers) to reach their API key. Surfacing the link for all users is a follow-up.
Security Considerations
Section titled “Security Considerations”- API keys are less secure than 3LO — no token expiration, no refresh rotation
- Keys are SHA-256 hashed at rest — DynamoDB compromise doesn’t expose usable keys
- Users can only manage their own keys (enforced by AppSync identity context)
- API Gateway throttling: 50 burst, 100 sustained requests/sec
- Keys can be revoked immediately by the user
lma_prefix makes keys identifiable if leaked in logs- REQUEST authorizer with no IdentitySource means every request invokes the authorizer Lambda (no bypass via missing headers)
Testing
Section titled “Testing”# Run the end-to-end test./utilities/test-mcp-api-key.sh <your-api-key> [api-gateway-url]
# Tests: initialize (Bearer), initialize (x-api-key), tools/list,# tools/call (list_meetings), ping, bad key rejectionConnecting from Quick Suite
Section titled “Connecting from Quick Suite”Add as a remote MCP server in Quick Suite:
- Transport: SSE/HTTP
- URL: The
MCPServerApiKeyEndpointstack output (e.g.https://xxx.execute-api.us-west-2.amazonaws.com/mcp) - Token: Your LMA API key (e.g.
lma_a1b2c3d4-e5f6-7890-abcd-ef1234567890)
Quick Suite sends Authorization: Bearer <key> on all requests via streamablehttp_client.
To confirm on a deployed stack: this has not been verified against a live Quick Suite tenant, and the Quick Suite setup guide documents only user-authentication (OAuth) for a remote MCP server. If your Quick Suite version has no bearer-token or API-key field, use Quick Desktop, Claude Desktop or another MCP client for the API key route. LMA’s endpoint accepts the key over
Authorization: Bearerorx-api-keyregardless of the client.