Skip to content

Commit 24f04d3

Browse files
Merge pull request #13 from keycardai/feat/refactor-fastmcp-api
feat(keycardai-mcp-fastmcp): refactor API for the provider
2 parents 479ed3d + 957cc5c commit 24f04d3

File tree

5 files changed

+459
-508
lines changed

5 files changed

+459
-508
lines changed

packages/mcp-fastmcp/README.md

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,73 +10,69 @@ pip install keycardai-mcp-fastmcp
1010

1111
## Quick Start
1212

13-
```python
14-
from fastmcp import FastMCP, Context
15-
from keycardai.mcp.integrations.fastmcp import KeycardAuthProvider, OAuthClientMiddleware, get_access_token_for_resource
13+
Add KeyCard authentication to your existing FastMCP server:
1614

17-
# Create FastMCP server with KeyCard authentication
18-
mcp = FastMCP("My Secure Service")
15+
### Install the Package
1916

20-
# Add KeyCard authentication
21-
auth = KeycardAuthProvider(
22-
zone_url="https://abc1234.keycard.cloud",
23-
mcp_server_name="My MCP Service"
24-
)
25-
mcp.set_auth_provider(auth)
17+
```bash
18+
pip install keycardai-mcp-fastmcp
19+
```
2620

27-
# Add OAuth client middleware for token exchange
28-
oauth_middleware = OAuthClientMiddleware(
29-
zone_url="https://abc1234.keycard.cloud",
30-
client_name="My MCP Service"
31-
)
32-
mcp.add_middleware(oauth_middleware)
21+
### Get Your KeyCard Zone ID
3322

34-
# Use decorator for automatic token exchange
35-
@mcp.tool()
36-
@get_access_token_for_resource("https://www.googleapis.com/calendar/v3")
37-
async def get_calendar_events(ctx: Context, maxResults: int = 10) -> dict:
38-
# ctx.access_token is automatically available with Google Calendar access
39-
access_token = ctx.access_token
40-
# Make API calls with the exchanged token...
41-
return {"events": [...], "totalEvents": 5}
42-
```
23+
1. Sign up at [keycard.ai](https://keycard.ai)
24+
2. Navigate to Zone Settings to get your zone ID
25+
3. Configure your preferred identity provider (Google, Microsoft, etc.)
26+
4. Create an MCP resource in your zone
4327

44-
## 🏗️ Architecture & Features
28+
### Add Authentication to Your FastMCP Server
4529

46-
This integration package provides FastMCP-specific components for KeyCard OAuth:
30+
```python
31+
from fastmcp import FastMCP, Context
32+
from keycardai.mcp.integrations.fastmcp import AuthProvider
4733

48-
### Core Components
34+
# Configure KeyCard authentication (recommended: use zone_id)
35+
auth_provider = AuthProvider(
36+
zone_id="your-zone-id", # Get this from keycard.ai
37+
mcp_server_name="My Secure FastMCP Server",
38+
mcp_server_url="http://127.0.0.1:8000/"
39+
)
4940

50-
| Component | Module | Description |
51-
|-----------|---------|-------------|
52-
| **KeycardAuthProvider** | `provider.py` | **FastMCP Authentication** - Integrates KeyCard zone tokens with FastMCP auth system |
53-
| **OAuthClientMiddleware** | `middleware.py` | **Client Lifecycle** - Manages OAuth client initialization and context injection |
54-
| **Token Exchange Decorators** | `decorators.py` | **Automated Exchange** - Decorators for seamless resource-specific token exchange |
41+
# Get the RemoteAuthProvider for FastMCP
42+
auth = auth_provider.get_remote_auth_provider()
5543

56-
### Authentication Flow
44+
# Create authenticated FastMCP server
45+
mcp = FastMCP("My Secure FastMCP Server", auth=auth)
5746

58-
1. **Token Verification**: `KeycardAuthProvider` validates incoming JWT tokens using KeyCard zone JWKS
59-
2. **Client Management**: `OAuthClientMiddleware` provides OAuth client to tools via FastMCP context
60-
3. **Token Exchange**: `@get_access_token_for_resource()` decorator automates RFC 8693 token exchange
61-
4. **API Access**: Tools receive resource-specific access tokens transparently
47+
@mcp.tool()
48+
def hello_world(name: str) -> str:
49+
return f"Hello, {name}!"
6250

63-
## Development
51+
# Example with token exchange for external API access
52+
@mcp.tool()
53+
@auth_provider.grant("https://api.example.com")
54+
def call_external_api(ctx: Context, query: str) -> str:
55+
# Access delegated token through context namespace
56+
token = ctx.get_state("keycardai").access("https://api.example.com").access_token
57+
# Use token to call external API
58+
return f"Results for {query}"
59+
60+
if __name__ == "__main__":
61+
mcp.run(transport="streamable-http")
62+
```
6463

65-
This package is part of the [KeycardAI Python SDK](../../README.md).
64+
### 🎉 Your FastMCP server is now protected with KeyCard authentication! 🎉
6665

67-
To develop:
66+
## Examples
67+
68+
For complete examples and advanced usage patterns, see our [documentation](https://docs.keycard.ai).
6869

69-
```bash
70-
# From workspace root
71-
uv sync
72-
uv run --package keycardai-mcp-fastmcp pytest
73-
```
7470
## License
7571

76-
MIT License - see [LICENSE](../../LICENSE) file for details.
72+
MIT License - see [LICENSE](https://github.com/keycardai/python-sdk/blob/main/LICENSE) file for details.
7773

7874
## Support
7975

76+
- 📖 [Documentation](https://docs.keycard.ai)
8077
- 🐛 [Issue Tracker](https://github.com/keycardai/python-sdk/issues)
81-
- 💬 [Community Discussions](https://github.com/keycardai/python-sdk/discussions)
8278
- 📧 [Support Email](mailto:support@keycard.ai)
Lines changed: 59 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,87 @@
11
"""FastMCP integration for KeyCard OAuth client.
22
33
This module provides seamless integration between KeyCard's OAuth client
4-
and FastMCP servers, following the sync/async API design standard.
4+
and FastMCP servers, enabling secure authentication and authorization.
55
66
Components:
7-
- KeycardAuthProvider: FastMCP authentication provider using KeyCard zone tokens
8-
- AccessMiddleware: Middleware that manages OAuth client lifecycle and provides grant decorator
9-
- Access token management through grant decorators
7+
- AuthProvider: KeyCard authentication provider with RemoteAuthProvider creation and grant decorator
8+
- AccessContext: Context object for accessing delegated tokens (used in FastMCP Context namespace)
9+
- Auth strategies: BasicAuth, MultiZoneBasicAuth, NoneAuth for different authentication scenarios
1010
11-
Example Usage:
11+
Basic Usage:
1212
13-
# Basic FastMCP server setup with KeyCard authentication
14-
import fastmcp
15-
from keycardai.mcp.integrations.fastmcp import (
16-
KeycardAuthProvider,
17-
AccessMiddleware,
18-
)
19-
20-
# Create MCP server with KeyCard authentication
21-
mcp = fastmcp.MCP("My KeyCard Server")
13+
from fastmcp import FastMCP, Context
14+
from keycardai.mcp.integrations.fastmcp import AuthProvider
2215
23-
# Add authentication provider
24-
auth_provider = KeycardAuthProvider(
25-
zone_url="https://my-keycard-zone.com",
26-
audience="my-mcp-server"
16+
# Create authentication provider
17+
auth_provider = AuthProvider(
18+
zone_id="abc1234",
19+
mcp_server_name="My Server",
20+
mcp_server_url="http://localhost:8000"
2721
)
28-
mcp.add_auth_provider(auth_provider)
2922
30-
# Add access middleware for automatic token management
31-
access = AccessMiddleware(
32-
zone_url="https://my-keycard-zone.com"
33-
)
34-
mcp.add_middleware(access)
23+
# Get the RemoteAuthProvider for FastMCP
24+
auth = auth_provider.get_remote_auth_provider()
25+
mcp = FastMCP("My Server", auth=auth)
3526
36-
# Use the grant decorator for automatic token exchange in tools
37-
@mcp.tool()
38-
@access.grant("https://api.example.com")
39-
async def call_external_api(ctx: Context, query: str) -> str:
40-
'''Call an external API on behalf of the authenticated user.
41-
42-
The decorator automatically exchanges the user's token for one
43-
that can access the specified resource.
44-
'''
45-
# Use the token to call the external API
46-
async with httpx.AsyncClient() as client:
47-
response = await client.get(
48-
"https://api.example.com/search",
49-
params={"q": query},
50-
headers={"Authorization": create_auth_header(ctx.get_state("keycardai").access("https://api.example.com").access_token)}
51-
)
52-
return response.json()
53-
54-
# Simplified setup for common scenarios
27+
# Use grant decorator for token exchange
5528
@mcp.tool()
56-
@access.grant("https://www.googleapis.com/calendar/v3")
57-
async def get_calendar_events(ctx: Context) -> list:
58-
'''Get user's calendar events with automatic token delegation.'''
59-
# Calendar API call with delegated token
60-
from keycardai.oauth.utils.bearer import create_auth_header
61-
token = ctx.get_state("keycardai").access("https://www.googleapis.com/calendar/v3").access_token
62-
headers = {"Authorization": create_auth_header(token)}
63-
# ... implementation details ...
64-
return events
65-
66-
# Run the server
67-
if __name__ == "__main__":
68-
mcp.run()
29+
@auth_provider.grant("https://api.example.com")
30+
def call_external_api(ctx: Context, query: str):
31+
token = ctx.get_state("keycardai").access("https://api.example.com").access_token
32+
# Use token to call external API
33+
return f"Results for {query}"
6934
7035
Advanced Configuration:
7136
72-
# Custom access middleware configuration
73-
access = AccessMiddleware(
74-
zone_url="https://my-keycard-zone.com",
75-
client_name="My Custom Client"
76-
)
37+
# With custom authentication (production)
38+
from keycardai.mcp.integrations.fastmcp import BasicAuth
7739
78-
# Custom authentication with specific requirements
79-
auth_provider = KeycardAuthProvider(
80-
zone_url="https://my-keycard-zone.com",
81-
audience="my-specific-audience",
82-
mcp_server_name="My Custom Server",
83-
resource_server_url="https://my-resource-server.com/"
40+
auth_provider = AuthProvider(
41+
zone_id="abc1234",
42+
mcp_server_name="Production Server",
43+
mcp_server_url="https://my-server.com",
44+
auth=BasicAuth("client_id", "client_secret")
8445
)
8546
86-
# Multiple resource access with single decorator
47+
# Multiple resource access
8748
@mcp.tool()
88-
@access.grant(["https://www.googleapis.com/calendar/v3", "https://www.googleapis.com/drive/v3"])
49+
@auth_provider.grant(["https://www.googleapis.com/calendar/v3", "https://www.googleapis.com/drive/v3"])
8950
async def sync_calendar_to_drive(ctx: Context):
90-
'''Sync calendar events to Google Drive with multiple token exchanges.'''
91-
from keycardai.oauth.utils.bearer import create_auth_header
9251
calendar_token = ctx.get_state("keycardai").access("https://www.googleapis.com/calendar/v3").access_token
9352
drive_token = ctx.get_state("keycardai").access("https://www.googleapis.com/drive/v3").access_token
94-
calendar_headers = {"Authorization": create_auth_header(calendar_token)}
95-
drive_headers = {"Authorization": create_auth_header(drive_token)}
96-
9753
# Use both tokens for cross-service operations
98-
# ... implementation ...
54+
return "Sync completed"
55+
56+
# Multi-zone support
57+
from keycardai.mcp.integrations.fastmcp import MultiZoneBasicAuth
58+
59+
auth_provider = AuthProvider(
60+
zone_url="https://keycard.cloud",
61+
mcp_server_url="https://my-server.com",
62+
auth=MultiZoneBasicAuth({
63+
"tenant1": ("id1", "secret1"),
64+
"tenant2": ("id2", "secret2"),
65+
})
66+
)
9967
"""
10068

101-
from .middleware import AccessMiddleware
102-
from .provider import KeycardAuthProvider
69+
# Re-export commonly used auth strategies for convenience
70+
from keycardai.oauth.http.auth import (
71+
AuthStrategy,
72+
BasicAuth,
73+
MultiZoneBasicAuth,
74+
NoneAuth,
75+
)
76+
77+
from .provider import AccessContext, AuthProvider
10378

10479
__all__ = [
105-
"KeycardAuthProvider",
106-
"AccessMiddleware",
80+
"AuthProvider",
81+
"AccessContext",
82+
# Auth strategies
83+
"AuthStrategy",
84+
"BasicAuth",
85+
"MultiZoneBasicAuth",
86+
"NoneAuth",
10787
]

0 commit comments

Comments
 (0)