@kya-os/mcp-i-cloudflare
v1.13.4
Published
Cloudflare Workers adapter for MCP-I framework
Downloads
5,107
Readme
@kya-os/mcp-i-cloudflare
Cloudflare Workers runtime for MCP-I (Model Context Protocol with Identity). Deploy cryptographically-signed MCP servers to Cloudflare's edge network with full identity support.
Overview
This package provides a Next.js-style architecture for building MCP-I agents on Cloudflare Workers. All framework complexity is hidden within the package, leaving your scaffolded code clean and focused on business logic.
Key Features
- ✅ MCPICloudflareServer: Central server class that handles all framework routing
- ✅ Service Architecture: Delegation, Proof, Admin, and Consent services
- ✅ defineConfig: Smart configuration with sensible defaults
- ✅ Identity Management: DID-based identity with Ed25519 keys
- ✅ Cryptographic Proofs: Sign tool responses with detached proofs
- ✅ Session Management: Stateless sessions with nonce protection
- ✅ KV Storage: Use Workers KV for nonce cache and identity
- ✅ Canonical Audit Delivery: Signed recorder receipts, durable SQLite buffering, bounded retry, and redacted DLQ
- ✅ CSRF Protection: Secure OAuth state validation with server-side storage
- ✅ Security Event Logging: Comprehensive observability for security events
Quick Start
1. Create a new project
npx @kya-os/create-mcpi-app my-agent --platform cloudflare
cd my-agent2. Run setup script
npm run setupThis interactive script will:
- Create KV namespaces
- Update
wrangler.tomlwith namespace IDs - Set up environment variables
- Configure secrets
3. Start development
npm run devArchitecture
Why McpAgent + CloudflareRuntime?
The scaffolded code uses two complementary components:
McpAgent(fromagents/mcp): Handles the MCP protocol transport layer- Provides SSE (Server-Sent Events) and HTTP streaming transports
- Required for Cloudflare Workers compatibility
- Manages Durable Objects for stateful session management
- ⚠️ Version Requirement: Use
agents@^0.2.21or later. Constructor signature changed - now only accepts(state, env)
CloudflareRuntime(from@kya-os/mcp-i-cloudflare): Provides MCP-I features- Identity management (DID generation and storage)
- Cryptographic proof generation
- Delegation verification
- Session management with nonce protection
Together, they provide full MCP + MCP-I functionality on Cloudflare Workers. The MCPICloudflareServer class orchestrates framework-specific routes (health, admin, consent, OAuth), while McpAgent handles the MCP protocol endpoints (/sse, /mcp).
Canonical protocol audit delivery uses a separate AuditProducer Durable
Object so recorder latency and retry alarms do not share authorization, consent,
delegation, or session state. See Durable canonical audit delivery.
MCPICloudflareServer
The main server class that orchestrates all framework functionality:
import { MCPICloudflareServer } from "@kya-os/mcp-i-cloudflare";
import { getRuntimeConfig } from "./mcpi-runtime-config";
export default {
async fetch(
request: Request,
env: CloudflareEnv,
ctx: ExecutionContext
): Promise<Response> {
const server = new MCPICloudflareServer({
env,
config: getRuntimeConfig(env),
});
return server.handleRequest(request, ctx);
},
};defineConfig
Simplified configuration with smart defaults:
import { defineConfig } from "@kya-os/mcp-i-cloudflare";
export function getRuntimeConfig(env: CloudflareEnv) {
return defineConfig({
// Only specify overrides - defaults are handled automatically
vars: {
ENVIRONMENT: env.ENVIRONMENT || "development",
AGENTSHIELD_API_KEY: env.AGENTSHIELD_API_KEY,
},
// Optional: Enable admin endpoints
admin: {
enabled: true,
apiKey: env.ADMIN_API_KEY,
},
});
}Services
The package provides several services that handle framework functionality:
- DelegationService: Token retrieval, verification, and cache management
- ProofService: Proof submission to AgentShield
- AdminService: Admin endpoints for cache management (optional)
- ConsentService: Consent page rendering (Phase 0 placeholder)
All services are automatically initialized by MCPICloudflareServer and hidden from your code.
API Reference
MCPICloudflareServer
class MCPICloudflareServer {
constructor(options: {
env: CloudflareEnv;
config: CloudflareRuntimeConfig;
runtime?: CloudflareRuntime;
});
async handleRequest(
request: Request,
ctx?: ExecutionContext
): Promise<Response>;
async handleScheduled(event: ScheduledEvent): Promise<void>;
// Service accessors (for advanced use cases)
getDelegationService(): DelegationService;
getProofService(): ProofService;
getAdminService(): AdminService | undefined;
getConsentService(): ConsentService;
}defineConfig
function defineConfig(
config: Partial<CloudflareRuntimeConfig>
): CloudflareRuntimeConfig;Merges user configuration with smart defaults. Only specify what you want to override.
Constants
export const DEFAULT_AGENTSHIELD_URL = "https://kya.vouched.id";
export const DEFAULT_SESSION_TTL = 30 * 60 * 1000; // 30 minutes
export const DEFAULT_DELEGATION_TTL = 7 * 24 * 60 * 60; // 7 days
export const DEFAULT_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
export const DEFAULT_VERIFICATION_CACHE_TTL = 60; // 1 minute
export const DEFAULT_SESSION_CACHE_TTL = 300; // 5 minutesConsent Configuration
The consent service automatically fetches configuration from AgentShield API and caches it for 5 minutes. You can customize the consent page appearance and behavior through the AgentShield dashboard.
Consent Config Structure
{
branding?: {
primaryColor?: string; // Hex color (e.g., "#2563eb")
logoUrl?: string; // Logo image URL
companyName?: string; // Company name
theme?: 'light' | 'dark' | 'auto';
};
terms?: {
text?: string; // Terms text
url?: string; // Terms URL
version?: string; // Terms version
required?: boolean; // Require acceptance
};
customFields?: Array<{
name: string;
label: string;
type: 'text' | 'textarea' | 'checkbox' | 'select';
required: boolean;
placeholder?: string;
options?: Array<{ value: string; label: string }>;
}>;
ui?: {
theme?: 'light' | 'dark' | 'auto';
popupEnabled?: boolean;
autoClose?: boolean;
autoCloseDelay?: number;
};
}Consent URL Auto-Detection
The consent service automatically detects the server URL from incoming requests, ensuring consent URLs point to your server-hosted page. Priority:
- Request Origin (auto-detected) - Best for local development
- MCP_SERVER_URL env var - Explicit configuration
- AgentShield Dashboard - Fallback for backward compatibility
Example Consent Flow
// 1. Tool requires delegation
const result = await runtime.processToolCall(
"checkout",
args,
handler,
session
);
// Returns DelegationRequiredError with consentUrl
// 2. User visits consent URL
// GET /consent?tool=checkout&scopes=checkout:execute&agent_did=...&session_id=...&project_id=...
// 3. User approves
// POST /consent/approve with approval data
// 4. Delegation created and token stored
// Token stored in KV: agent:did:delegation and session:id
// 5. Tool call retried with delegation token
// Should succeedEndpoints
/health (GET)
Health check endpoint.
/consent/* (GET/POST)
Consent page endpoints for user authorization flows.
Endpoints:
GET /consent- Render consent page with tool and scope informationPOST /consent/approve- Handle user approval and create delegationGET /consent/success- Display success page after approval
Query Parameters (GET /consent):
tool- Tool name requiring delegationscopes- Comma-separated list of required scopesagent_did- Agent DID identifiersession_id- Session ID for trackingproject_id- AgentShield project ID
Request Body (POST /consent/approve):
{
"tool": "checkout",
"scopes": ["checkout:execute", "payment:process"],
"agent_did": "did:key:z6Mk...",
"session_id": "session_123",
"project_id": "proj_123",
"termsAccepted": true,
"customFields": {
"email": "[email protected]"
}
}Response (POST /consent/approve):
{
"success": true,
"delegation_id": "del_abc123",
"delegation_token": "token_xyz789"
}Features:
- Server-hosted consent pages (HTTP/SSE transports)
- Dashboard-hosted fallback (STDIO transport)
- XSS prevention with HTML escaping and CSP headers
- Customizable branding and terms via AgentShield API
- Custom fields support for additional user data
- Automatic token storage in KV with dual keys
/admin/* (POST)
Admin endpoints for cache management (optional, requires admin.enabled: true).
/oauth/callback (GET)
OAuth callback handler for delegation flow.
Security Features
CSRF Protection for OAuth Flows
The framework includes built-in CSRF (Cross-Site Request Forgery) protection for OAuth authorization flows. This prevents attackers from tricking users into authorizing unintended actions.
How It Works
State Generation: When initiating an OAuth flow, the framework generates a cryptographically secure random state value (32 bytes).
Secure Storage: The state value and associated OAuth parameters (project ID, agent DID, session ID, etc.) are stored securely in KV storage with a 10-minute TTL.
State Validation: On OAuth callback, the framework retrieves and validates the state from secure storage before processing the authorization code.
One-Time Use: State values are deleted immediately after retrieval, preventing replay attacks.
CSRF Detection: If the state is missing, expired, or invalid, the callback is rejected with a clear error message indicating a potential CSRF attack.
Configuration
CSRF protection is automatically enabled when DELEGATION_STORAGE KV namespace is configured.
OAUTH_ENCRYPTION_SECRET is REQUIRED. It was previously optional (token encryption only), but the consent auth-gate fix made every check that depends on it fail closed: CSRF validation, CSRF token minting, and identity-assertion mint/verify all refuse rather than degrade when it is unset. A worker without it will 403 every delegation mint and 500 the credential render — the consent flow does not work at all.
Set it as a secret, not a committed var:
wrangler secret put OAUTH_ENCRYPTION_SECRETBoth surfaces resolve to env.OAUTH_ENCRYPTION_SECRET, so a [vars] entry does satisfy the runtime — but it commits the value to source control, so prefer wrangler secret put (or your platform's secret store). Managed deployments have this provisioned automatically; see docs/security/consent-approve-gate-verification.md.
Backward Compatibility
If OAuthSecurityService is not available (e.g., DELEGATION_STORAGE not configured), the framework falls back to base64-encoded state parameters. This maintains backward compatibility but provides less security. For production deployments, always configure DELEGATION_STORAGE.
Example
// OAuth URL generation (automatic CSRF protection)
const oauthUrl = await consentService.buildOAuthUrl(
projectId,
agentDid,
sessionId,
scopes,
serverUrl,
oauthSecurityService // Automatically created if DELEGATION_STORAGE is configured
);
// OAuth callback (automatic CSRF validation)
const handler = createOAuthCallbackHandler({
agentShieldApiUrl: env.AGENTSHIELD_API_URL,
delegationStorage: env.DELEGATION_STORAGE,
oauthSecurityService, // Automatically created if DELEGATION_STORAGE is configured
});Security Event Logging
All security-critical events are logged with structured data for monitoring and observability. Security events are prefixed with 🔒 SECURITY EVENT: for easy filtering.
Logged Events
- OAuth Provider Errors: Errors returned by OAuth providers
- CSRF Protection: State validation success/failure, expired states, invalid states
- OAuth Code Exchange: Token exchange start, success, and failure
- Delegation Storage: Token storage events (user+agent scoped, legacy, session cache)
- OAuth Identity Linking: User DID linking with OAuth identities
- Storage Errors: Non-fatal storage errors that don't block operations
Log Format
All security events include:
{
timestamp: string; // ISO timestamp
eventType: string; // Event type identifier (e.g., 'csrf_protection_failed')
severity?: 'error' | 'warning' | 'info';
// ... event-specific fields
}Example Logs
// CSRF protection success
[OAuth] 🔒 SECURITY EVENT: State validated successfully: {
projectId: 'project-123',
agentDid: 'did:key:z6Mk...',
sessionId: 'session-123...',
timestamp: '2025-01-21T10:30:00.000Z',
eventType: 'csrf_protection_success'
}
// CSRF attack detected
[OAuth] 🔒 SECURITY EVENT: State validation failed - state not found or expired: {
stateParam: 'invalid-state-value...',
timestamp: '2025-01-21T10:30:00.000Z',
eventType: 'csrf_protection_failed',
reason: 'state_not_found_or_expired'
}
// Delegation token stored
[OAuth] 🔒 SECURITY EVENT: Delegation token stored with user+agent DID: {
key: 'delegation:user:did:key:z6Mk...',
ttl: 604800,
agentDid: 'did:key:z6Mk...',
userDid: 'did:key:z6Mk...',
timestamp: '2025-01-21T10:30:00.000Z',
eventType: 'delegation_token_stored',
storageType: 'user_agent_scoped'
}Monitoring
Security events can be monitored using:
- Cloudflare Workers Logs: View logs in real-time with
wrangler tail - Log Aggregation: Forward logs to external services (Datadog, Splunk, etc.)
- Alerting: Set up alerts for critical security events (CSRF failures, token exchange failures)
Privacy
All security logs redact sensitive information:
- DIDs are truncated (first 20 characters + "...")
- Tokens are never logged in full
- OAuth subjects are hashed or truncated
- PII is redacted before logging
Migration from v1.x
If you're upgrading from v1.x, see the migration guide below.
Breaking Changes
- Architecture: Complete refactor to Next.js-style architecture
- Scaffolded Output: Simplified from ~826 lines to ~100 lines
- Configuration: New
defineConfig()API with smart defaults - Services: All framework logic moved to package services
Migration Steps
- Update
@kya-os/mcp-i-cloudflareto 1.4.0 - Replace
index.tswith new simplified version - Replace
mcpi-runtime-config.tswith new simplified version usingdefineConfig() - Remove any custom service implementations (now in package)
- Run
npm run setupto configure
License
MIT
