Skip to content

Commit 328bc8c

Browse files
authored
Merge pull request #120 from guyernest/feat/cargo-pmcp-oauth
feat(cargo-pmcp): add OAuth integration for aws-lambda and pmcp.run deployment targets using AWS Cognito.
2 parents a00778b + 6ab0bb1 commit 328bc8c

File tree

17 files changed

+4251
-79
lines changed

17 files changed

+4251
-79
lines changed

cargo-pmcp/README.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Production-grade MCP server development toolkit.
1717
- **WASM Support**: Automatic WASM compilation for edge deployments
1818
- **Infrastructure as Code**: CDK-based AWS deployment with complete stack management
1919
- **Deployment Management**: Logs, metrics, secrets, rollback, and destroy capabilities
20+
- **OAuth Authentication**: Production-ready OAuth 2.0 with AWS Cognito, supporting Dynamic Client Registration (DCR)
2021

2122
## Installation
2223

@@ -208,6 +209,48 @@ cargo pmcp deploy destroy --target google-cloud-run --clean
208209
- `deploy test --verbose` - Test the deployment
209210
- `deploy outputs --format json` - Show deployment outputs
210211

212+
### OAuth Authentication
213+
214+
Enable OAuth 2.0 authentication for your MCP server with AWS Cognito. MCP clients (like Claude Desktop, ChatGPT, Cursor) automatically discover and use OAuth via the standard OpenID Connect discovery endpoint.
215+
216+
**Initialize with OAuth:**
217+
```bash
218+
# Create deployment with OAuth enabled
219+
cargo pmcp deploy init --target aws-lambda --oauth cognito
220+
221+
# This creates:
222+
# - Cognito User Pool with optional social logins
223+
# - OAuth Proxy Lambda (handles DCR, authorize, token endpoints)
224+
# - Token Validator Lambda Authorizer (stateless JWT validation)
225+
# - ClientRegistrationTable in DynamoDB
226+
```
227+
228+
**OAuth Options:**
229+
- `--oauth cognito` - Use AWS Cognito (recommended for AWS deployments)
230+
- `--oauth oidc` - Use external OIDC provider (future)
231+
- `--oauth shared:<name>` - Use organization's shared OAuth infrastructure
232+
233+
**How It Works:**
234+
1. MCP clients discover OAuth endpoints via `/.well-known/openid-configuration`
235+
2. Clients self-register using Dynamic Client Registration (RFC 7591)
236+
3. Users authenticate via Cognito Hosted UI (supports social logins, MFA)
237+
4. Clients send Bearer tokens on every MCP request
238+
5. API Gateway validates tokens using Lambda Authorizer (stateless JWT)
239+
6. Your MCP server code requires zero OAuth logic
240+
241+
**View Registered Clients:**
242+
```bash
243+
cargo pmcp oauth clients
244+
```
245+
246+
**Test with OAuth:**
247+
```bash
248+
# Opens browser for authentication, then runs tests
249+
cargo pmcp test --server myserver
250+
```
251+
252+
For detailed OAuth architecture and configuration, see [docs/oauth-design.md](docs/oauth-design.md).
253+
211254
## Test Scenarios
212255

213256
Test scenarios are YAML files that define test steps and assertions for your MCP server.
@@ -276,8 +319,8 @@ The typical development workflow:
276319
5. **Generate tests**: `cargo pmcp test --server myserver --generate-scenarios`
277320
6. **Customize tests**: Edit `scenarios/myserver/generated.yaml`
278321
7. **Run tests**: `cargo pmcp test --server myserver`
279-
8. **Deploy**:
280-
- AWS Lambda: `cargo pmcp deploy init --target aws-lambda && cargo pmcp deploy`
322+
8. **Deploy with OAuth**:
323+
- AWS Lambda: `cargo pmcp deploy init --target aws-lambda --oauth cognito && cargo pmcp deploy`
281324
- Google Cloud Run: `cargo pmcp deploy init --target google-cloud-run && cargo pmcp deploy`
282325
- Cloudflare Workers: `cargo pmcp deploy init --target cloudflare-workers && cargo pmcp deploy`
283326
9. **Monitor**: `cargo pmcp deploy logs --tail` and `cargo pmcp deploy metrics`
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# pmcp.run Service Integration Update Required
2+
3+
## Summary
4+
5+
The cargo-pmcp SDK has been updated to generate Lambda-only CDK templates (no API Gateway) for pmcp-run deployments. However, the pmcp.run Step Functions workflow still expects the old template format with `ApiUrl` output.
6+
7+
## Current Error
8+
9+
```
10+
Deployment failed: Failed to extract stack outputs
11+
```
12+
13+
This occurs because the workflow tries to extract `ApiUrl` from CloudFormation outputs, but the Lambda-only template doesn't have an API Gateway.
14+
15+
## cargo-pmcp Changes (Completed)
16+
17+
1. **CDK Template for pmcp-run** now only deploys:
18+
- Lambda function
19+
- IAM execution role (implicit)
20+
- Log group
21+
22+
2. **Outputs from CDK template**:
23+
- `LambdaArn` - The Lambda function ARN
24+
- `LambdaName` - The Lambda function name
25+
- `ApiUrl` - Placeholder: `https://api.pmcp.run/{use-deployment-id}/mcp` (for backward compat)
26+
- `DashboardUrl` - CloudWatch console URL
27+
28+
3. **URL Construction**: cargo-pmcp constructs the MCP endpoint URL from the deployment ID:
29+
```
30+
https://api.pmcp.run/{deployment_id}/mcp
31+
```
32+
33+
## pmcp.run Service Changes Required
34+
35+
### 1. Update Step Functions Workflow
36+
37+
**File**: `amplify/functions/deployment-workflow/state-machine.json`
38+
39+
**Current** (line 320):
40+
```json
41+
"apiUrl": "{% $states.result.Stacks[0].Outputs[OutputKey='ApiUrl'].OutputValue %}"
42+
```
43+
44+
**New**:
45+
```json
46+
"lambdaArn": "{% $states.result.Stacks[0].Outputs[OutputKey='LambdaArn'].OutputValue %}",
47+
"lambdaName": "{% $states.result.Stacks[0].Outputs[OutputKey='LambdaName'].OutputValue %}",
48+
"apiUrl": "{% 'https://api.pmcp.run/' + $deploymentId + '/mcp' %}"
49+
```
50+
51+
### 2. Wire Lambda to Shared API Gateway
52+
53+
After CloudFormation deployment succeeds, the workflow needs to:
54+
55+
1. **Create Lambda integration** on the shared API Gateway:
56+
```typescript
57+
const integration = new apigatewayv2.CfnIntegration(this, 'Integration', {
58+
apiId: SHARED_API_GATEWAY_ID,
59+
integrationType: 'AWS_PROXY',
60+
integrationUri: lambdaArn,
61+
payloadFormatVersion: '2.0',
62+
});
63+
```
64+
65+
2. **Create route** for `/{serverId}/mcp`:
66+
```typescript
67+
new apigatewayv2.CfnRoute(this, 'Route', {
68+
apiId: SHARED_API_GATEWAY_ID,
69+
routeKey: `POST /${serverId}/mcp`,
70+
target: `integrations/${integration.ref}`,
71+
});
72+
```
73+
74+
3. **Grant API Gateway permission** to invoke the Lambda:
75+
```typescript
76+
new lambda.CfnPermission(this, 'ApiGatewayPermission', {
77+
action: 'lambda:InvokeFunction',
78+
functionName: lambdaName,
79+
principal: 'apigateway.amazonaws.com',
80+
sourceArn: `arn:aws:execute-api:${region}:${account}:${SHARED_API_GATEWAY_ID}/*/*`,
81+
});
82+
```
83+
84+
### 3. Update DynamoDB Record
85+
86+
Store `lambdaArn` in the deployment record:
87+
```json
88+
{
89+
"id": "dep_xxxxx",
90+
"projectName": "chess",
91+
"status": "success",
92+
"lambdaArn": "arn:aws:lambda:us-west-2:123456789:function:chess",
93+
"lambdaName": "chess",
94+
"url": "https://api.pmcp.run/dep_xxxxx/mcp"
95+
}
96+
```
97+
98+
## Alternative: Quick Fix for Backward Compatibility
99+
100+
If the service-side changes take time, cargo-pmcp can temporarily generate templates with a per-server API Gateway (the old way) for pmcp-run target. However, this defeats the cost optimization benefits of the shared API Gateway architecture.
101+
102+
## Testing
103+
104+
Once pmcp.run service is updated:
105+
106+
```bash
107+
cd ~/Development/mcp/pmcp/test-chess/chess
108+
rm -rf .pmcp deploy
109+
cargo pmcp deploy init --oauth cognito --target pmcp-run
110+
cargo pmcp deploy --target pmcp-run
111+
```
112+
113+
Expected output:
114+
```
115+
🎉 Deployment successful!
116+
117+
📊 Deployment Details:
118+
Name: chess
119+
ID: dep_xxxxx
120+
121+
🔌 MCP Endpoint:
122+
URL: https://api.pmcp.run/dep_xxxxx/mcp
123+
124+
🏥 Health Check:
125+
URL: https://api.pmcp.run/dep_xxxxx/health
126+
```

0 commit comments

Comments
 (0)