# 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 ```typescript #!/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 Promise>` for **lazy loading** - `setupToolModules()` — registers each tool file via dynamic `import()`: ```typescript 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` ```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` ```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: ```typescript async get(path: string, params?: Record): Promise async create(path: string, data: unknown): Promise async update(path: string, data: unknown): Promise async delete(path: string): Promise ``` --- ## 3. Tool Files (`src/tools/*.ts`) ### Structure: ```typescript 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: ```typescript export interface Contact { id: string; firstName: string; lastName: string; email: string; phone?: string; company?: string; createdAt: string; updatedAt: string; } export interface PaginatedResponse { 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: ```typescript 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( }> ); } ``` ### `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`: ```html {App Name} - {Platform} MCP
``` ### `styles.css` — Dark Theme: ```css :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: ```javascript { 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`): ```javascript { 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.*