Add Delegated Access
Let your MCP tools call GitHub, Google, and other APIs on behalf of authenticated users
Your MCP tools need to call external APIs as the user, not a shared service account. Keycard’s token exchange handles this — you write tools, Keycard manages OAuth flows, credential exchange, and refresh per-user.
How Token Exchange Works
Section titled “How Token Exchange Works”Token exchange lets your MCP server act as both a protected resource (receiving authenticated requests from AI agents) and an API client (calling upstream APIs on behalf of the authenticated user).
flowchart LR
A[User] -->|Token A| B[AI Agent]
B -->|Token A| C[Your MCP Server]
C -->|Exchange A → B| D[Keycard]
D -->|Token B| C
C -->|Token B| E[External API]- User authenticates to your MCP server with a Keycard token (Token A)
- Your MCP server exchanges Token A for an external API token (Token B)
- Your MCP server calls the external API with the exchanged token
- The user’s data is accessed with their own permissions — not a shared service account
Keycard Setup
Section titled “Keycard Setup”These steps are the same regardless of which external API you’re integrating. Complete them before following a provider-specific guide.
-
Add the API from Resource Catalog
In Keycard Console, navigate to Resource Catalog and add the API you want to integrate (e.g., GitHub, Google Calendar, Google Drive). This automatically creates the OAuth provider and API resource.
-
Set up the OAuth App callback
- Go to Zone Settings in Keycard Console and copy the OAuth2 Redirect URL
- In the external provider’s developer console, create or edit your OAuth App
- Set the authorization callback / redirect URI to the Keycard redirect URL
-
Register your MCP server resource
Navigate to Resources → Create Resource:
Field Value Resource Name Your MCP Server Resource Identifier http://localhost:8000/mcpCredential Provider Zone Provider -
Create an Application
Navigate to Applications → Create Application:
Field Value Provided Resource Your MCP Server Dependency The external API resource(s) After creating the application, generate client credentials (Client ID + Client Secret) and save them.
The Grant Pattern
Section titled “The Grant Pattern”Every third-party integration follows the same steps:
- Add from Resource Catalog (or manually create the provider and resource for any OAuth 2.0 API)
- Register your MCP server — set Credential Provider to Zone Provider
- Create an Application — set your MCP server as the Provided Resource and external APIs as Dependencies
- Generate client credentials — Client ID + Client Secret for your application
- Use the grant pattern in code:
@auth_provider.grant("https://api.example.com")async def my_tool(ctx: Context): access_context: AccessContext = await ctx.get_state("keycardai") token = access_context.access("https://api.example.com").access_token # Call API with tokenapp.get("/api/endpoint", authProvider.grant("https://api.example.com"), async (req, res) => { const { accessContext } = req as DelegatedRequest; const token = accessContext.access("https://api.example.com").accessToken; // Call API with token});handler := authProvider.Grant("https://api.example.com")( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ac := mcp.AccessContextFromRequest(r) token, _ := ac.Access("https://api.example.com") // Call API with token.AccessToken }),)This works for any OAuth 2.0 provider — Slack, Linear, Notion, and more.
Provider Guides
Section titled “Provider Guides”Follow a provider-specific guide to see the full implementation:
Production Notes
Section titled “Production Notes”When deploying to production:
- Update resource identifiers in Keycard Console to your production URLs (must use HTTPS)
- Update OAuth redirect URIs in GitHub/Google to match your production Keycard zone
- Set environment variables securely — never commit client secrets to source control
- Token caching and refresh is handled automatically by Keycard
Troubleshooting
Section titled “Troubleshooting”Token Exchange Fails
Section titled “Token Exchange Fails”Symptom: access_context.has_errors() returns True (Python) / accessContext.hasErrors() returns true (TypeScript) / ac.HasErrors() returns true (Go)
- Verify the external API resource is added as a dependency on your Application
- Check that the OAuth provider credentials (in Resource Catalog) are correct
- Ensure the user has completed the OAuth consent flow for the external API
- Confirm your MCP server is reachable at the registered resource identifier URL
Invalid or Expired Token
Section titled “Invalid or Expired Token”Symptom: External API returns 401 Unauthorized
- Token refresh is automatic — if this persists, check provider configuration
- Verify the scopes in Keycard Console match what the API requires
- The user may need to re-authorize if they revoked access
Scope Mismatch
Section titled “Scope Mismatch”Symptom: External API returns 403 Forbidden or missing data
- Ensure scopes in Keycard Console match what the API requires
- For Google: verify the required APIs are enabled in Google Cloud Console
- User may need to disconnect and reconnect to pick up new scopes