2025-07-12 11:48:38 -05:00

18 KiB

name description
MCP Server PRP Template This template is designed to provide a production-ready Model Context Protocol (MCP) server using the proven patterns from this codebase.

Purpose

Template optimized for AI agents to implement production-ready Model Context Protocol (MCP) servers with GitHub OAuth authentication, database integration, and Cloudflare Workers deployment using the proven patterns from this codebase.

Core Principles

  1. Context is King: Include ALL necessary MCP patterns, authentication flows, and deployment configurations
  2. Validation Loops: Provide executable tests from TypeScript compilation to production deployment
  3. Security First: Build-in authentication, authorization, and SQL injection protection
  4. Production Ready: Include monitoring, error handling, and deployment automation

Goal

Build a production-ready MCP (Model Context Protocol) server with:

  • [SPECIFIC MCP FUNCTIONALITY] - describe the specific tools and resources to implement
  • GitHub OAuth authentication with role-based access control
  • Cloudflare Workers deployment with monitoring
  • [ADDITIONAL FEATURES] - any specific features beyond the base authentication/database

Why

  • Developer Productivity: Enable secure AI assistant access to [SPECIFIC DATA/OPERATIONS]
  • Enterprise Security: GitHub OAuth with granular permission system
  • Scalability: Cloudflare Workers global edge deployment
  • Integration: [HOW THIS FITS WITH EXISTING SYSTEMS]
  • User Value: [SPECIFIC BENEFITS TO END USERS]

What

MCP Server Features

Core MCP Tools:

  • Tools are organized in modular files and registered via src/tools/register-tools.ts
  • Each feature/domain gets its own tool registration file (e.g., database-tools.ts, analytics-tools.ts)
  • [LIST SPECIFIC TOOLS] - e.g., "queryDatabase", "listTables", "executeOperations"
  • User authentication and permission validation happens during tool registration
  • Comprehensive error handling and logging
  • [DOMAIN-SPECIFIC TOOLS] - tools specific to your use case

Authentication & Authorization:

  • GitHub OAuth 2.0 integration with signed cookie approval system
  • Role-based access control (read-only vs privileged users)
  • User context propagation to all MCP tools
  • Secure session management with HMAC-signed cookies

Database Integration:

  • PostgreSQL connection pooling with automatic cleanup
  • SQL injection protection and query validation
  • Read/write operation separation based on user permissions
  • Error sanitization to prevent information leakage

Deployment & Monitoring:

  • Cloudflare Workers with Durable Objects for state management
  • Optional Sentry integration for error tracking and performance monitoring
  • Environment-based configuration (development vs production)
  • Real-time logging and alerting

Success Criteria

  • MCP server passes validation with MCP Inspector
  • GitHub OAuth flow works end-to-end (authorization → callback → MCP access)
  • TypeScript compilation succeeds with no errors
  • Local development server starts and responds correctly
  • Production deployment to Cloudflare Workers succeeds
  • Authentication prevents unauthorized access to sensitive operations
  • Error handling provides user-friendly messages without leaking system details
  • [DOMAIN-SPECIFIC SUCCESS CRITERIA]

All Needed Context

Documentation & References (MUST READ)

# CRITICAL MCP PATTERNS - Read these first
- docfile: PRPs/ai_docs/mcp_patterns.md
  why: Core MCP development patterns, security practices, and error handling

# Critial code examples
- docfile: PRPs/ai_docs/claude_api_usage.md
  why: How to use the Anthropic API to get a response from an LLM

# TOOL REGISTRATION SYSTEM - Understand the modular approach
- file: src/tools/register-tools.ts
  why: Central registry showing how all tools are imported and registered - STUDY this pattern

# EXAMPLE MCP TOOLS - Look here how to create and register new tools
- file: examples/database-tools.ts
  why: Example tools for a Postgres MCP server showing best practices for tool creation and registration

- file: examples/database-tools-sentry.ts
  why: Example tools for the Postgres MCP server but with the Sentry integration for production monitoring

# EXISTING CODEBASE PATTERNS - Study these implementations
- file: src/index.ts
  why: Complete MCP server with authentication, database, and tools - MIRROR this pattern

- file: src/github-handler.ts
  why: OAuth flow implementation - USE this exact pattern for authentication

- file: src/database.ts
  why: Database security, connection pooling, SQL validation - FOLLOW these patterns

- file: wrangler.jsonc
  why: Cloudflare Workers configuration - COPY this pattern for deployment

# OFFICIAL MCP DOCUMENTATION
- url: https://modelcontextprotocol.io/docs/concepts/tools
  why: MCP tool registration and schema definition patterns

- url: https://modelcontextprotocol.io/docs/concepts/resources
  why: MCP resource implementation if needed

# Add n documentation related to the users use case as needed below

Current Codebase Tree (Run tree -I node_modules in project root)

# INSERT ACTUAL TREE OUTPUT HERE
/
├── src/
│   ├── index.ts                 # Main authenticated MCP server ← STUDY THIS
│   ├── index_sentry.ts         # Sentry monitoring version
│   ├── simple-math.ts          # Basic MCP example ← GOOD STARTING POINT
│   ├── github-handler.ts       # OAuth implementation ← USE THIS PATTERN
│   ├── database.ts             # Database utilities ← SECURITY PATTERNS
│   ├── utils.ts                # OAuth helpers
│   ├── workers-oauth-utils.ts  # Cookie security system
│   └── tools/                  # Tool registration system
│       └── register-tools.ts   # Central tool registry ← UNDERSTAND THIS
├── PRPs/
│   ├── templates/prp_mcp_base.md  # This template
│   └── ai_docs/                   # Implementation guides ← READ ALL
├── examples/                   # Example tool implementations
│   ├── database-tools.ts       # Database tools example ← FOLLOW PATTERN
│   └── database-tools-sentry.ts # With Sentry monitoring
├── wrangler.jsonc              # Cloudflare config ← COPY PATTERNS
├── package.json                # Dependencies
└── tsconfig.json               # TypeScript config

Known Gotchas & Critical MCP/Cloudflare Patterns

// CRITICAL: Cloudflare Workers require specific patterns
// 1. ALWAYS implement cleanup for Durable Objects
export class YourMCP extends McpAgent<Env, Record<string, never>, Props> {
  async cleanup(): Promise<void> {
    await closeDb(); // CRITICAL: Close database connections
  }

  async alarm(): Promise<void> {
    await this.cleanup(); // CRITICAL: Handle Durable Object alarms
  }
}

// 2. ALWAYS validate SQL to prevent injection (use existing patterns)
const validation = validateSqlQuery(sql); // from src/database.ts
if (!validation.isValid) {
  return createErrorResponse(validation.error);
}

// 3. ALWAYS check permissions before sensitive operations
const ALLOWED_USERNAMES = new Set(["admin1", "admin2"]);
if (!ALLOWED_USERNAMES.has(this.props.login)) {
  return createErrorResponse("Insufficient permissions");
}

// 4. ALWAYS use withDatabase wrapper for connection management
return await withDatabase(this.env.DATABASE_URL, async (db) => {
  // Database operations here
});

// 5. ALWAYS use Zod for input validation
import { z } from "zod";
const schema = z.object({
  param: z.string().min(1).max(100),
});

// 6. TypeScript compilation requires exact interface matching
interface Env {
  DATABASE_URL: string;
  GITHUB_CLIENT_ID: string;
  GITHUB_CLIENT_SECRET: string;
  OAUTH_KV: KVNamespace;
  // Add your environment variables here
}

Implementation Blueprint

Data Models & Types

Define TypeScript interfaces and Zod schemas for type safety and validation.

// User authentication props (inherited from OAuth)
type Props = {
  login: string; // GitHub username
  name: string; // Display name
  email: string; // Email address
  accessToken: string; // GitHub access token
};

// MCP tool input schemas (customize for your tools)
const YourToolSchema = z.object({
  param1: z.string().min(1, "Parameter cannot be empty"),
  param2: z.number().int().positive().optional(),
  options: z.object({}).optional(),
});

// Environment interface (add your variables)
interface Env {
  DATABASE_URL: string;
  GITHUB_CLIENT_ID: string;
  GITHUB_CLIENT_SECRET: string;
  OAUTH_KV: KVNamespace;
  // YOUR_SPECIFIC_ENV_VAR: string;
}

// Permission levels (customize for your use case)
enum Permission {
  READ = "read",
  WRITE = "write",
  ADMIN = "admin",
}

List of Tasks (Complete in order)

Task 1 - Project Setup:
  COPY wrangler.jsonc to wrangler-[server-name].jsonc:
    - MODIFY name field to "[server-name]"
    - ADD any new environment variables to vars section
    - KEEP existing OAuth and database configuration

  CREATE .dev.vars file (if not exists):
    - ADD GITHUB_CLIENT_ID=your_client_id
    - ADD GITHUB_CLIENT_SECRET=your_client_secret
    - ADD DATABASE_URL=postgresql://...
    - ADD COOKIE_ENCRYPTION_KEY=your_32_byte_key
    - ADD any domain-specific environment variables

Task 2 - GitHub OAuth App:
  CREATE new GitHub OAuth app:
    - SET homepage URL: https://your-worker.workers.dev
    - SET callback URL: https://your-worker.workers.dev/callback
    - COPY client ID and secret to .dev.vars

  OR REUSE existing OAuth app:
    - UPDATE callback URL if using different subdomain
    - VERIFY client ID and secret in environment

Task 3 - MCP Server Implementation:
  CREATE src/[server-name].ts OR MODIFY src/index.ts:
    - COPY class structure from src/index.ts
    - MODIFY server name and version in McpServer constructor
    - CALL registerAllTools(server, env, props) in init() method
    - KEEP authentication and database patterns identical

  CREATE tool modules:
    - CREATE new tool files following examples/database-tools.ts pattern
    - EXPORT registration functions that accept (server, env, props)
    - USE Zod schemas for input validation
    - IMPLEMENT proper error handling with createErrorResponse
    - ADD permission checking during tool registration

  UPDATE tool registry:
    - MODIFY src/tools/register-tools.ts to import your new tools
    - ADD your registration function call in registerAllTools()

Task 4 - Database Integration (if needed):
  USE existing database patterns from src/database.ts:
    - IMPORT withDatabase, validateSqlQuery, isWriteOperation
    - IMPLEMENT database operations with security validation
    - SEPARATE read vs write operations based on user permissions
    - USE formatDatabaseError for user-friendly error messages

Task 5 - Environment Configuration:
  SETUP Cloudflare KV namespace:
    - RUN: wrangler kv namespace create "OAUTH_KV"
    - UPDATE wrangler.jsonc with returned namespace ID

  SET production secrets:
    - RUN: wrangler secret put GITHUB_CLIENT_ID
    - RUN: wrangler secret put GITHUB_CLIENT_SECRET
    - RUN: wrangler secret put DATABASE_URL
    - RUN: wrangler secret put COOKIE_ENCRYPTION_KEY

Task 6 - Local Testing:
  TEST basic functionality:
    - RUN: wrangler dev
    - VERIFY server starts without errors
    - TEST OAuth flow: http://localhost:8792/authorize
    - VERIFY MCP endpoint: http://localhost:8792/mcp

Task 7 - Production Deployment:
  DEPLOY to Cloudflare Workers:
    - RUN: wrangler deploy
    - VERIFY deployment success
    - TEST production OAuth flow
    - VERIFY MCP endpoint accessibility

Per Task Implementation Details

// Task 3 - MCP Server Implementation Pattern
export class YourMCP extends McpAgent<Env, Record<string, never>, Props> {
  server = new McpServer({
    name: "Your MCP Server Name",
    version: "1.0.0",
  });

  // CRITICAL: Always implement cleanup
  async cleanup(): Promise<void> {
    try {
      await closeDb();
      console.log("Database connections closed successfully");
    } catch (error) {
      console.error("Error during database cleanup:", error);
    }
  }

  async alarm(): Promise<void> {
    await this.cleanup();
  }

  async init() {
    // PATTERN: Use centralized tool registration
    registerAllTools(this.server, this.env, this.props);
  }
}

// Task 3 - Tool Module Pattern (e.g., src/tools/your-feature-tools.ts)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { Props } from "../types";
import { z } from "zod";

const PRIVILEGED_USERS = new Set(["admin1", "admin2"]);

export function registerYourFeatureTools(server: McpServer, env: Env, props: Props) {
  // Tool 1: Available to all authenticated users
  server.tool(
    "yourBasicTool",
    "Description of your basic tool",
    YourToolSchema, // Zod validation schema
    async ({ param1, param2, options }) => {
      try {
        // PATTERN: Tool implementation with error handling
        const result = await performOperation(param1, param2, options);

        return {
          content: [
            {
              type: "text",
              text: `**Success**\n\nOperation completed\n\n**Result:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\``,
            },
          ],
        };
      } catch (error) {
        return createErrorResponse(`Operation failed: ${error.message}`);
      }
    },
  );

  // Tool 2: Only for privileged users
  if (PRIVILEGED_USERS.has(props.login)) {
    server.tool(
      "privilegedTool",
      "Administrative tool for privileged users",
      { action: z.string() },
      async ({ action }) => {
        // Implementation
        return {
          content: [
            {
              type: "text",
              text: `Admin action '${action}' executed by ${props.login}`,
            },
          ],
        };
      },
    );
  }
}

// Task 3 - Update Tool Registry (src/tools/register-tools.ts)
import { registerYourFeatureTools } from "./your-feature-tools";

export function registerAllTools(server: McpServer, env: Env, props: Props) {
  // Existing registrations
  registerDatabaseTools(server, env, props);
  
  // Add your new registration
  registerYourFeatureTools(server, env, props);
}

// PATTERN: Export OAuth provider with MCP endpoints
export default new OAuthProvider({
  apiHandlers: {
    "/sse": YourMCP.serveSSE("/sse") as any,
    "/mcp": YourMCP.serve("/mcp") as any,
  },
  authorizeEndpoint: "/authorize",
  clientRegistrationEndpoint: "/register",
  defaultHandler: GitHubHandler as any,
  tokenEndpoint: "/token",
});

Integration Points

CLOUDFLARE_WORKERS:
  - wrangler.jsonc: Update name, environment variables, KV bindings
  - Environment secrets: GitHub OAuth credentials, database URL, encryption key
  - Durable Objects: Configure MCP agent binding for state persistence

GITHUB_OAUTH:
  - GitHub App: Create with callback URL matching your Workers domain
  - Client credentials: Store as Cloudflare Workers secrets
  - Callback URL: Must match exactly: https://your-worker.workers.dev/callback

DATABASE:
  - PostgreSQL connection: Use existing connection pooling patterns
  - Environment variable: DATABASE_URL with full connection string
  - Security: Use validateSqlQuery and isWriteOperation for all SQL

ENVIRONMENT_VARIABLES:
  - Development: .dev.vars file for local testing
  - Production: Cloudflare Workers secrets for deployment
  - Required: GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, DATABASE_URL, COOKIE_ENCRYPTION_KEY

KV_STORAGE:
  - OAuth state: Used by OAuth provider for state management
  - Namespace: Create with `wrangler kv namespace create "OAUTH_KV"`
  - Configuration: Add namespace ID to wrangler.jsonc bindings

Validation Gate

Level 1: TypeScript & Configuration

# CRITICAL: Run these FIRST - fix any errors before proceeding
npm run type-check                 # TypeScript compilation
wrangler types                     # Generate Cloudflare Workers types

# Expected: No TypeScript errors
# If errors: Fix type issues, missing interfaces, import problems

Level 2: Local Development Testing

# Start local development server
wrangler dev

# Test OAuth flow (should redirect to GitHub)
curl -v http://localhost:8792/authorize

# Test MCP endpoint (should return server info)
curl -v http://localhost:8792/mcp

# Expected: Server starts, OAuth redirects to GitHub, MCP responds with server info
# If errors: Check console output, verify environment variables, fix configuration

Level 3: Unit test each feature, function, and file, following existing testing patterns if they are there.

npm run test

Run unit tests with the above command (Vitest) to make sure all functionality is working.

Level 4: Database Integration Testing (if applicable)

# Test database connection
curl -X POST http://localhost:8792/mcp \
  -H "Content-Type: application/json" \
  -d '{"method": "tools/call", "params": {"name": "listTables", "arguments": {}}}'

# Test permission validation
# Test SQL injection protection and other kinds of security if applicable
# Test error handling for database failures

# Expected: Database operations work, permissions enforced, errors handled gracefully, etc.
# If errors: Check DATABASE_URL, connection settings, permission logic

Final Validation Checklist

Core Functionality

  • TypeScript compilation: npm run type-check passes
  • Unit tests pass: npm run test passes
  • Local server starts: wrangler dev runs without errors
  • MCP endpoint responds: curl http://localhost:8792/mcp returns server info
  • OAuth flow works: Authentication redirects and completes successfully

Anti-Patterns to Avoid

MCP-Specific

  • Don't skip input validation with Zod - always validate tool parameters
  • Don't forget to implement cleanup() method for Durable Objects
  • Don't hardcode user permissions - use configurable permission systems

Development Process

  • Don't skip the validation loops - each level catches different issues
  • Don't guess about OAuth configuration - test the full flow
  • Don't deploy without monitoring - implement logging and error tracking
  • Don't ignore TypeScript errors - fix all type issues before deployment