clawdbot-workspace/mcp-gold-standard.md

13 KiB

MCP Server Gold Standard — MANDATORY REFERENCE

READ THIS before ANY MCP server work. No exceptions. This is the definitive spec for how every MCPEngine server must be built. Last updated: 2026-02-14


1. Server Architecture

main.ts — Entry Point

#!/usr/bin/env node
  • Shebang line for CLI execution
  • Env validation with clear error messages (e.g., "Get your access token from: Settings > Apps > ...")
  • Graceful shutdown handlers for SIGINT/SIGTERM
  • Health check support
  • Dual transport: stdio (default) + HTTP/SSE option via --http --port=3000
  • Loads config → creates API client → creates server → starts transport

server.ts — Server Class

  • MUST be a CLASS (e.g., ShopifyMCPServer), not inline code
  • toolModules: Map<string, () => Promise<ToolModule[]>> for lazy loading
  • setupToolModules() — registers each tool file via dynamic import():
    this.toolModules.set('orders', async () => {
      const module = await import('./tools/orders.js');
      return module.default;
    });
    
  • setupHandlers() — registers ListToolsRequestSchema and CallToolRequestSchema
  • loadAllTools() — resolves all lazy modules for ListTools
  • CallTool handler routes by tool name to correct handler function
  • Capabilities: { tools: {}, resources: {} }

package.json

{
  "name": "@mcpengine/{platform-name}",
  "version": "1.0.0",
  "type": "module",
  "main": "dist/main.js",
  "bin": { "@mcpengine/{platform-name}": "dist/main.js" },
  "scripts": {
    "build": "tsc",
    "start": "node dist/main.js",
    "dev": "tsx watch src/main.ts"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "axios": "^1.7.0",
    "zod": "^3.23.0"
  },
  "devDependencies": {
    "typescript": "^5.6.0",
    "tsx": "^4.19.0",
    "@types/node": "^22.0.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "dist",
    "rootDir": "src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "noUncheckedIndexedAccess": true,
    "jsx": "react-jsx"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "src/apps", "src/ui", "src/**/react-app"]
}

NOTE: Always exclude apps/ui from TSC — they compile separately.


2. API Client (src/client/{platform}-client.ts or src/clients/{platform}.ts)

  • Axios-based with AxiosInstance
  • Constructor takes config object: { accessToken, baseUrl, timeout?, apiVersion? }
  • Request interceptor: Timestamp logging of method + URL
  • Response interceptor: Rate limit header parsing, warning at 80% threshold, error normalization
  • Rate limit tracking: { current, max } from response headers
  • Pagination helpers: Return { data: T[], pageInfo: { hasNextPage, nextPageUrl } }
  • Typed error handling with status code + message
  • Default timeout: 30000ms
  • Base URL constructed from env vars

Client Methods Pattern:

async get<T>(path: string, params?: Record<string, unknown>): Promise<T>
async create<T>(path: string, data: unknown): Promise<T>
async update<T>(path: string, data: unknown): Promise<T>
async delete(path: string): Promise<void>

3. Tool Files (src/tools/*.ts)

Structure:

import { z } from 'zod';
import { PlatformClient } from '../clients/platform.js';

const ListOrdersInput = z.object({
  limit: z.number().min(1).max(250).default(50).describe('Results per page'),
  page_info: z.string().optional().describe('Cursor for pagination'),
  status: z.enum(['open', 'closed', 'any']).optional().describe('Filter by status'),
  created_at_min: z.string().optional().describe('Filter by min creation date (ISO 8601)'),
});

// ... more Zod schemas ...

export default [
  {
    name: '{platform}_list_orders',
    description: 'List orders with pagination and filtering by status, date range, and financial status. Use when the user wants to browse, search, or export their order history. Returns paginated results with cursor-based navigation.',
    inputSchema: {
      type: 'object' as const,
      properties: { /* JSON Schema from Zod */ },
      required: ['limit'],
    },
    handler: async (input: unknown, client: PlatformClient) => {
      const validated = ListOrdersInput.parse(input);
      const result = await client.get('/orders.json', validated);
      return {
        content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],
      };
    },
  },
  // ... more tools
];

Naming Conventions (MANDATORY):

  • {platform}_list_* — paginated collections
  • {platform}_get_* — single resource by ID
  • {platform}_create_* — create new resource
  • {platform}_update_* — update existing resource
  • {platform}_delete_* — delete resource
  • {platform}_search_* — query-based lookup
  • Domain verbs: {platform}_send_email, {platform}_cancel_order, {platform}_archive_card
  • ALL snake_case, ALL lowercase
  • NEVER mix fetch_* / get_* / retrieve_* — pick ONE (we use get_)

Description Requirements:

  • BAD: "Lists contacts"
  • GOOD: "Lists contacts from HubSpot with optional filtering by email, name, company, or lifecycle stage. Use when the user wants to browse, search, or export their contact database. Returns paginated results with cursor-based navigation. Supports up to 100 results per page."
  • Every description must tell an AI agent WHEN and WHY to use the tool
  • Include: what it returns, pagination details, filtering options, rate limit notes

Pagination (MANDATORY for all list_* tools):

  • limit param with min/max/default
  • page_info or cursor or offset param
  • Response includes has_more / hasNextPage indicator
  • Response includes next_cursor / next_page_url for follow-up

Handler Pattern:

  1. Validate input with Zod .parse(input)
  2. Call client method
  3. Return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] }

Tool Count Minimums:

API Size Minimum Tools
Small (<30 endpoints) 15-20
Medium (30-100 endpoints) 30-50
Large (100+ endpoints) 50-80+
7-8 tools = a demo, NOT a product

4. Types (src/types/index.ts)

  • TypeScript interfaces for ALL API entities
  • Export everything (interfaces, type aliases, enums)
  • Include: request params, response types, pagination types, error types
  • Example:
export interface Contact {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phone?: string;
  company?: string;
  createdAt: string;
  updatedAt: string;
}

export interface PaginatedResponse<T> {
  data: T[];
  pageInfo: {
    hasNextPage: boolean;
    nextPageUrl?: string;
  };
}

5. Apps (src/apps/{app-name}/)

Each app is a self-contained React application in its own subdirectory.

Files per app:

src/apps/{app-name}/
  App.tsx      — Main React component
  main.tsx     — Entry point with ErrorBoundary + Suspense
  index.html   — HTML shell
  styles.css   — Dark theme styling

main.tsx Pattern:

import { Suspense, lazy, Component, ErrorInfo, ReactNode } from 'react';
import { createRoot } from 'react-dom/client';

const App = lazy(() => import('./App'));

// ErrorBoundary class with getDerivedStateFromError + componentDidCatch
// LoadingSkeleton component with shimmer divs

const container = document.getElementById('root');
if (container) {
  const root = createRoot(container);
  root.render(
    <ErrorBoundary>
      <Suspense fallback={<LoadingSkeleton />}>
        <App />
      </Suspense>
    </ErrorBoundary>
  );
}

App.tsx Pattern:

  • Self-contained with mock data (no live API calls)
  • useState for state management
  • useDebounce hook for search
  • useToast hook for notifications
  • useTransition for non-blocking updates
  • Search/filter/sort functionality
  • Responsive layout
  • Mock data should be realistic and platform-relevant

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{App Name} - {Platform} MCP</title>
  <link rel="stylesheet" href="./styles.css">
</head>
<body>
  <div id="root"></div>
  <script type="module" src="./main.tsx"></script>
</body>
</html>

styles.css — Dark Theme:

:root {
  --bg-primary: #0f172a;
  --bg-secondary: #1e293b;
  --bg-tertiary: #334155;
  --text-primary: #f1f5f9;
  --text-secondary: #cbd5e1;
  --text-muted: #94a3b8;
  --accent: #3b82f6;
  --accent-hover: #2563eb;
  --success: #10b981;
  --error: #ef4444;
  --warning: #f59e0b;
  --border: #475569;
}

App Ideas per Platform Type:

  • CRM: contact-manager, deal-pipeline, activity-feed, report-dashboard
  • E-commerce: order-management, product-catalog, inventory-tracker, analytics-dashboard
  • Project Mgmt: task-board, timeline-view, team-workload, sprint-dashboard
  • Support: ticket-queue, knowledge-base, customer-timeline, satisfaction-dashboard
  • HR: employee-directory, time-off-calendar, org-chart, payroll-dashboard

6. Landing Pages

Generation:

  • Use landing-pages/site-generator.js with config object
  • Run: node site-generator.js {platform-id}
  • Output: single HTML file

Config Structure:

{
  name: 'Platform Name',
  tagline: 'AI-Power Your {Domain} in 2 Clicks',
  color: '#HEX',           // Brand color
  tools: '47',             // Tool count string
  description: 'The complete {Platform} MCP server. {What it does} with AI.',
  features: [
    { title: 'Feature Name', desc: 'What it does in one sentence.' },
    // 4 features
  ],
  painPoints: [
    { bad: 'Manual way of doing X', good: 'AI does X automatically' },
    // 3 pain points
  ]
}

Chat Demo Config (chatDemoData):

{
  platformId: {
    messages: [
      { type: 'user', text: 'Natural question about the platform' },
      { 
        type: 'ai', 
        text: 'Response text',
        widgetData: { type: 'metrics|tickets|board|deals|pnl|schedule', ... },
        action: '✓ Summary of what was done'
      },
      { type: 'user', text: 'Follow-up action request' },
      { type: 'ai', text: 'Confirmation', action: '✓ Action completed summary' }
    ]
  }
}

6 Widget Types for Chat Demo:

  1. tickets — Table with ID, subject, priority badge, time. Has summary + alert.
  2. metrics — 3-column grid of metric cards with label, value, change (+/-). Has alert.
  3. board — Kanban-style columns with cards
  4. deals — Pipeline/deal cards with values
  5. pnl — Profit/loss bars with labels
  6. schedule — Time slots with bookings

Landing Page Sections:

  1. Hero with gradient text + glow effect
  2. Features grid (4 cards with hover glow)
  3. Animated chat demo — scroll-triggered GSAP, messages appear sequentially (1s delay)
  4. Pain points (bad → good comparisons)
  5. CTA section
  6. Footer

Animations:

  • animate-float / animate-float-delayed / animate-float-slow — floating elements
  • card-glow:hover — box-shadow + translateY(-4px)
  • gradient-shift — animated gradient background
  • video-glow — pulsing box-shadow
  • GSAP ScrollTrigger for chat demo (plays once on viewport enter)

7. README.md

Every server MUST have a README with:

  1. Title + description
  2. Features list (bullet points of capabilities)
  3. Installation (npm install && npm run build)
  4. Environment Variables table — Variable, Required (/), Description, Example
  5. Getting Your Access Token — Step-by-step for that specific platform
  6. Required API Scopes — List of permissions needed
  7. Usage — Stdio mode + HTTP mode examples
  8. Coverage Manifest:
    Total API endpoints: X
    Tools implemented: Y
    Intentionally skipped: Z (with reasons)
    Coverage: Y/X = NN%
    

8. File Tree Summary

servers/{platform}/
├── package.json              # @mcpengine/{platform}, type:module, bin field
├── tsconfig.json             # ES2022, Node16, strict, exclude apps
├── README.md                 # Full docs + coverage manifest
├── src/
│   ├── main.ts               # Entry: env validation, graceful shutdown
│   ├── server.ts             # Server CLASS with lazy-loaded tool modules
│   ├── clients/              # (or client/)
│   │   └── {platform}.ts     # Axios client with interceptors + rate limiting
│   ├── types/
│   │   └── index.ts          # All TypeScript interfaces
│   ├── tools/
│   │   ├── orders.ts         # export default [...tools]
│   │   ├── customers.ts
│   │   └── ...               # One file per domain area
│   └── apps/
│       ├── order-management/
│       │   ├── App.tsx
│       │   ├── main.tsx
│       │   ├── index.html
│       │   └── styles.css
│       └── ...               # One dir per app
└── landing-pages/
    └── sites/{platform}.html  # Generated landing page

This is the gold standard. Every MCP server we build or fix must match this spec.