Skip to content

Commit 2c413e4

Browse files
Merge pull request #16 from keycardai/feat/jwt-private-key-support
Feat/jwt private key support
2 parents b1eeec1 + facb113 commit 2c413e4

34 files changed

Lines changed: 2147 additions & 720 deletions

File tree

README.md

Lines changed: 167 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -4,223 +4,263 @@ A collection of Python packages for KeyCard services, organized as a uv workspac
44

55
## Quick Start
66

7-
Get up and running with KeyCard's MCP (Model Context Protocol) integration in minutes:
7+
Choose the integration that best fits your MCP setup:
88

9-
### Install the Packages
9+
## Quick Start with keycardai-mcp (Standard MCP)
10+
11+
For standard MCP servers using the official MCP Python SDK:
12+
13+
### Install the Package
1014

1115
```bash
12-
pip install mcp keycardai-mcp
16+
pip install keycardai-mcp
1317
```
1418

15-
or
19+
or
1620

1721
```bash
18-
uv add mcp keycardai-mcp
22+
uv add keycardai-mcp
1923
```
2024

21-
### Create Your First MCP Server
25+
### Get Your KeyCard Zone ID
26+
27+
1. Sign up at [keycard.ai](https://keycard.ai)
28+
2. Navigate to Zone Settings to get your zone ID
29+
3. Configure your preferred identity provider (Google, Microsoft, etc.)
30+
4. Create an MCP resource in your zone
31+
32+
### Add Authentication to Your MCP Server
2233

2334
```python
2435
from mcp.server.fastmcp import FastMCP
36+
from keycardai.mcp.server.auth import AuthProvider
2537

26-
mcp = FastMCP("Hello World")
38+
# Your existing MCP server
39+
mcp = FastMCP("My Secure MCP Server")
2740

2841
@mcp.tool()
29-
def hello_world(name: str) -> str:
30-
return f"Hello, {name}!"
42+
def my_protected_tool(data: str) -> str:
43+
return f"Processed: {data}"
3144

32-
if __name__ == "__main__":
33-
mcp.run(transport="streamable-http")
34-
```
35-
36-
### Run your MCP server
45+
# Add KeyCard authentication
46+
access = AuthProvider(
47+
zone_id="your_zone_id_here",
48+
mcp_server_name="My Secure MCP Server",
49+
)
3750

38-
```bash
39-
python server.py
51+
# Create authenticated app
52+
app = access.app(mcp)
4053
```
4154

42-
For more details, refer to the [mcp](https://github.com/modelcontextprotocol/python-sdk?tab=readme-ov-file#streamable-http-transport) documentation.
43-
44-
### Configure the remote MCP in your AI client, like [Cursor](https://cursor.com/?from=home)
55+
### Run with Authentication
4556

46-
```json
47-
{
48-
"mcpServers": {
49-
"hello-world": {
50-
"url": "http://localhost:8000/mcp"
51-
}
52-
}
53-
}
57+
```bash
58+
pip install uvicorn
59+
uvicorn server:app
5460
```
5561

56-
### Test the remote server with client
57-
58-
<img src="docs/images/cursor_hello_world_agent_call.png" alt="Cursor Hello World Agent Call" width="500">
59-
60-
### Signup to Keycard and get your zone identifier
61-
62-
Refer to [docs](https://docs.keycard.ai/) on how to sign up. Navigate to Zone Settings to obtain the zone ID.
63-
64-
<img src="docs/images/keycard_zone_information.png" alt="Keycard ZoneId Information" width="400">
65-
66-
### Configure Your Preferred Identity Provider
67-
68-
<img src="docs/images/keycard_identity_provider_config.png" alt="Keycard Identity Provider Configuration" width="400">
69-
70-
### Setup MCP resource
71-
72-
<img src="docs/images/create_mcp_resource.png" alt="Create MCP Resource" width="400">
73-
74-
### Add authentication to the MCP server
62+
### Add Delegated Access (Optional)
7563

7664
```python
77-
from mcp.server.fastmcp import FastMCP
78-
79-
from keycardai.mcp.server.auth import AuthProvider
80-
81-
# From the zone setting above
82-
zone_id = "90zqtq5lvtobrmyl3b0i0k2z1q"
65+
import os
66+
from mcp.server.fastmcp import FastMCP, Context
67+
from keycardai.mcp.server.auth import AuthProvider, AccessContext, BasicAuth
8368

69+
# Configure your provider with client credentials
8470
access = AuthProvider(
85-
zone_id = zone_id,
86-
mcp_server_name="Hello World Mcp",
71+
zone_id="your_zone_id",
72+
mcp_server_name="My MCP Server",
73+
auth=BasicAuth(
74+
os.getenv("KEYCARD_CLIENT_ID"),
75+
os.getenv("KEYCARD_CLIENT_SECRET")
76+
)
8777
)
8878

89-
mcp = FastMCP("Minimal MCP")
79+
mcp = FastMCP("My MCP Server")
9080

9181
@mcp.tool()
92-
def hello_world(name: str) -> str:
93-
return f"Hello, {name}!"
82+
@access.grant("https://protected-api")
83+
def protected_tool(ctx: Context, access_context: AccessContext, name: str) -> str:
84+
# Use the access_context to call external APIs on behalf of the user
85+
token = access_context.access("https://protected-api").access_token
86+
# Make authenticated API calls...
87+
return f"Protected data for {name}"
9488

95-
# Create Starlette app to handle authorization flows
9689
app = access.app(mcp)
9790
```
9891

99-
### Run Your Server
100-
101-
The authorization flows require additional handlers to advertise the metadata.
92+
## Quick Start with keycardai-mcp-fastmcp (FastMCP)
10293

103-
This is implemented using the underlying Starlette application. For more information, refer to the official [mcp](https://github.com/modelcontextprotocol/python-sdk?tab=readme-ov-file#streamablehttp-servers) documentation.
94+
For FastMCP servers using the FastMCP framework:
10495

105-
You can use any async server, for example [uvicorn](https://www.uvicorn.org/):
96+
### Install the Package
10697

10798
```bash
108-
uv add uvicorn
99+
pip install keycardai-mcp-fastmcp
109100
```
110101

111102
or
112103

113104
```bash
114-
pip install uvicorn
105+
uv add keycardai-mcp-fastmcp
115106
```
116107

117-
```bash
118-
python -m uvicorn server:app
119-
```
108+
### Get Your KeyCard Zone ID
120109

121-
### Authenticate in client
110+
1. Sign up at [keycard.ai](https://keycard.ai)
111+
2. Navigate to Zone Settings to get your zone ID
112+
3. Configure your preferred identity provider (Google, Microsoft, etc.)
113+
4. Create an MCP resource in your zone
122114

123-
<img src="docs/images/cursor_authenticate.png" alt="Cursor Authentication Prompt" width="500">
115+
### Add Authentication to Your FastMCP Server
124116

117+
```python
118+
from fastmcp import FastMCP, Context
119+
from keycardai.mcp.integrations.fastmcp import AuthProvider
120+
121+
# Configure KeyCard authentication
122+
auth_provider = AuthProvider(
123+
zone_id="your-zone-id", # Get this from keycard.ai
124+
mcp_server_name="My Secure FastMCP Server",
125+
mcp_base_url="http://127.0.0.1:8000/"
126+
)
125127

126-
### 🎉 Your MCP server is now running with KeyCard authentication! 🎉
128+
# Get the RemoteAuthProvider for FastMCP
129+
auth = auth_provider.get_remote_auth_provider()
127130

128-
## Features
131+
# Create authenticated FastMCP server
132+
mcp = FastMCP("My Secure FastMCP Server", auth=auth)
129133

130-
### Delegated Access
134+
@mcp.tool()
135+
def hello_world(name: str) -> str:
136+
return f"Hello, {name}!"
131137

132-
You can use Keycard to allow MCP servers to access other resources on behalf of the user.
138+
if __name__ == "__main__":
139+
mcp.run(transport="streamable-http")
140+
```
133141

134-
It automatically requests user consent and performs necessary secure exchanges to provide granular access to resources.
142+
### Add Delegated Access (Optional)
135143

136-
#### Configure credential provider
144+
```python
145+
from fastmcp import FastMCP, Context
146+
from keycardai.mcp.integrations.fastmcp import AuthProvider, AccessContext
147+
148+
# Configure KeyCard authentication
149+
auth_provider = AuthProvider(
150+
zone_id="your-zone-id",
151+
mcp_server_name="My Secure FastMCP Server",
152+
mcp_base_url="http://127.0.0.1:8000/"
153+
)
137154

138-
Configure a credential provider for your resource, for example Google Workspace.
155+
# Get the RemoteAuthProvider for FastMCP
156+
auth = auth_provider.get_remote_auth_provider()
139157

140-
<img src="docs/images/keycard_credential_provider_config.png" alt="Keycard Credential Provider Configuration" width="400">
158+
# Create authenticated FastMCP server
159+
mcp = FastMCP("My Secure FastMCP Server", auth=auth)
141160

142-
#### Configure protected resource
161+
# Example with token exchange for external API access
162+
@mcp.tool()
163+
@auth_provider.grant("https://api.example.com")
164+
def call_external_api(ctx: Context, query: str) -> str:
165+
# Get access context to check token exchange status
166+
access_context: AccessContext = ctx.get_state("keycardai")
167+
168+
# Check for errors before accessing token
169+
if access_context.has_errors():
170+
return f"Error: Failed to obtain access token - {access_context.get_errors()}"
171+
172+
# Access delegated token through context namespace
173+
token = access_context.access("https://api.example.com").access_token
174+
# Use token to call external API
175+
return f"Results for {query}"
143176

144-
Configure a protected resource, for example the Google Drive API.
177+
if __name__ == "__main__":
178+
mcp.run(transport="streamable-http")
179+
```
145180

146-
<img src="docs/images/keycard_resource_create.png" alt="Keycard Resource Creation" width="400">
181+
### Configure Your AI Client
147182

148-
#### Allow access from MCP to protected resource
183+
Configure the remote MCP in your AI client, like [Cursor](https://cursor.com/?from=home):
149184

150-
To allow the MCP server to make delegated calls to the API, set the dependency on the MCP server for the protected resource.
185+
```json
186+
{
187+
"mcpServers": {
188+
"my-secure-server": {
189+
"url": "http://localhost:8000/mcp"
190+
}
191+
}
192+
}
193+
```
151194

152-
<img src="docs/images/keycard_set_dependency.png" alt="Keycard Set Dependency" width="400">
195+
### 🎉 Your MCP server is now protected with KeyCard authentication! 🎉
153196

154-
#### Give the MCP server identity secret
197+
## Features
155198

156-
In order for the MCP server to securely perform exchanges, it requires an identity secret.
199+
### Delegated Access
157200

158-
<img src="docs/images/keycard_identity_configuration.png" alt="Keycard Resource Identity" width="400">
201+
KeyCard allows MCP servers to access other resources on behalf of users with automatic consent and secure token exchange.
159202

160-
Note: Keep the client_id and client_secret safe. We will use them in the next steps.
203+
#### Setup Protected Resources
161204

162-
#### Add delegation control to tool calls
205+
1. **Configure credential provider** (e.g., Google Workspace)
206+
2. **Configure protected resource** (e.g., Google Drive API)
207+
3. **Set MCP server dependencies** to allow delegated access
208+
4. **Create client secret identity** for secure authentication
163209

164-
Note: For demonstration, we will print a different message when access is granted.
165-
In real use cases, you would use the token to make requests to downstream APIs.
166210

167-
```python
168-
import os
169-
from mcp.server.fastmcp import FastMCP, Context
211+
## Overview
170212

171-
from keycardai.mcp.server.auth import AuthProvider, AccessContext, BasicAuth
213+
This workspace contains multiple Python packages that provide various KeyCard functionality:
172214

173-
# From the zone setting above
174-
zone_id = "90zqtq5lvtobrmyl3b0i0k2z1q"
215+
- **keycardai-oauth**: OAuth 2.0 implementation with support for RFC 8693 (Token Exchange)
216+
- **keycardai-mcp**: Core MCP (Model Context Protocol) integration utilities for standard MCP servers
217+
- **keycardai-mcp-fastmcp**: FastMCP-specific integration package with decorators and middleware
175218

176-
access = AuthProvider(
177-
zone_id = zone_id,
178-
mcp_server_name="Hello World Mcp",
179-
auth=BasicAuth(os.getenv("KEYCARD_CLIENT_ID"), os.getenv("KEYCARD_CLIENT_SECRET"))
180-
)
219+
## Installation
181220

182-
mcp = FastMCP("Minimal MCP")
221+
### For Standard MCP Servers
183222

184-
protected_resource_identifier = "https://protected-api"
223+
If you're using the official MCP Python SDK:
185224

186-
@mcp.tool()
187-
@access.grant(protected_resource_identifier)
188-
def hello_world(ctx: Context, access_context: AccessContext, name: str) -> str:
189-
msg = f"Hello, {name}!"
190-
if access_context.access(protected_resource_identifier).access_token:
191-
msg = f"Hello, {name}! I can see you have extra access"
192-
return msg
193-
194-
# Create Starlette app to handle authorization flows
195-
app = access.app(mcp)
225+
```bash
226+
pip install keycardai-mcp
196227
```
197228

198-
#### Use obtained access to make API calls on behalf of users
229+
or
199230

200-
<img src="docs/images/cursor_delegated_access_example.png" alt="Keycard Set Dependency" width="400">
231+
```bash
232+
uv add keycardai-mcp
233+
```
201234

235+
### For FastMCP Servers
202236

203-
## Overview
237+
If you're using the FastMCP framework:
204238

205-
This workspace contains multiple Python packages that provide various KeyCard functionality:
239+
```bash
240+
pip install keycardai-mcp-fastmcp
241+
```
206242

207-
- **keycardai-oauth**: OAuth 2.0 implementation with support for RFC 8693 (Token Exchange)
208-
- **keycardai-mcp**: Core MCP (Model Context Protocol) integration utilities
209-
- **keycardai-mcp-fastmcp**: FastMCP-specific integration package with decorators and middleware
243+
or
210244

211-
## Installation
245+
```bash
246+
uv add keycardai-mcp-fastmcp
247+
```
212248

213-
Install the SDK packages using pip:
249+
### For OAuth Functionality Only
250+
251+
If you only need OAuth capabilities:
214252

215253
```bash
216-
# Install individual packages as needed
217254
pip install keycardai-oauth
218-
pip install keycardai-mcp
219-
pip install keycardai-mcp-fastmcp
255+
```
220256

221-
# Or install from source
257+
### Install from Source
258+
259+
```bash
222260
git clone git@github.com:keycardai/python-sdk.git
223261
cd python-sdk
262+
263+
# Install specific packages as needed
224264
pip install ./packages/oauth
225265
pip install ./packages/mcp
226266
pip install ./packages/mcp-fastmcp

0 commit comments

Comments
 (0)