-
Notifications
You must be signed in to change notification settings - Fork 0
docs: improve SDK discoverability and newcomer DX #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| --- | ||
| title: "Key Concepts" | ||
| description: "Core concepts and terminology used across the Keycard Python SDK" | ||
| --- | ||
|
|
||
| # Key Concepts | ||
|
|
||
| This page defines the core terms used throughout the Keycard SDK. Each term links to the relevant documentation for deeper details. | ||
|
|
||
| ## Zone | ||
|
|
||
| A **zone** is a Keycard environment that groups your identity providers, MCP resources, and access policies. Every Keycard integration starts with a zone. | ||
|
|
||
| - **zone_id** — The unique identifier for your zone, used in SDK configuration | ||
| - **zone_url** — The full URL of your zone's OAuth endpoints (alternative to zone_id) | ||
|
|
||
| Get your zone ID from [console.keycard.ai](https://console.keycard.ai). You can use either `zone_id` or `zone_url` to configure the SDK — zone_id is simpler, zone_url gives more control. | ||
|
|
||
| ## Delegated Access | ||
|
|
||
| **Delegated access** lets your MCP tools call external APIs (Google Calendar, GitHub, Slack, etc.) on behalf of your authenticated users. Under the hood, it uses [RFC 8693 token exchange](https://datatracker.ietf.org/doc/html/rfc8693) to swap the user's MCP token for a scoped token targeting the external API. | ||
|
|
||
| See the [Delegated Access guide](examples/delegated-access) for how it works and how to set it up. | ||
|
|
||
| ## @grant Decorator | ||
|
|
||
| The **`@grant` decorator** declares which external APIs a tool needs access to. It takes one or more audience URLs and automatically performs token exchange before your function runs. | ||
|
|
||
| ```python | ||
| @auth_provider.grant("https://www.googleapis.com/calendar/v3") | ||
| async def get_events(ctx: Context) -> dict: | ||
| access_context = ctx.get_state("keycardai") | ||
| token = access_context.access("https://www.googleapis.com/calendar/v3").access_token | ||
| # Use token to call Google Calendar API | ||
| ``` | ||
|
|
||
| ## AccessContext | ||
|
|
||
| **AccessContext** is the result of a grant. It contains the exchanged tokens (or errors) for each requested audience. It is **non-throwing by design** — token exchange failures are captured as errors on the context, not raised as exceptions. | ||
|
|
||
| Always check before using tokens: | ||
|
|
||
| ```python | ||
| if access_context.has_errors(): | ||
| return {"error": access_context.get_errors()} | ||
|
|
||
| token = access_context.access("https://api.example.com").access_token | ||
| ``` | ||
|
|
||
| How you get the AccessContext depends on the package: | ||
|
|
||
| | Package | How to get AccessContext | | ||
| |---|---| | ||
| | `keycardai-mcp-fastmcp` | `ctx.get_state("keycardai")` | | ||
| | `keycardai-mcp` | Function parameter: `access_ctx: AccessContext` | | ||
|
|
||
| ## Application Credentials | ||
|
|
||
| **Application credentials** are how your MCP server authenticates with Keycard for token exchange. They're required for [delegated access](#delegated-access). | ||
|
|
||
| Three types are supported: | ||
|
|
||
| | Type | Use Case | | ||
| |---|---| | ||
| | `ClientSecret` | Most common — client ID + secret pair | | ||
| | `WebIdentity` | Private key JWT assertion ([RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523)) | | ||
| | `EKSWorkloadIdentity` | AWS EKS workload identity for Kubernetes deployments | | ||
|
|
||
| Set credentials via environment variables (`KEYCARD_CLIENT_ID`, `KEYCARD_CLIENT_SECRET`) or pass them explicitly in code. | ||
|
|
||
| ## AuthProvider | ||
|
|
||
| **AuthProvider** is the main entry point for adding Keycard authentication to your MCP server. Each package has its own AuthProvider: | ||
|
|
||
| - **FastMCP:** `from keycardai.mcp.integrations.fastmcp import AuthProvider` | ||
| - **Standard MCP:** `from keycardai.mcp.server.auth import AuthProvider` | ||
|
|
||
| The AuthProvider handles OAuth metadata serving, token validation, and (with application credentials) token exchange. | ||
|
|
||
| ## MCP Client | ||
|
|
||
| The **MCP Client** connects *to* authenticated MCP servers. It handles OAuth flows, multi-server connections, and provides integrations for AI agent frameworks (LangChain, OpenAI Agents, CrewAI). | ||
|
|
||
| See the [MCP Client documentation](https://github.com/keycardai/python-sdk/blob/main/packages/mcp/src/keycardai/mcp/client/README.md) for complete usage guide. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| --- | ||
| title: "Delegated Access" | ||
| description: "How token exchange lets your MCP tools call external APIs on behalf of users" | ||
| --- | ||
|
|
||
| # Delegated Access | ||
|
|
||
| Delegated access is Keycard's core value proposition: your MCP tools can call external APIs (Google Calendar, GitHub, Slack, etc.) **on behalf of authenticated users** without ever handling their credentials directly. | ||
|
|
||
| ## How it works | ||
|
|
||
| When a user calls a tool that needs external API access: | ||
|
|
||
| 1. The `@grant` decorator intercepts the call and identifies which APIs are needed | ||
| 2. Keycard exchanges the user's MCP token for scoped tokens targeting each external API ([RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange) | ||
| 3. Exchanged tokens are collected in an `AccessContext` object | ||
| 4. Your function receives the tokens and calls the external APIs | ||
|
|
||
| If any token exchange fails, the error is set on the `AccessContext` — no exceptions are thrown. Always check `.has_errors()` before using tokens. | ||
|
|
||
| ## Setup requirements | ||
|
|
||
| Delegated access requires **application credentials** so your server can authenticate with Keycard for token exchange. Set these as environment variables: | ||
|
|
||
| ```bash | ||
| export KEYCARD_CLIENT_ID="your-client-id" # From console.keycard.ai | ||
| export KEYCARD_CLIENT_SECRET="your-client-secret" # From console.keycard.ai | ||
| ``` | ||
|
|
||
| Three credential types are supported: `ClientSecret` (most common), `WebIdentity` (private key JWT), and `EKSWorkloadIdentity` (AWS EKS). See the package READMEs for details on each. | ||
|
|
||
| ## Get started | ||
|
|
||
| Runnable examples with full setup instructions (including GitHub App configuration): | ||
|
|
||
| - **FastMCP:** [Delegated Access example](https://github.com/keycardai/python-sdk/tree/main/packages/mcp-fastmcp/examples/delegated_access) — GitHub API integration with error handling patterns | ||
| - **Standard MCP:** [Delegated Access example](https://github.com/keycardai/python-sdk/tree/main/packages/mcp/examples/delegated_access) — Same flow using the low-level MCP SDK | ||
|
|
||
| ## Key differences between packages | ||
|
|
||
| | | FastMCP (`keycardai-mcp-fastmcp`) | Standard MCP (`keycardai-mcp`) | | ||
| |---|---|---| | ||
| | **AccessContext** | Retrieved from context: `ctx.get_state("keycardai")` | Injected as function parameter: `access_ctx: AccessContext` | | ||
| | **Grant decorator** | `@auth_provider.grant("https://api.example.com")` | `@auth_provider.grant("https://api.example.com")` | | ||
|
|
||
| ## Reference | ||
|
|
||
| - [keycardai-mcp-fastmcp README — Delegated Access](https://github.com/keycardai/python-sdk/tree/main/packages/mcp-fastmcp#delegated-access) — Full configuration, credential types, error handling | ||
| - [keycardai-mcp README — Delegated Access](https://github.com/keycardai/python-sdk/tree/main/packages/mcp#delegated-access) — Same for standard MCP | ||
| - [Root README — Delegated Access](https://github.com/keycardai/python-sdk#delegated-access) — Side-by-side code comparison |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| --- | ||
| title: "Testing" | ||
| description: "How to test MCP servers that use Keycard authentication" | ||
| --- | ||
|
|
||
| # Testing | ||
|
|
||
| Keycard provides testing utilities so you can write unit tests for your MCP tools without needing a real Keycard zone or OAuth flow. | ||
|
|
||
| ## How it works | ||
|
|
||
| The `mock_access_context()` function creates a fake `AccessContext` that simulates token exchange results. You can configure it to return tokens, errors, or a mix of both — matching any scenario your tool might encounter in production. | ||
|
|
||
| This lets you test: | ||
|
|
||
| - **Default token** — The token from the authenticated user's session | ||
| - **Custom tokens** — Tokens exchanged for specific external APIs | ||
| - **Resource-specific tokens** — Different tokens for different APIs in a multi-grant tool | ||
| - **Error scenarios** — What happens when token exchange fails | ||
|
|
||
| ## Package support | ||
|
|
||
| Testing utilities are currently available for the FastMCP integration: | ||
|
|
||
| ```python | ||
| from keycardai.mcp.integrations.fastmcp.testing import mock_access_context | ||
| ``` | ||
|
|
||
| For standard MCP (`keycardai-mcp`), test by constructing `AccessContext` objects directly. | ||
|
|
||
| ## Get started | ||
|
|
||
| The FastMCP README has a comprehensive testing section with examples for all scenarios: | ||
|
|
||
| - [Testing section](https://github.com/keycardai/python-sdk/tree/main/packages/mcp-fastmcp#testing) — `mock_access_context()` usage, test patterns, and complete examples | ||
|
|
||
| ## Reference | ||
|
|
||
| - [mock_access_context API](sdk/keycardai-mcp-integrations-fastmcp-testing-test_utils) — Auto-generated API reference |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These URLs dont lead to any doc, it ends at the respective folder? Was this intentional?