npm package: @kya-os/mcp-i-cloudflare
Description: Cloudflare Workers adapter for MCP-I framework
Latest version: 1.8.2

# @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
- ✅ **Audit Logging**: Track all operations with configurable logging
- ✅ **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

```bash
npx @kya-os/create-mcpi-app my-agent --platform cloudflare
cd my-agent
```

### 2. Run setup script

```bash
npm run setup
```

This interactive script will:

- Create KV namespaces
- Update `wrangler.toml` with namespace IDs
- Set up environment variables
- Configure secrets

### 3. Start development

```bash
npm run dev
```

## Architecture

### Why McpAgent + CloudflareRuntime?

The scaffolded code uses two complementary components:

- **`McpAgent`** (from `agents/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.21` or 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`).

### MCPICloudflareServer

The main server class that orchestrates all framework functionality:

```typescript
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:

```typescript
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

```typescript
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

```typescript
function defineConfig(
  config: Partial<CloudflareRuntimeConfig>
): CloudflareRuntimeConfig;
```

Merges user configuration with smart defaults. Only specify what you want to override.

### Constants

```typescript
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 minutes
```

## Consent 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

```typescript
{
  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:

1. **Request Origin** (auto-detected) - Best for local development
2. **MCP_SERVER_URL** env var - Explicit configuration
3. **AgentShield Dashboard** - Fallback for backward compatibility

### Example Consent Flow

```typescript
// 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 succeed
```

## Endpoints

### `/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 information
- `POST /consent/approve` - Handle user approval and create delegation
- `GET /consent/success` - Display success page after approval

**Query Parameters (GET /consent):**

- `tool` - Tool name requiring delegation
- `scopes` - Comma-separated list of required scopes
- `agent_did` - Agent DID identifier
- `session_id` - Session ID for tracking
- `project_id` - AgentShield project ID

**Request Body (POST /consent/approve):**

```json
{
  "tool": "checkout",
  "scopes": ["checkout:execute", "payment:process"],
  "agent_did": "did:key:z6Mk...",
  "session_id": "session_123",
  "project_id": "proj_123",
  "termsAccepted": true,
  "customFields": {
    "email": "user@example.com"
  }
}
```

**Response (POST /consent/approve):**

```json
{
  "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

1. **State Generation**: When initiating an OAuth flow, the framework generates a cryptographically secure random state value (32 bytes).

2. **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.

3. **State Validation**: On OAuth callback, the framework retrieves and validates the state from secure storage before processing the authorization code.

4. **One-Time Use**: State values are deleted immediately after retrieval, preventing replay attacks.

5. **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. Optional token encryption can be enabled by setting `OAUTH_ENCRYPTION_SECRET`:

```bash
# wrangler.toml
[env.production.vars]
OAUTH_ENCRYPTION_SECRET = "your-encryption-secret-here"
```

#### 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

```typescript
// 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:

```typescript
{
  timestamp: string;        // ISO timestamp
  eventType: string;       // Event type identifier (e.g., 'csrf_protection_failed')
  severity?: 'error' | 'warning' | 'info';
  // ... event-specific fields
}
```

#### Example Logs

```javascript
// 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

1. Update `@kya-os/mcp-i-cloudflare` to 1.4.0
2. Replace `index.ts` with new simplified version
3. Replace `mcpi-runtime-config.ts` with new simplified version using `defineConfig()`
4. Remove any custom service implementations (now in package)
5. Run `npm run setup` to configure

## License

MIT