# MCP HTTP Webhook Server Library - Technical Specification

**Version:** 1.0.0  
**Target SDK:** Model Context Protocol TypeScript SDK  
**Transport Method:** HTTP + Webhooks (No SSE)  
**Reference:** [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)

---

## 1. Executive Summary

This library provides an opinionated, production-ready framework for building MCP servers that operate entirely over HTTP with webhook-based resource subscriptions. Unlike the standard MCP SSE transport, this library eliminates persistent connections in favor of stateless HTTP requests and webhook callbacks.

**Key Value Propositions:**
- **Horizontally Scalable:** No connection state = trivial multi-instance deployment
- **Third-Party Integration:** Native webhook support for GitHub, Google Drive, Slack, PostgreSQL, etc.
- **Client Flexibility:** Clients provide their own webhook endpoints
- **Production Ready:** Built-in retry logic, signature verification, and persistent storage

---

## 2. Architecture Overview

### 2.1 Core Components

```
┌─────────────────────────────────────────────────────────────┐
│                     MCP HTTP Server                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │   Protocol   │  │  Webhook     │  │ Subscription │     │
│  │   Handler    │  │  Manager     │  │  Manager     │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
│                                                              │
│  ┌──────────────────────────────────────────────────────┐  │
│  │           Key-Value Store Interface                   │  │
│  │         (Redis / DynamoDB / PostgreSQL)              │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                              │
└─────────────────────────────────────────────────────────────┘
         ▲                    ▲                    ▲
         │                    │                    │
    HTTP Requests      Third-Party           Client
    (Tools/Resources)   Webhooks            Webhooks
```

### 2.2 Data Flow

**Standard Tool/Resource Access:**
```
Client → HTTP POST → MCP Server → Tool/Resource Handler → Response
```

**Subscription Flow:**
```
1. Client → POST /resources/subscribe (with callbackUrl)
2. MCP Server → Generates subscriptionId
3. MCP Server → Creates webhook URL for third-party
4. MCP Server → Calls onSubscribe handler
5. Handler → Registers webhook with third-party service
6. MCP Server → Stores subscription in KV store
7. MCP Server → Returns subscriptionId to client
```

**Notification Flow:**
```
1. Third-Party → POST /webhooks/incoming/{subscriptionId}
2. MCP Server → Loads subscription from KV store
3. MCP Server → Calls onWebhook handler
4. Handler → Parses payload, returns change info
5. MCP Server → HTTP POST to client callbackUrl
6. Client → Receives notification with subscriptionId
```

---

## 3. Installation & Setup

### 3.1 Installation

```bash
npm install mcp-http-webhook
# or
pnpm add mcp-http-webhook
# or
yarn add mcp-http-webhook
```

### 3.2 Peer Dependencies

```json
{
  "@modelcontextprotocol/sdk": "^1.22.0",
  "express": "^4.18.0",
  "ioredis": "^5.3.0"
}
```

---

## 4. Core API Reference

### 4.1 Server Creation

**Function:** `createMCPServer(config: MCPServerConfig): MCPServer`

**Configuration Interface:**

```typescript
interface MCPServerConfig {
  // Server Identity (MCP Protocol)
  name: string;
  version: string;
  
  // HTTP Server Configuration
  port?: number;              // Default: 3000
  host?: string;              // Default: '0.0.0.0'
  basePath?: string;          // Default: '/mcp'
  publicUrl: string;          // Required: e.g., 'https://mcp.example.com'
  
  // Authentication
  authenticate?: (req: Request) => Promise<AuthContext>;
  
  // Core MCP Components
  tools: ToolDefinition[];
  resources: ResourceDefinition[];
  prompts?: PromptDefinition[];
  
  // Storage (Required for subscriptions)
  store: KeyValueStore;
  
  // Webhook Configuration
  webhooks?: WebhookConfig;
  
  // Logging
  logger?: Logger;
  logLevel?: 'debug' | 'info' | 'warn' | 'error';
}
```

**Reference Implementation:**  
See [examples/basic-setup.ts](#)

---

## 5. Tool Definition

Tools follow the standard MCP tool specification with HTTP transport.

**Interface:**

```typescript
interface ToolDefinition<TInput = any, TOutput = any> {
  name: string;
  description: string;
  inputSchema: JSONSchema;
  handler: (input: TInput, context: AuthContext) => Promise<TOutput>;
}
```

**Example:**

```typescript
{
  name: 'create_github_issue',
  description: 'Creates a new issue in a GitHub repository',
  inputSchema: {
    type: 'object',
    properties: {
      owner: { type: 'string', description: 'Repository owner' },
      repo: { type: 'string', description: 'Repository name' },
      title: { type: 'string', description: 'Issue title' },
      body: { type: 'string', description: 'Issue body' }
    },
    required: ['owner', 'repo', 'title']
  },
  handler: async (input, context) => {
    const octokit = new Octokit({ auth: context.githubToken });
    const { data } = await octokit.issues.create(input);
    return { issue: data };
  }
}
```

**HTTP Endpoint:** `POST /mcp/tools/call`

**Request Format:**
```json
{
  "method": "tools/call",
  "params": {
    "name": "create_github_issue",
    "arguments": {
      "owner": "octocat",
      "repo": "hello-world",
      "title": "Bug found"
    }
  }
}
```

**Reference:**  
- [MCP Tool Specification](https://spec.modelcontextprotocol.io/specification/server/tools/)
- [Tool Examples](#)

---

## 6. Resource Definition

Resources represent data that can be read and optionally subscribed to.

### 6.1 Basic Resource

**Interface:**

```typescript
interface ResourceDefinition<TData = any> {
  uri: string;                // URI template, e.g., "github://repo/{owner}/{repo}/issues"
  name: string;
  description: string;
  mimeType?: string;
  
  read: (uri: string, context: AuthContext) => Promise<{
    contents: TData;
    metadata?: Record<string, any>;
  }>;
  
  list?: (context: AuthContext) => Promise<ResourceListItem[]>;
  
  subscription?: ResourceSubscription;
}
```

**Example:**

```typescript
{
  uri: 'github://repo/{owner}/{repo}/issues',
  name: 'GitHub Repository Issues',
  description: 'List of all issues in a repository',
  mimeType: 'application/json',
  
  read: async (uri, context) => {
    const { owner, repo } = parseUriTemplate(uri);
    const octokit = new Octokit({ auth: context.githubToken });
    const { data } = await octokit.issues.listForRepo({ owner, repo });
    return { contents: data };
  },
  
  list: async (context) => {
    const octokit = new Octokit({ auth: context.githubToken });
    const { data } = await octokit.repos.listForAuthenticatedUser();
    return data.map(repo => ({
      uri: `github://repo/${repo.owner.login}/${repo.name}/issues`,
      name: `${repo.full_name} Issues`
    }));
  }
}
```

**HTTP Endpoints:**
- `POST /mcp/resources/list`
- `POST /mcp/resources/read`

**Reference:**  
- [MCP Resource Specification](https://spec.modelcontextprotocol.io/specification/server/resources/)
- [Resource Examples](#)

---

## 7. Webhook-Based Subscriptions

This is the core differentiator of this library.

### 7.1 Subscription Interface

```typescript
interface ResourceSubscription {
  /**
   * Called when a client subscribes to a resource
   * 
   * @param uri - Resource URI being subscribed to
   * @param subscriptionId - Unique ID (generated by library)
   * @param thirdPartyWebhookUrl - URL to give to third-party service
   * @param context - Authentication context
   * @returns Metadata to persist (webhook IDs, etc.)
   */
  onSubscribe: (
    uri: string,
    subscriptionId: string,
    thirdPartyWebhookUrl: string,
    context: AuthContext
  ) => Promise<SubscriptionMetadata>;
  
  /**
   * Called when a client unsubscribes
   * 
   * @param uri - Resource URI
   * @param subscriptionId - Subscription ID
   * @param storedData - Data from onSubscribe
   * @param context - Authentication context
   */
  onUnsubscribe: (
    uri: string,
    subscriptionId: string,
    storedData: SubscriptionMetadata,
    context: AuthContext
  ) => Promise<void>;
  
  /**
   * Called when third-party webhook is received
   * 
   * @param subscriptionId - From webhook URL path
   * @param payload - Webhook payload
   * @param headers - HTTP headers (for signature verification)
   * @returns Change information or null
   */
  onWebhook: (
    subscriptionId: string,
    payload: any,
    headers: Record<string, string>
  ) => Promise<WebhookChangeInfo | null>;
}

interface SubscriptionMetadata {
  thirdPartyWebhookId: string;
  metadata?: Record<string, any>;
}

interface WebhookChangeInfo {
  resourceUri: string;
  changeType: 'created' | 'updated' | 'deleted';
  data?: any;
}
```

### 7.2 Subscription Lifecycle

**Step 1: Client Subscribes**

```
POST /mcp/resources/subscribe
Authorization: Bearer <token>
Content-Type: application/json

{
  "method": "resources/subscribe",
  "params": {
    "uri": "github://repo/octocat/hello-world/issues",
    "callbackUrl": "https://client.example.com/webhooks/mcp",
    "callbackSecret": "client-webhook-secret"
  }
}

Response:
{
  "subscriptionId": "sub_xyz789",
  "status": "active"
}
```

**Internal Flow:**
1. Library generates `subscriptionId`
2. Library creates `thirdPartyWebhookUrl = {publicUrl}/webhooks/incoming/{subscriptionId}`
3. Library calls `onSubscribe(uri, subscriptionId, thirdPartyWebhookUrl, context)`
4. Handler registers webhook with third-party (GitHub, etc.)
5. Library stores subscription data in KV store
6. Library returns `subscriptionId` to client

**Step 2: Third-Party Notifies MCP Server**

```
POST /webhooks/incoming/sub_xyz789
X-GitHub-Event: issues
X-Hub-Signature-256: sha256=...
Content-Type: application/json

{
  "action": "opened",
  "issue": { ... },
  "repository": { ... }
}
```

**Internal Flow:**
1. Library extracts `subscriptionId` from URL path
2. Library loads subscription from KV store
3. Library calls `onWebhook(subscriptionId, payload, headers)`
4. Handler parses payload, returns `WebhookChangeInfo`
5. Library calls client webhook with notification

**Step 3: MCP Server Notifies Client**

```
POST https://client.example.com/webhooks/mcp
X-MCP-Signature: sha256=...
Content-Type: application/json

{
  "subscriptionId": "sub_xyz789",
  "resourceUri": "github://repo/octocat/hello-world/issues/42",
  "changeType": "created",
  "data": {
    "number": 42,
    "title": "New issue",
    "state": "open"
  },
  "timestamp": 1698765432000
}
```

**Step 4: Client Unsubscribes**

```
POST /mcp/resources/unsubscribe
Authorization: Bearer <token>

{
  "method": "resources/unsubscribe",
  "params": {
    "subscriptionId": "sub_xyz789"
  }
}
```

**Internal Flow:**
1. Library loads subscription from KV store
2. Library calls `onUnsubscribe(uri, subscriptionId, storedData, context)`
3. Handler removes webhook from third-party service
4. Library deletes subscription from KV store

**Reference:**  
- [Subscription Example: GitHub](#)
- [Subscription Example: Google Drive](#)
- [Subscription Example: Slack](#)

---

## 8. Key-Value Store Interface

The library requires a persistent store for subscription data.

### 8.1 Store Interface

```typescript
interface KeyValueStore {
  /**
   * Get value by key
   * @returns Value as string, or null if not found
   */
  get(key: string): Promise<string | null>;
  
  /**
   * Set value with optional TTL
   * @param key - Key to set
   * @param value - Value (will be JSON stringified)
   * @param ttl - Time to live in seconds (optional)
   */
  set(key: string, value: string, ttl?: number): Promise<void>;
  
  /**
   * Delete key
   */
  delete(key: string): Promise<void>;
  
  /**
   * Scan keys by pattern (optional, for debugging)
   * @param pattern - Glob pattern (e.g., "subscription:*")
   */
  scan?(pattern: string): Promise<string[]>;
}
```

### 8.2 Storage Schema

The library uses these key patterns:

```typescript
// Primary subscription data
`subscription:{subscriptionId}` → {
  uri: string,
  resourceType: string,
  clientCallbackUrl: string,
  clientCallbackSecret?: string,
  userId: string,
  thirdPartyWebhookId: string,
  metadata?: any,
  createdAt: number
}

// User subscription index
`user:{userId}:subscriptions` → string[]  // Array of subscriptionIds

// Resource subscription index (optional, for bulk operations)
`resource:{resourceUri}:subscriptions` → string[]
```

### 8.3 Redis Implementation

```typescript
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);

const store: KeyValueStore = {
  get: async (key) => await redis.get(key),
  
  set: async (key, value, ttl) => {
    if (ttl) {
      await redis.setex(key, ttl, value);
    } else {
      await redis.set(key, value);
    }
  },
  
  delete: async (key) => {
    await redis.del(key);
  },
  
  scan: async (pattern) => {
    const keys: string[] = [];
    let cursor = '0';
    do {
      const [newCursor, matches] = await redis.scan(
        cursor, 
        'MATCH', 
        pattern,
        'COUNT',
        100
      );
      cursor = newCursor;
      keys.push(...matches);
    } while (cursor !== '0');
    return keys;
  }
};
```

**Reference:**  
- [Store Examples: Redis](#)
- [Store Examples: DynamoDB](#)
- [Store Examples: PostgreSQL](#)
- [Store Examples: In-Memory (Dev Only)](#)

---

## 9. Webhook Configuration

### 9.1 Webhook Config Interface

```typescript
interface WebhookConfig {
  // Incoming webhooks from third-parties
  incomingPath?: string;      // Default: '/webhooks/incoming'
  incomingSecret?: string;    // Shared secret for verification
  verifyIncomingSignature?: (
    payload: any,
    signature: string,
    secret: string
  ) => boolean;
  
  // Outgoing webhooks to clients
  outgoing?: {
    timeout?: number;         // Default: 5000ms
    retries?: number;         // Default: 3
    retryDelay?: number;      // Default: 1000ms (exponential backoff)
    
    // Sign outgoing webhook payloads
    signPayload?: (payload: any, secret: string) => string;
  };
}
```

### 9.2 Signature Verification Examples

**GitHub Signature Verification:**

```typescript
import crypto from 'crypto';

function verifyGitHubSignature(
  payload: any,
  signature: string,
  secret: string
): boolean {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(JSON.stringify(payload));
  const expected = `sha256=${hmac.digest('hex')}`;
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
```

**Slack Signature Verification:**

```typescript
function verifySlackSignature(
  payload: any,
  signature: string,
  timestamp: string,
  secret: string
): boolean {
  const baseString = `v0:${timestamp}:${JSON.stringify(payload)}`;
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(baseString);
  const expected = `v0=${hmac.digest('hex')}`;
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
```

**Reference:**  
- [Webhook Security Guide](#)
- [Third-Party Webhook Formats](#)

---

## 10. Error Handling

### 10.1 Error Types

```typescript
class MCPError extends Error {
  code: number;
  data?: any;
}

class AuthenticationError extends MCPError { code = -32001 }
class ValidationError extends MCPError { code = -32002 }
class ResourceNotFoundError extends MCPError { code = -32003 }
class ToolExecutionError extends MCPError { code = -32004 }
class WebhookError extends MCPError { code = -32005 }
class StorageError extends MCPError { code = -32006 }
```

### 10.2 Retry Logic

The library implements exponential backoff for client webhook calls:

```typescript
// Pseudocode
for (attempt = 0; attempt < maxRetries; attempt++) {
  try {
    response = await callClientWebhook(url, payload);
    if (response.ok) return success;
    
    // Don't retry 4xx client errors
    if (response.status >= 400 && response.status < 500) {
      return failure;
    }
  } catch (error) {
    if (attempt < maxRetries - 1) {
      await sleep(retryDelay * Math.pow(2, attempt));
    }
  }
}

// All retries failed - store in dead letter queue
await storeFailedWebhook(url, payload);
```

**Reference:**  
- [Error Handling Guide](#)
- [Dead Letter Queue Setup](#)

---

## 11. Authentication

### 11.1 Authentication Handler

```typescript
interface AuthContext {
  userId: string;
  [key: string]: any;  // Additional context (tokens, permissions, etc.)
}

type AuthenticateFunction = (req: Request) => Promise<AuthContext>;
```

### 11.2 Example Implementations

**Bearer Token:**

```typescript
authenticate: async (req) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) throw new AuthenticationError('Missing token');
  
  const payload = await verifyJWT(token);
  return {
    userId: payload.sub,
    email: payload.email,
    githubToken: payload.githubToken
  };
}
```

**API Key:**

```typescript
authenticate: async (req) => {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) throw new AuthenticationError('Missing API key');
  
  const user = await db.users.findByApiKey(apiKey);
  if (!user) throw new AuthenticationError('Invalid API key');
  
  return {
    userId: user.id,
    permissions: user.permissions
  };
}
```

**OAuth2:**

```typescript
authenticate: async (req) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  const userInfo = await oauth2Client.verifyToken(token);
  
  return {
    userId: userInfo.sub,
    email: userInfo.email,
    scopes: userInfo.scope.split(' ')
  };
}
```

**Reference:**  
- [Authentication Examples](#)
- [OAuth2 Integration Guide](#)

---

## 12. Complete Example: GitHub MCP Server

**File:** `github-mcp-server.ts`

```typescript
import { createMCPServer } from 'mcp-http-webhook';
import { Octokit } from '@octokit/rest';
import Redis from 'ioredis';
import crypto from 'crypto';

const redis = new Redis(process.env.REDIS_URL);

const server = createMCPServer({
  name: 'github-mcp',
  version: '1.0.0',
  port: 3000,
  publicUrl: process.env.PUBLIC_URL || 'https://mcp.example.com',
  
  store: {
    get: async (key) => await redis.get(key),
    set: async (key, value, ttl) => {
      if (ttl) await redis.setex(key, ttl, value);
      else await redis.set(key, value);
    },
    delete: async (key) => await redis.del(key),
    scan: async (pattern) => {
      const keys: string[] = [];
      let cursor = '0';
      do {
        const [newCursor, matches] = await redis.scan(cursor, 'MATCH', pattern);
        cursor = newCursor;
        keys.push(...matches);
      } while (cursor !== '0');
      return keys;
    }
  },
  
  authenticate: async (req) => {
    const token = req.headers.authorization?.replace('Bearer ', '');
    if (!token) throw new Error('Unauthorized');
    
    const payload = await verifyJWT(token);
    return {
      userId: payload.sub,
      githubToken: payload.githubToken
    };
  },
  
  tools: [
    {
      name: 'create_issue',
      description: 'Create a new GitHub issue',
      inputSchema: {
        type: 'object',
        properties: {
          owner: { type: 'string', description: 'Repository owner' },
          repo: { type: 'string', description: 'Repository name' },
          title: { type: 'string', description: 'Issue title' },
          body: { type: 'string', description: 'Issue description' }
        },
        required: ['owner', 'repo', 'title']
      },
      handler: async (input, context) => {
        const octokit = new Octokit({ auth: context.githubToken });
        const { data } = await octokit.issues.create({
          owner: input.owner,
          repo: input.repo,
          title: input.title,
          body: input.body
        });
        return { issue: data };
      }
    },
    {
      name: 'list_repositories',
      description: 'List all repositories for authenticated user',
      inputSchema: {
        type: 'object',
        properties: {
          type: { 
            type: 'string', 
            enum: ['all', 'owner', 'member'],
            default: 'all'
          }
        }
      },
      handler: async (input, context) => {
        const octokit = new Octokit({ auth: context.githubToken });
        const { data } = await octokit.repos.listForAuthenticatedUser({
          type: input.type || 'all'
        });
        return { repositories: data };
      }
    }
  ],
  
  resources: [
    {
      uri: 'github://repo/{owner}/{repo}/issues',
      name: 'GitHub Repository Issues',
      description: 'All issues in a GitHub repository',
      mimeType: 'application/json',
      
      read: async (uri, context) => {
        const { owner, repo } = parseUriTemplate(uri);
        const octokit = new Octokit({ auth: context.githubToken });
        const { data } = await octokit.issues.listForRepo({
          owner,
          repo,
          state: 'all'
        });
        return { contents: data };
      },
      
      list: async (context) => {
        const octokit = new Octokit({ auth: context.githubToken });
        const { data } = await octokit.repos.listForAuthenticatedUser();
        
        return data.map(repo => ({
          uri: `github://repo/${repo.owner.login}/${repo.name}/issues`,
          name: `${repo.full_name} Issues`,
          description: `Issue tracker for ${repo.full_name}`,
          mimeType: 'application/json'
        }));
      },
      
      subscription: {
        onSubscribe: async (uri, subscriptionId, thirdPartyWebhookUrl, context) => {
          const { owner, repo } = parseUriTemplate(uri);
          const octokit = new Octokit({ auth: context.githubToken });
          
          // Create webhook in GitHub pointing to our server
          const { data: webhook } = await octokit.repos.createWebhook({
            owner,
            repo,
            config: {
              url: thirdPartyWebhookUrl,
              content_type: 'json',
              secret: process.env.GITHUB_WEBHOOK_SECRET
            },
            events: ['issues', 'issue_comment']
          });
          
          console.log(`Created GitHub webhook ${webhook.id} -> ${thirdPartyWebhookUrl}`);
          
          return {
            thirdPartyWebhookId: webhook.id.toString(),
            metadata: { owner, repo }
          };
        },
        
        onUnsubscribe: async (uri, subscriptionId, storedData, context) => {
          const { owner, repo } = storedData.metadata;
          const octokit = new Octokit({ auth: context.githubToken });
          
          await octokit.repos.deleteWebhook({
            owner,
            repo,
            hook_id: parseInt(storedData.thirdPartyWebhookId)
          });
          
          console.log(`Deleted GitHub webhook ${storedData.thirdPartyWebhookId}`);
        },
        
        onWebhook: async (subscriptionId, payload, headers) => {
          // Verify GitHub signature
          const signature = headers['x-hub-signature-256'];
          const body = JSON.stringify(payload);
          const hmac = crypto.createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET!);
          hmac.update(body);
          const expected = `sha256=${hmac.digest('hex')}`;
          
          if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
            throw new Error('Invalid webhook signature');
          }
          
          const event = headers['x-github-event'];
          
          if (event === 'issues') {
            const { action, issue, repository } = payload;
            
            if (['opened', 'edited', 'closed', 'reopened'].includes(action)) {
              return {
                resourceUri: `github://repo/${repository.owner.login}/${repository.name}/issues`,
                changeType: action === 'opened' ? 'created' : 
                           action === 'closed' ? 'deleted' : 'updated',
                data: {
                  issueNumber: issue.number,
                  title: issue.title,
                  state: issue.state,
                  action
                }
              };
            }
          }
          
          if (event === 'issue_comment') {
            const { action, issue, comment, repository } = payload;
            
            if (action === 'created') {
              return {
                resourceUri: `github://repo/${repository.owner.login}/${repository.name}/issues`,
                changeType: 'updated',
                data: {
                  issueNumber: issue.number,
                  commentId: comment.id,
                  commentBody: comment.body,
                  commentAuthor: comment.user.login
                }
              };
            }
          }
          
          return null;
        }
      }
    }
  ],
  
  webhooks: {
    incomingPath: '/webhooks/incoming',
    incomingSecret: process.env.GITHUB_WEBHOOK_SECRET,
    verifyIncomingSignature: (payload, signature, secret) => {
      const hmac = crypto.createHmac('sha256', secret);
      hmac.update(JSON.stringify(payload));
      const expected = `sha256=${hmac.digest('hex')}`;
      return crypto.timingSafeEqual(
        Buffer.from(signature),
        Buffer.from(expected)
      );
    },
    outgoing: {
      timeout: 5000,
      retries: 3,
      retryDelay: 1000,
      signPayload: (payload, secret) => {
        const hmac = crypto.createHmac('sha256', secret);
        hmac.update(JSON.stringify(payload));
        return `sha256=${hmac.digest('hex')}`;
      }
    }
  },
  
  logger: console,
  logLevel: 'info'
});

await server.start();
console.log(`GitHub MCP server running on port 3000`);
console.log(`Webhook endpoint: ${process.env.PUBLIC_URL}/webhooks/incoming/{subscriptionId}`);
```

**Reference:**  
- [Complete Examples Repository](#)
- [GitHub Integration Guide](#)

---

## 13. Deployment Guide

### 13.1 Environment Variables

```bash
# Server Configuration
PORT=3000
PUBLIC_URL=https://mcp.example.com
NODE_ENV=production

# Redis Configuration
REDIS_URL=redis://localhost:6379

# GitHub Configuration
GITHUB_WEBHOOK_SECRET=your-webhook-secret

# JWT Configuration
JWT_SECRET=your-jwt-secret
```

### 13.2 Docker Deployment

**Dockerfile:**

```dockerfile
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --production

COPY . .
RUN npm run build

EXPOSE 3000

CMD ["node", "dist/index.js"]
```

**docker-compose.yml:**

```yaml
version: '3.8'

services:
  mcp-server:
    build: .
    ports:
      - "3000:3000"
    environment:
      - PUBLIC_URL=https://mcp.example.com
      - REDIS_URL=redis://redis:6379
      - GITHUB_WEBHOOK_SECRET=${GITHUB_WEBHOOK_SECRET}
    depends_on:
      - redis
  
  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data

volumes:
  redis-data:
```

### 13.3 Kubernetes Deployment

**deployment.yaml:**

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-http-webhook
  labels:
    app: mcp-http-webhook
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mcp-http-webhook
  template:
    metadata:
      labels:
        app: mcp-http-webhook
    spec:
      containers:
      - name: mcp-server
        image: your-registry/mcp-http-webhook:latest
        ports:
        - containerPort: 3000
        env:
        - name: PORT
          value: "3000"
        - name: PUBLIC_URL
          value: "https://mcp.example.com"
        - name: REDIS_URL
          valueFrom:
            secretKeyRef:
              name: mcp-secrets
              key: redis-url
        - name: GITHUB_WEBHOOK_SECRET
          valueFrom:
            secretKeyRef:
              name: mcp-secrets
              key: github-webhook-secret
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: mcp-secrets
              key: jwt-secret
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: mcp-http-webhook
spec:
  selector:
    app: mcp-http-webhook
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: mcp-http-webhook
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - mcp.example.com
    secretName: mcp-tls
  rules:
  - host: mcp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: mcp-http-webhook
            port:
              number: 80
```

**secrets.yaml:**

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: mcp-secrets
type: Opaque
stringData:
  redis-url: "redis://redis-service:6379"
  github-webhook-secret: "your-webhook-secret"
  jwt-secret: "your-jwt-secret"
```

### 13.4 Scaling Considerations

**Horizontal Scaling:**
- Multiple instances can run simultaneously
- No connection state = trivial load balancing
- All state stored in Redis/external KV store
- Use sticky sessions NOT required

**Load Balancer Configuration:**
```nginx
upstream mcp_backend {
  least_conn;  # Or round_robin
  server mcp-1:3000;
  server mcp-2:3000;
  server mcp-3:3000;
}

server {
  listen 443 ssl;
  server_name mcp.example.com;
  
  location / {
    proxy_pass http://mcp_backend;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
```

**Reference:**  
- [Deployment Examples](#)
- [Kubernetes Best Practices](#)
- [High Availability Guide](#)

---

## 14. Monitoring & Observability

### 14.1 Health Check Endpoints

The library automatically exposes:

```
GET /health       # Basic health check
GET /ready        # Readiness check (includes store connectivity)
GET /metrics      # Prometheus metrics (optional)
```

### 14.2 Logging

**Structured Logging Interface:**

```typescript
interface Logger {
  debug(message: string, meta?: any): void;
  info(message: string, meta?: any): void;
  warn(message: string, meta?: any): void;
  error(message: string, meta?: any): void;
}
```

**Winston Example:**

```typescript
import winston from 'winston';

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
      )
    })
  ]
});

const server = createMCPServer({
  // ...
  logger,
  logLevel: 'info'
});
```

### 14.3 Metrics

**Key Metrics Tracked:**

- `mcp_requests_total` - Total HTTP requests (by method, status)
- `mcp_request_duration_seconds` - Request latency histogram
- `mcp_tool_calls_total` - Tool invocations (by tool name, status)
- `mcp_resource_reads_total` - Resource read operations
- `mcp_subscriptions_active` - Current active subscriptions
- `mcp_webhook_incoming_total` - Third-party webhooks received
- `mcp_webhook_outgoing_total` - Client notifications sent (by status)
- `mcp_webhook_retry_total` - Webhook retry attempts
- `mcp_store_operations_total` - KV store operations (by type)

**Prometheus Integration:**

```typescript
import promClient from 'prom-client';

const register = new promClient.Registry();
promClient.collectDefaultMetrics({ register });

const server = createMCPServer({
  // ...
  metrics: {
    enabled: true,
    registry: register
  }
});

// Metrics available at GET /metrics
```

### 14.4 Distributed Tracing

**OpenTelemetry Integration:**

```typescript
import { trace } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { JaegerExporter } from '@opentelemetry/exporter-jaeger';

const provider = new NodeTracerProvider();
provider.addSpanProcessor(
  new SimpleSpanProcessor(new JaegerExporter())
);
provider.register();

const server = createMCPServer({
  // ...
  tracing: {
    enabled: true,
    serviceName: 'mcp-http-webhook'
  }
});
```

**Reference:**  
- [Monitoring Setup Guide](#)
- [Grafana Dashboard Templates](#)
- [Alert Configuration Examples](#)

---

## 15. Testing

### 15.1 Unit Testing

**Example using Jest:**

```typescript
import { describe, test, expect, beforeEach } from '@jest/globals';
import { createMCPServer } from 'mcp-http-webhook';

describe('MCP Server', () => {
  let server: MCPServer;
  let mockStore: KeyValueStore;
  
  beforeEach(() => {
    mockStore = {
      get: jest.fn(),
      set: jest.fn(),
      delete: jest.fn()
    };
    
    server = createMCPServer({
      name: 'test-server',
      version: '1.0.0',
      publicUrl: 'http://localhost:3000',
      store: mockStore,
      tools: [],
      resources: []
    });
  });
  
  test('should create server instance', () => {
    expect(server).toBeDefined();
  });
  
  test('should handle tool calls', async () => {
    // Test implementation
  });
});
```

### 15.2 Integration Testing

**Testing Subscription Flow:**

```typescript
import request from 'supertest';

describe('Subscription Flow', () => {
  test('should create subscription and handle webhook', async () => {
    // 1. Subscribe to resource
    const subscribeRes = await request(app)
      .post('/mcp/resources/subscribe')
      .set('Authorization', 'Bearer test-token')
      .send({
        method: 'resources/subscribe',
        params: {
          uri: 'github://repo/test/test/issues',
          callbackUrl: 'http://client.test/webhook',
          callbackSecret: 'test-secret'
        }
      });
    
    expect(subscribeRes.status).toBe(200);
    const { subscriptionId } = subscribeRes.body;
    
    // 2. Simulate third-party webhook
    const webhookRes = await request(app)
      .post(`/webhooks/incoming/${subscriptionId}`)
      .set('X-GitHub-Event', 'issues')
      .send({
        action: 'opened',
        issue: { number: 1, title: 'Test' },
        repository: { owner: { login: 'test' }, name: 'test' }
      });
    
    expect(webhookRes.status).toBe(200);
    
    // 3. Verify client webhook was called
    // (Use nock or similar to mock HTTP calls)
  });
});
```

### 15.3 Load Testing

**Using k6:**

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
  stages: [
    { duration: '30s', target: 20 },
    { duration: '1m', target: 50 },
    { duration: '30s', target: 0 }
  ]
};

export default function() {
  const payload = JSON.stringify({
    method: 'tools/call',
    params: {
      name: 'create_issue',
      arguments: {
        owner: 'test',
        repo: 'test',
        title: 'Load test issue'
      }
    }
  });
  
  const params = {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer test-token'
    }
  };
  
  const res = http.post('http://localhost:3000/mcp/tools/call', payload, params);
  
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500
  });
  
  sleep(1);
}
```

**Reference:**  
- [Testing Examples](#)
- [Mock Store Implementation](#)
- [Load Testing Guide](#)

---

## 16. Security Considerations

### 16.1 Webhook Security

**Critical Security Practices:**

1. **Always Verify Signatures**
   - Third-party webhooks: Verify using provider's signature
   - Client webhooks: Sign outgoing payloads

2. **Use HTTPS Only**
   - `publicUrl` must be HTTPS in production
   - Reject non-HTTPS callback URLs

3. **Rate Limiting**
   - Implement rate limits on webhook endpoints
   - Prevent DoS attacks

4. **Timeout Configuration**
   - Set reasonable timeouts for outgoing webhooks
   - Prevent hanging connections

### 16.2 Authentication Best Practices

```typescript
// Example: Multi-factor auth check
authenticate: async (req) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  const payload = await verifyJWT(token);
  
  // Verify IP whitelist for sensitive operations
  if (payload.requiresIpCheck) {
    const clientIp = req.ip;
    if (!isIpWhitelisted(clientIp, payload.userId)) {
      throw new AuthenticationError('IP not whitelisted');
    }
  }
  
  // Check for revoked tokens
  const isRevoked = await redis.get(`revoked:${payload.jti}`);
  if (isRevoked) {
    throw new AuthenticationError('Token revoked');
  }
  
  return {
    userId: payload.sub,
    permissions: payload.permissions
  };
}
```

### 16.3 Input Validation

The library automatically validates:
- Tool inputs against `inputSchema`
- Resource URIs against URI templates
- Webhook payloads (basic structure)

**Additional Validation:**

```typescript
tools: [{
  name: 'create_issue',
  inputSchema: {
    type: 'object',
    properties: {
      title: { 
        type: 'string', 
        minLength: 1, 
        maxLength: 256 
      },
      body: { 
        type: 'string', 
        maxLength: 65536 
      }
    },
    required: ['title']
  },
  handler: async (input, context) => {
    // Additional business logic validation
    if (containsSpam(input.title)) {
      throw new ValidationError('Content contains spam');
    }
    
    // Proceed with tool execution
  }
}]
```

### 16.4 Secret Management

**DO NOT hardcode secrets:**

```typescript
// ❌ BAD
const secret = 'my-secret-key';

// ✅ GOOD
const secret = process.env.GITHUB_WEBHOOK_SECRET;
if (!secret) {
  throw new Error('GITHUB_WEBHOOK_SECRET not configured');
}
```

**Use Secret Management Services:**
- AWS Secrets Manager
- HashiCorp Vault
- Kubernetes Secrets
- Azure Key Vault

**Reference:**  
- [Security Checklist](#)
- [Secret Management Guide](#)
- [Penetration Testing Guide](#)

---

## 17. Advanced Features

### 17.1 Batch Operations

```typescript
interface BatchConfig {
  maxBatchSize?: number;
  batchTimeout?: number;
}

const server = createMCPServer({
  // ...
  batch: {
    maxBatchSize: 100,
    batchTimeout: 1000
  }
});

// Clients can send multiple requests in one HTTP call
POST /mcp/batch
{
  "requests": [
    { "method": "tools/call", "params": {...} },
    { "method": "resources/read", "params": {...} }
  ]
}
```

### 17.2 Caching Layer

```typescript
interface CacheConfig {
  enabled: boolean;
  ttl?: number;  // Default TTL in seconds
  keyPrefix?: string;
}

const server = createMCPServer({
  // ...
  cache: {
    enabled: true,
    ttl: 300,  // 5 minutes
    keyPrefix: 'mcp:cache:'
  }
});

// Resources can specify custom cache behavior
resources: [{
  uri: 'github://repo/{owner}/{repo}/issues',
  cache: {
    enabled: true,
    ttl: 600,  // 10 minutes
    key: (uri) => `issues:${uri}`
  },
  read: async (uri, context) => {
    // Will be cached automatically
  }
}]
```

### 17.3 Webhook Dead Letter Queue

```typescript
interface DeadLetterQueueConfig {
  enabled: boolean;
  store: KeyValueStore;
  retention?: number;  // Days to retain failed webhooks
  maxRetries?: number;
}

const server = createMCPServer({
  // ...
  webhooks: {
    deadLetterQueue: {
      enabled: true,
      store: dlqStore,
      retention: 7,
      maxRetries: 5
    }
  }
});

// Failed webhooks stored as:
// dlq:{timestamp}:{subscriptionId} -> { url, payload, attempts, lastError }
```

### 17.4 Resource Pagination

```typescript
resources: [{
  uri: 'github://repo/{owner}/{repo}/issues',
  read: async (uri, context, options) => {
    const { page = 1, limit = 50 } = options?.pagination || {};
    
    const octokit = new Octokit({ auth: context.githubToken });
    const { data } = await octokit.issues.listForRepo({
      owner,
      repo,
      page,
      per_page: limit
    });
    
    return {
      contents: data,
      pagination: {
        page,
        limit,
        hasMore: data.length === limit,
        nextPage: data.length === limit ? page + 1 : null
      }
    };
  }
}]

// Client request:
POST /mcp/resources/read
{
  "method": "resources/read",
  "params": {
    "uri": "github://repo/test/test/issues",
    "pagination": {
      "page": 1,
      "limit": 50
    }
  }
}
```

### 17.5 Middleware Support

```typescript
interface Middleware {
  (req: Request, res: Response, next: NextFunction): void | Promise<void>;
}

const server = createMCPServer({
  // ...
  middleware: [
    // Rate limiting
    rateLimit({
      windowMs: 15 * 60 * 1000,
      max: 100,
      message: 'Too many requests'
    }),
    
    // Request logging
    (req, res, next) => {
      console.log(`${req.method} ${req.path}`);
      next();
    },
    
    // Custom auth middleware
    async (req, res, next) => {
      if (req.path.startsWith('/admin')) {
        // Additional admin checks
      }
      next();
    }
  ]
});
```

**Reference:**  
- [Advanced Features Guide](#)
- [Caching Strategies](#)
- [DLQ Management](#)

---

## 18. Migration Guide

### 18.1 From Standard MCP SSE Transport

**Key Changes:**

| Standard MCP | HTTP Webhook MCP |
|--------------|------------------|
| Persistent SSE connection | Stateless HTTP requests |
| Server pushes to client | Client provides callback URL |
| `subscribe()` keeps connection | `subscribe()` returns subscriptionId |
| Real-time push | Webhook callback |
| Connection per client | Shared webhook endpoints |

**Migration Steps:**

1. **Update Client Implementation**
   - Remove SSE connection logic
   - Implement webhook receiver endpoint
   - Provide callback URL in subscribe requests
   - Handle notifications via webhooks

2. **Update Server Implementation**
   - Replace `StdioServerTransport` with `createMCPServer()`
   - Add KV store configuration
   - Implement subscription handlers
   - Configure webhook endpoints

3. **Update Resource Subscriptions**
   ```typescript
   // Before (Standard MCP)
   server.setRequestHandler(SubscribeRequest, async (request) => {
     // Setup SSE push
   });
   
   // After (HTTP Webhook)
   resources: [{
     subscription: {
       onSubscribe: async (uri, subscriptionId, webhookUrl, context) => {
         // Register with third-party
         // Return metadata
       }
     }
   }]
   ```

### 18.2 From Custom Implementation

If migrating from a custom webhook implementation:

1. **Adopt Standard MCP Protocol**
   - Use MCP JSON-RPC message format
   - Implement standard endpoints (`/tools/list`, `/resources/read`, etc.)

2. **Use Library's Subscription Management**
   - Replace custom subscription tracking with library's KV store
   - Leverage automatic webhook routing

3. **Standardize Error Handling**
   - Use MCP error codes
   - Implement standard error responses

**Reference:**  
- [Migration Examples](#)
- [Comparison Guide](#)

---

## 19. Troubleshooting

### 19.1 Common Issues

**Webhook Not Received:**

```bash
# Check webhook registration
curl -H "Authorization: Bearer $TOKEN" \
  https://api.github.com/repos/owner/repo/hooks

# Test webhook delivery
curl -X POST https://mcp.example.com/webhooks/incoming/sub_xyz \
  -H "Content-Type: application/json" \
  -d '{"test": true}'

# Check server logs
docker logs mcp-server | grep "webhook"

# Verify subscription exists in store
redis-cli GET "subscription:sub_xyz"
```

**Client Not Receiving Notifications:**

```typescript
// Enable debug logging
const server = createMCPServer({
  // ...
  logLevel: 'debug',
  webhooks: {
    outgoing: {
      timeout: 5000,
      retries: 3,
      
      // Add debugging
      onBeforeCall: (url, payload) => {
        console.log('Calling client webhook:', url, payload);
      },
      onAfterCall: (url, response, error) => {
        console.log('Webhook result:', { url, status: response?.status, error });
      }
    }
  }
});
```

**Store Connection Issues:**

```typescript
// Test store connectivity
async function testStore(store: KeyValueStore) {
  try {
    await store.set('test-key', 'test-value', 60);
    const value = await store.get('test-key');
    await store.delete('test-key');
    
    if (value === 'test-value') {
      console.log('✓ Store connection OK');
    } else {
      console.error('✗ Store read/write mismatch');
    }
  } catch (error) {
    console.error('✗ Store connection failed:', error);
  }
}
```

**Signature Verification Failures:**

```typescript
// Debug signature verification
verifyIncomingSignature: (payload, signature, secret) => {
  console.log('Verifying signature:');
  console.log('- Received signature:', signature);
  console.log('- Payload:', JSON.stringify(payload));
  console.log('- Secret length:', secret?.length);
  
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(JSON.stringify(payload));
  const expected = `sha256=${hmac.digest('hex')}`;
  
  console.log('- Expected signature:', expected);
  console.log('- Match:', signature === expected);
  
  return signature === expected;
}
```

### 19.2 Debugging Tools

**List All Subscriptions:**

```typescript
// GET /debug/subscriptions (development only)
async function listAllSubscriptions(store: KeyValueStore) {
  const keys = await store.scan('subscription:*');
  const subscriptions = await Promise.all(
    keys.map(async (key) => {
      const data = await store.get(key);
      return { key, data: JSON.parse(data || '{}') };
    })
  );
  return subscriptions;
}
```

**Test Webhook Delivery:**

```bash
# Simulate third-party webhook
curl -X POST http://localhost:3000/webhooks/incoming/sub_xyz \
  -H "Content-Type: application/json" \
  -H "X-GitHub-Event: issues" \
  -H "X-Hub-Signature-256: sha256=..." \
  -d @webhook-payload.json
```

**Monitor Webhook Queue:**

```typescript
// Check dead letter queue
async function checkDLQ(store: KeyValueStore) {
  const failedWebhooks = await store.scan('dlq:*');
  console.log(`Failed webhooks: ${failedWebhooks.length}`);
  
  for (const key of failedWebhooks) {
    const data = await store.get(key);
    console.log(JSON.parse(data || '{}'));
  }
}
```

**Reference:**  
- [Troubleshooting Guide](#)
- [Debug Mode Documentation](#)
- [FAQ](#)

---

## 20. API Reference

### 20.1 Core Types

```typescript
// Server configuration
interface MCPServerConfig { /* ... */ }

// Tool definition
interface ToolDefinition<TInput, TOutput> { /* ... */ }

// Resource definition
interface ResourceDefinition<TData> { /* ... */ }

// Subscription handlers
interface ResourceSubscription { /* ... */ }

// Storage interface
interface KeyValueStore { /* ... */ }

// Webhook configuration
interface WebhookConfig { /* ... */ }

// Authentication context
interface AuthContext { /* ... */ }
```

Full type definitions: [API Types Documentation](#)

### 20.2 Utility Functions

**URI Template Parsing:**

```typescript
import { parseUriTemplate, matchUriTemplate } from 'mcp-http-webhook/utils';

const params = parseUriTemplate(
  'github://repo/{owner}/{repo}/issues',
  'github://repo/octocat/hello-world/issues'
);
// { owner: 'octocat', repo: 'hello-world' }

const matches = matchUriTemplate(
  'github://repo/{owner}/{repo}/issues',
  'github://repo/octocat/hello-world/issues'
);
// true
```

**ID Generation:**

```typescript
import { generateSubscriptionId, generateWebhookUrl } from 'mcp-http-webhook/utils';

const id = generateSubscriptionId();
// 'sub_1a2b3c4d5e6f'

const url = generateWebhookUrl(publicUrl, subscriptionId);
// 'https://mcp.example.com/webhooks/incoming/sub_1a2b3c4d5e6f'
```

**Signature Utilities:**

```typescript
import { createHmacSignature, verifyHmacSignature } from 'mcp-http-webhook/utils';

const signature = createHmacSignature(payload, secret, 'sha256');
const isValid = verifyHmacSignature(payload, signature, secret, 'sha256');
```

**Reference:**  
- [Utility Functions Documentation](#)
- [Helper Methods](#)

---

## 21. Examples Repository

### 21.1 Complete Examples

- **GitHub Integration** - [github-mcp-server.ts](#)
  - Issue tracking, PR management, webhook subscriptions
  
- **Google Drive Integration** - [gdrive-mcp-server.ts](#)
  - File watching, change notifications via Drive API webhooks
  
- **Slack Integration** - [slack-mcp-server.ts](#)
  - Channel messages, event subscriptions, slash commands
  
- **PostgreSQL Changes** - [postgres-mcp-server.ts](#)
  - LISTEN/NOTIFY based resource subscriptions
  
- **Stripe Webhooks** - [stripe-mcp-server.ts](#)
  - Payment events, subscription changes
  
- **Shopify Integration** - [shopify-mcp-server.ts](#)
  - Product updates, order notifications

### 21.2 Store Implementations

- **Redis** - [redis-store.ts](#)
- **DynamoDB** - [dynamodb-store.ts](#)
- **PostgreSQL** - [postgres-store.ts](#)
- **MongoDB** - [mongodb-store.ts](#)
- **In-Memory (Dev)** - [memory-store.ts](#)

### 21.3 Authentication Examples

- **JWT Bearer Token** - [jwt-auth.ts](#)
- **API Key** - [apikey-auth.ts](#)
- **OAuth2** - [oauth2-auth.ts](#)
- **Mutual TLS** - [mtls-auth.ts](#)

**Reference:**  
- [Examples Repository](https://github.com/your-org/mcp-http-webhook/tree/main/examples)

---

## 22. Performance Optimization

### 22.1 Connection Pooling

**Redis Connection Pool:**

```typescript
import Redis from 'ioredis';

const redis = new Redis({
  host: process.env.REDIS_HOST,
  port: parseInt(process.env.REDIS_PORT || '6379'),
  maxRetriesPerRequest: 3,
  enableReadyCheck: true,
  lazyConnect: false,
  
  // Connection pool settings
  connectionName: 'mcp-server',
  connectTimeout: 10000,
  
  // Reconnection strategy
  retryStrategy: (times) => {
    const delay = Math.min(times * 50, 2000);
    return delay;
  }
});
```

**HTTP Client Pool:**

```typescript
import { Agent } from 'https';

const httpsAgent = new Agent({
  keepAlive: true,
  maxSockets: 50,
  maxFreeSockets: 10,
  timeout: 60000,
  keepAliveMsecs: 30000
});

// Use with fetch or axios
fetch(url, { agent: httpsAgent });
```

### 22.2 Caching Strategies

**Multi-Level Caching:**

```typescript
import NodeCache from 'node-cache';

const memoryCache = new NodeCache({ stdTTL: 60, checkperiod: 120 });

const cachedRead = async (uri: string, context: AuthContext) => {
  // L1: Memory cache (fastest)
  const memoryCached = memoryCache.get(uri);
  if (memoryCached) return memoryCached;
  
  // L2: Redis cache
  const redisCached = await redis.get(`cache:${uri}`);
  if (redisCached) {
    const data = JSON.parse(redisCached);
    memoryCache.set(uri, data);
    return data;
  }
  
  // L3: Fetch from source
  const data = await fetchFromSource(uri, context);
  
  // Update caches
  memoryCache.set(uri, data);
  await redis.setex(`cache:${uri}`, 300, JSON.stringify(data));
  
  return data;
};
```

### 22.3 Batch Processing

**Webhook Batching:**

```typescript
class WebhookBatcher {
  private queue: Map<string, any[]> = new Map();
  private timer: NodeJS.Timeout | null = null;
  
  add(clientUrl: string, notification: any) {
    if (!this.queue.has(clientUrl)) {
      this.queue.set(clientUrl, []);
    }
    this.queue.get(clientUrl)!.push(notification);
    
    if (!this.timer) {
      this.timer = setTimeout(() => this.flush(), 1000);
    }
  }
  
  async flush() {
    const batches = Array.from(this.queue.entries());
    this.queue.clear();
    this.timer = null;
    
    await Promise.all(
      batches.map(([url, notifications]) =>
        this.sendBatch(url, notifications)
      )
    );
  }
  
  async sendBatch(url: string, notifications: any[]) {
    // Send all notifications in one request
    await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ batch: notifications })
    });
  }
}
```

**Reference:**  
- [Performance Tuning Guide](#)
- [Benchmarking Tools](#)
- [Optimization Examples](#)

---

## 23. Contributing

### 23.1 Development Setup

```bash
# Clone repository
git clone https://github.com/your-org/mcp-http-webhook.git
cd mcp-http-webhook

# Install dependencies
pnpm install

# Run tests
pnpm test

# Start development server
pnpm dev

# Build
pnpm build
```

### 23.2 Project Structure

```
mcp-http-webhook/
├── src/
│   ├── index.ts              # Main export
│   ├── server.ts             # Server creation
│   ├── protocol/             # MCP protocol implementation
│   ├── webhooks/             # Webhook handling
│   ├── subscriptions/        # Subscription management
│   ├── utils/                # Utility functions
│   └── types/                # TypeScript types
├── examples/                 # Example implementations
├── tests/                    # Test suites
├── docs/                     # Documentation
└── scripts/                  # Build and utility scripts
```

### 23.3 Coding Standards

**TypeScript Guidelines:**

```typescript
// Use explicit types for public APIs
export interface ToolDefinition<TInput = any, TOutput = any> {
  name: string;
  description: string;
  inputSchema: JSONSchema;
  handler: ToolHandler<TInput, TOutput>;
}

// Use async/await over promises
async function fetchData(): Promise<Data> {
  return await api.get('/data');
}

// Prefer const over let
const config = getConfig();

// Use optional chaining and nullish coalescing
const value = config?.webhooks?.timeout ?? 5000;

// Document public APIs with JSDoc
/**
 * Creates a new MCP server instance
 * @param config - Server configuration
 * @returns Configured MCP server
 * @throws {ValidationError} If configuration is invalid
 */
export function createMCPServer(config: MCPServerConfig): MCPServer {
  // ...
}
```

**Code Style:**

```typescript
// Naming conventions
class SubscriptionManager { }        // PascalCase for classes
function handleWebhook() { }         // camelCase for functions
const MAX_RETRIES = 3;               // UPPER_CASE for constants
interface WebhookConfig { }          // PascalCase for interfaces

// Import ordering
import { external } from 'external';  // External packages
import { internal } from './internal'; // Internal modules
import type { Type } from './types';   // Type imports last

// Error handling
try {
  await riskyOperation();
} catch (error) {
  logger.error('Operation failed', { error, context });
  throw new OperationError('Failed to complete', { cause: error });
}
```

### 23.4 Testing Requirements

**Test Coverage Goals:**
- Unit tests: >80% coverage
- Integration tests for all major flows
- E2E tests for critical paths

**Test Structure:**

```typescript
describe('SubscriptionManager', () => {
  let manager: SubscriptionManager;
  let mockStore: jest.Mocked<KeyValueStore>;
  
  beforeEach(() => {
    mockStore = {
      get: jest.fn(),
      set: jest.fn(),
      delete: jest.fn()
    };
    manager = new SubscriptionManager(mockStore);
  });
  
  describe('createSubscription', () => {
    it('should create a new subscription', async () => {
      const result = await manager.createSubscription({
        uri: 'test://resource',
        clientCallbackUrl: 'http://client.test/webhook'
      });
      
      expect(result.subscriptionId).toBeDefined();
      expect(mockStore.set).toHaveBeenCalled();
    });
    
    it('should throw error for invalid URI', async () => {
      await expect(
        manager.createSubscription({
          uri: 'invalid',
          clientCallbackUrl: 'http://client.test/webhook'
        })
      ).rejects.toThrow(ValidationError);
    });
  });
});
```

### 23.5 Pull Request Process

1. **Fork and Branch**
   ```bash
   git checkout -b feature/my-new-feature
   ```

2. **Make Changes**
   - Write code following style guide
   - Add/update tests
   - Update documentation

3. **Run Quality Checks**
   ```bash
   pnpm lint        # ESLint
   pnpm format      # Prettier
   pnpm test        # Jest tests
   pnpm type-check  # TypeScript
   ```

4. **Commit with Conventional Commits**
   ```bash
   git commit -m "feat: add webhook retry backoff configuration"
   git commit -m "fix: resolve subscription cleanup race condition"
   git commit -m "docs: update subscription flow diagram"
   ```

5. **Submit PR**
   - Clear description of changes
   - Link to related issues
   - Include screenshots/examples if applicable

**Reference:**  
- [Contributing Guide](https://github.com/your-org/mcp-http-webhook/blob/main/CONTRIBUTING.md)
- [Code of Conduct](https://github.com/your-org/mcp-http-webhook/blob/main/CODE_OF_CONDUCT.md)

---

## 24. Changelog

### Version 1.0.0 (2025-01-15)

**Initial Release**

**Features:**
- ✨ HTTP-based MCP server implementation
- ✨ Webhook-based resource subscriptions
- ✨ Key-value store abstraction for persistence
- ✨ Automatic webhook routing and retry logic
- ✨ Signature verification for incoming/outgoing webhooks
- ✨ Support for tools, resources, and prompts
- ✨ Built-in authentication middleware
- ✨ Prometheus metrics integration
- ✨ OpenTelemetry tracing support
- ✨ Dead letter queue for failed webhooks

**Storage Implementations:**
- 📦 Redis adapter
- 📦 DynamoDB adapter
- 📦 PostgreSQL adapter
- 📦 In-memory adapter (development)

**Examples:**
- 📚 GitHub integration
- 📚 Google Drive integration
- 📚 Slack integration
- 📚 PostgreSQL NOTIFY integration
- 📚 Stripe webhooks

**Documentation:**
- 📖 Complete API reference
- 📖 Deployment guides (Docker, Kubernetes)
- 📖 Security best practices
- 📖 Performance optimization guide
- 📖 Migration guide from standard MCP

---

## 25. Roadmap

### Version 1.1.0 (Q2 2025)

**Planned Features:**
- 🚀 GraphQL subscription support
- 🚀 WebSocket fallback transport
- 🚀 Built-in rate limiting per user
- 🚀 Subscription priority levels
- 🚀 Webhook payload transformation
- 🚀 Multi-region deployment support
- 🚀 Admin dashboard UI

### Version 1.2.0 (Q3 2025)

**Planned Features:**
- 🚀 gRPC transport option
- 🚀 Event sourcing support
- 🚀 Built-in circuit breaker
- 🚀 A/B testing framework
- 🚀 Subscription groups/topics
- 🚀 Webhook replay functionality

### Future Considerations
- Cloud provider integrations (AWS EventBridge, Azure Event Grid)
- Machine learning-based anomaly detection
- Advanced analytics and insights
- Multi-tenancy support
- Federation across multiple MCP servers

**Feature Requests:**  
Submit feature requests at [GitHub Issues](https://github.com/your-org/mcp-http-webhook/issues)

---

## 26. FAQ

### General Questions

**Q: How is this different from standard MCP with SSE transport?**

A: This library eliminates persistent connections entirely. Instead of maintaining an SSE connection for push notifications, clients provide a webhook URL and receive notifications via HTTP POST. This makes horizontal scaling trivial and works better with serverless architectures.

**Q: Can I use this with the standard MCP clients?**

A: No, standard MCP clients expect SSE transport. You'll need to implement a client that works with HTTP + webhooks. However, the protocol messages (tools/call, resources/read, etc.) remain the same.

**Q: Why do I need an external key-value store?**

A: Subscription state must be shared across multiple server instances. An external store (Redis, DynamoDB, etc.) enables horizontal scaling and prevents data loss during deployments.

### Subscription Questions

**Q: What happens if a client's webhook endpoint is down?**

A: The library automatically retries with exponential backoff (configurable). After all retries fail, the notification is stored in the dead letter queue for manual intervention.

**Q: Can one resource have multiple subscriptions from the same client?**

A: Yes, each subscription gets a unique `subscriptionId`. A client can subscribe to the same resource multiple times with different callback URLs.

**Q: How do I test subscriptions locally without exposing a public URL?**

A: Use tools like [ngrok](https://ngrok.com/) or [localhost.run](https://localhost.run/) to create temporary public URLs that tunnel to your local machine.

```bash
ngrok http 3000
# Use the generated URL as your publicUrl
```

### Third-Party Integration Questions

**Q: What if the third-party service doesn't support webhooks?**

A: You can implement polling-based subscriptions:

```typescript
subscription: {
  onSubscribe: async (uri, subscriptionId, webhookUrl, context) => {
    // Start a polling job
    const jobId = await queue.add('poll-resource', {
      uri,
      subscriptionId,
      interval: 60000  // Poll every minute
    });
    
    return { thirdPartyWebhookId: jobId };
  },
  
  // In your polling worker:
  // - Fetch resource data
  // - Compare with previous state
  // - If changed, POST to webhookUrl
}
```

**Q: How do I handle webhook signature verification for different third-party services?**

A: Each resource can implement custom verification logic:

```typescript
onWebhook: async (subscriptionId, payload, headers) => {
  // GitHub uses X-Hub-Signature-256
  if (headers['x-hub-signature-256']) {
    verifyGitHubSignature(payload, headers['x-hub-signature-256'], secret);
  }
  
  // Slack uses X-Slack-Signature
  if (headers['x-slack-signature']) {
    verifySlackSignature(payload, headers['x-slack-signature'], headers['x-slack-request-timestamp'], secret);
  }
  
  // Process webhook...
}
```

### Performance Questions

**Q: How many subscriptions can the server handle?**

A: This depends on your key-value store. Redis can easily handle millions of subscriptions. The bottleneck is typically outgoing webhook calls, which can be optimized with batching and connection pooling.

**Q: What's the latency for webhook notifications?**

A: Typically <500ms from third-party webhook receipt to client notification, depending on network conditions and your webhook processing logic.

**Q: Should I batch webhook notifications?**

A: Yes, if you expect high-frequency updates. The library supports batching multiple notifications into a single HTTP call to clients.

### Security Questions

**Q: How do I prevent replay attacks on webhooks?**

A: Implement timestamp verification:

```typescript
verifyIncomingSignature: (payload, signature, secret) => {
  const timestamp = headers['x-webhook-timestamp'];
  const now = Date.now() / 1000;
  
  // Reject webhooks older than 5 minutes
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false;
  }
  
  // Verify signature including timestamp
  return verifySignature(payload, signature, secret);
}
```

**Q: Should I whitelist client callback URLs?**

A: Yes, for security:

```typescript
const ALLOWED_DOMAINS = ['client.example.com', 'app.client.com'];

authenticate: async (req) => {
  const callbackUrl = req.body.params?.callbackUrl;
  if (callbackUrl) {
    const domain = new URL(callbackUrl).hostname;
    if (!ALLOWED_DOMAINS.includes(domain)) {
      throw new ValidationError('Callback URL not whitelisted');
    }
  }
  // ... continue authentication
}
```

### Deployment Questions

**Q: Can I run this serverlessly (AWS Lambda, Cloud Functions)?**

A: Yes, but with caveats:
- Tools and resources work perfectly (stateless HTTP)
- Incoming webhooks work fine
- Outgoing webhook calls need to handle cold starts
- Consider using a separate always-on service for webhook processing

**Q: How do I handle zero-downtime deployments?**

A: 
1. Use external store (Redis/DynamoDB) for state
2. Deploy new version alongside old
3. Update load balancer to point to new version
4. Old version continues processing in-flight webhooks
5. Gracefully shutdown old version after drain period

```typescript
process.on('SIGTERM', async () => {
  console.log('Received SIGTERM, starting graceful shutdown');
  
  // Stop accepting new connections
  server.close();
  
  // Wait for in-flight requests to complete
  await waitForInFlightRequests();
  
  // Close store connections
  await redis.quit();
  
  process.exit(0);
});
```

---

## 27. Resources & Links

### Official Documentation
- [MCP Specification](https://spec.modelcontextprotocol.io/)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [Library GitHub Repository](https://github.com/your-org/mcp-http-webhook)
- [API Documentation](https://docs.mcp-http-webhook.dev/api)

### Community
- [Discord Server](https://discord.gg/mcp-webhook)
- [GitHub Discussions](https://github.com/your-org/mcp-http-webhook/discussions)
- [Stack Overflow Tag: mcp-http-webhook](https://stackoverflow.com/questions/tagged/mcp-http-webhook)

### Examples & Tutorials
- [Complete Examples Repository](https://github.com/your-org/mcp-http-webhook/tree/main/examples)
- [Video Tutorials](https://youtube.com/playlist?list=xxx)
- [Blog: Building Your First MCP Server](https://blog.example.com/first-mcp-server)
- [Blog: Scaling MCP to 1M Subscriptions](https://blog.example.com/scaling-mcp)

### Related Projects
- [MCP Client Libraries](https://github.com/modelcontextprotocol)
- [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
- [MCP CLI Tools](https://github.com/modelcontextprotocol/cli)

### Third-Party Integrations
- [GitHub Webhooks Documentation](https://docs.github.com/en/webhooks)
- [Google Drive Push Notifications](https://developers.google.com/drive/api/guides/push)
- [Slack Events API](https://api.slack.com/apis/connections/events-api)
- [Stripe Webhooks](https://stripe.com/docs/webhooks)
- [PostgreSQL NOTIFY](https://www.postgresql.org/docs/current/sql-notify.html)

---

## 28. License

**MIT License**

Copyright (c) 2025 Your Organization

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

---

## 29. Support

### Commercial Support

For enterprise support, custom integrations, and consulting services:
- Email: support@mcp-http-webhook.dev
- Enterprise Plans: [View Pricing](https://mcp-http-webhook.dev/pricing)

### Community Support

- **GitHub Issues**: Bug reports and feature requests
- **Discussions**: Questions and community help
- **Discord**: Real-time chat with maintainers and community
- **Stack Overflow**: Tag your questions with `mcp-http-webhook`

### Bug Reports

When filing a bug report, please include:

```markdown
**Environment:**
- Library version: 1.0.0
- Node.js version: 20.x
- Store implementation: Redis 7.x
- Deployment: Kubernetes / Docker / Bare metal

**Expected Behavior:**
[What you expected to happen]

**Actual Behavior:**
[What actually happened]

**Reproduction Steps:**
1. Create server with config...
2. Subscribe to resource...
3. Send webhook...

**Logs:**
```
[Relevant log output]
```

**Additional Context:**
[Any other relevant information]
```

---

## 30. Acknowledgments

This library builds upon the excellent work of:

- **Anthropic** - For creating the Model Context Protocol specification
- **MCP Community** - For feedback and contributions
- **Third-Party Service Providers** - For comprehensive webhook documentation
- **Open Source Contributors** - For example implementations and testing

Special thanks to all contributors who have helped make this library possible.

---

## Appendix A: Complete Type Definitions

See [Type Reference Documentation](https://docs.mcp-http-webhook.dev/types) for complete TypeScript type definitions.

## Appendix B: HTTP API Specification

See [HTTP API Reference](https://docs.mcp-http-webhook.dev/http-api) for detailed endpoint specifications, request/response formats, and error codes.

## Appendix C: Storage Migration Guide

See [Storage Migration Guide](https://docs.mcp-http-webhook.dev/storage-migration) for instructions on migrating between different key-value store implementations.

## Appendix D: Performance Benchmarks

See [Benchmark Results](https://docs.mcp-http-webhook.dev/benchmarks) for performance comparisons across different configurations and deployment scenarios.

---

**Document Version:** 1.0.0  
**Last Updated:** 2025-01-15  
**Maintained By:** MCP HTTP Webhook Team

For the latest version of this document, visit: [https://docs.mcp-http-webhook.dev/specification](https://docs.mcp-http-webhook.dev/specification)