Jake Shore 8d65417afe Add 11 MCP agent skills to repo — 550KB of encoded pipeline knowledge
Skills added:
- mcp-api-analyzer (43KB) — Phase 1: API analysis
- mcp-server-builder (88KB) — Phase 2: Server build
- mcp-server-development (31KB) — TS MCP patterns
- mcp-app-designer (85KB) — Phase 3: Visual apps
- mcp-apps-integration (20KB) — structuredContent UI
- mcp-apps-official (48KB) — MCP Apps SDK
- mcp-apps-merged (39KB) — Combined apps reference
- mcp-localbosses-integrator (61KB) — Phase 4: LocalBosses wiring
- mcp-qa-tester (113KB) — Phase 5: Full QA framework
- mcp-deployment (17KB) — Phase 6: Production deploy
- mcp-skill (exa integration)

These skills are the encoded knowledge that lets agents build
production-quality MCP servers autonomously through the pipeline.
2026-02-06 06:36:37 -05:00

724 lines
20 KiB
Markdown

# MCP Server Blueprint — February 2026
**This is the definitive template for building production-ready MCP servers in 2026.**
Use this checklist for EVERY new MCP server. No skipping steps. These patterns ensure your server is:
- ✅ Usable (not just functional)
- ✅ Fast (lazy loading, efficient queries)
- ✅ Discoverable (labels, descriptions)
- ✅ Interactive (MCP Apps where appropriate)
- ✅ Debuggable (logging, progress)
- ✅ Production-ready (error handling, deployment)
---
## Phase 1: Planning (Before Writing Code)
### 1.1 Define Server Scope
- [ ] What API/service are you integrating?
- [ ] What are the 5-10 most important operations?
- [ ] Who is the target user? (developers, business users, etc.)
- [ ] What data is most frequently accessed?
### 1.2 Identify Tool Categories
Label your tools by category. Common patterns:
- [ ] **CRUD operations** (create, read, update, delete)
- [ ] **Search/Filter** (find data with queries)
- [ ] **Analytics/Reporting** (stats, dashboards, summaries)
- [ ] **Workflows** (multi-step operations)
- [ ] **Admin** (configuration, settings)
### 1.3 Identify UI Opportunities
Which operations benefit from visual display?
- [ ] **Data grids** — Contact lists, search results, tables
- [ ] **Dashboards** — Metrics, KPIs, analytics
- [ ] **Cards** — Detail views (invoices, opportunities, profiles)
- [ ] **Timelines** — Activity feeds, history
- [ ] **Forms** — Quick actions (booking, creating records)
- [ ] **Kanban** — Pipeline views, project boards
If you have 3+ UI opportunities, plan for MCP Apps.
---
## Phase 2: Core Server Setup
### 2.1 Project Structure
```bash
mkdir mcp-server-myservice
cd mcp-server-myservice
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node tsx fs-extra @types/fs-extra
```
### 2.2 File Structure
```
mcp-server-myservice/
├── src/
│ ├── index.ts # Main server (or server.ts)
│ ├── clients/
│ │ └── api-client.ts # API client
│ ├── apps/ # If using MCP Apps
│ │ └── index.ts # Apps manager
│ ├── ui/ # If using MCP Apps
│ │ ├── contact-grid.html
│ │ └── dashboard.html
│ └── types/
│ └── index.ts # Shared types
├── dist/ # Build output
├── scripts/
│ └── copy-ui.js # UI build script
├── package.json
├── tsconfig.json
├── .env.example
├── .gitignore
├── .npmignore
├── Dockerfile
├── railway.json
└── README.md
```
### 2.3 Package Configuration
```json
{
"name": "mcp-server-myservice",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"bin": {
"mcp-server-myservice": "dist/index.js"
},
"scripts": {
"build": "npm run build:ts && npm run build:ui",
"build:ts": "tsc",
"build:ui": "node scripts/copy-ui.js",
"dev": "tsx src/index.ts",
"start": "node dist/index.js"
},
"files": ["dist", "README.md", "LICENSE"],
"keywords": ["mcp", "mcp-server", "model-context-protocol", "myservice"]
}
```
---
## Phase 3: Tool Design (The Most Important Phase)
### 3.1 Tool Naming Convention
**Use:** `verb_noun` (snake_case)
**Avoid:** camelCase, PascalCase, kebab-case
**CRUD patterns:**
- `list_contacts` (with pagination + filters)
- `get_contact` (by ID)
- `create_contact` (returns created object)
- `update_contact` (partial updates)
- `delete_contact` (confirm before delete)
- `search_contacts` (full-text search if different from list)
**Other patterns:**
- `send_email`, `schedule_appointment`, `export_report`, `analyze_pipeline`
### 3.2 Tool Metadata & Labels ⭐ CRITICAL
Every tool MUST have `_meta` with labels:
```typescript
{
name: "search_contacts",
description: "Search contacts with filters. Returns paginated results.",
inputSchema: { /* ... */ },
_meta: {
labels: {
category: "contacts", // Group by feature
access: "read", // read | write | delete
complexity: "simple", // simple | complex | batch
},
},
}
```
**Label categories to use:**
- `category`: contacts, deals, analytics, calendar, email, admin, workflows
- `access`: read, write, delete
- `complexity`: simple (1 API call), complex (multiple calls), batch (loops)
- `sensitivity`: public, internal, confidential (optional)
### 3.3 Input Schemas — Best Practices
```typescript
inputSchema: {
type: "object" as const,
properties: {
// Always describe parameters
page: {
type: "number",
description: "Page number (default 1, starts at 1)"
},
pageSize: {
type: "number",
description: "Results per page (default 50, max 100)"
},
// Use enums for fixed options
status: {
type: "string",
description: "Filter by status",
enum: ["active", "inactive", "pending"],
},
// ISO 8601 for dates
createdAfter: {
type: "string",
description: "Filter created after (ISO 8601: 2026-02-03T14:00:00Z)"
},
},
// Mark required fields explicitly
required: ["contactId"],
}
```
### 3.4 Pagination (Mandatory for List Operations)
Every `list_` or `search_` tool MUST support pagination:
```typescript
{
name: "list_contacts",
description: "List contacts with pagination and filters",
inputSchema: {
type: "object" as const,
properties: {
page: { type: "number", description: "Page number (default 1)" },
pageSize: { type: "number", description: "Results per page (default 50, max 100)" },
query: { type: "string", description: "Search query (optional)" },
},
},
_meta: {
labels: { category: "contacts", access: "read", complexity: "simple" },
},
}
```
**In handler:**
```typescript
case "list_contacts": {
const { page = 1, pageSize = 50, query } = args;
const params = new URLSearchParams();
params.append("page", String(page));
params.append("pageSize", String(Math.min(Number(pageSize), 100))); // Cap at API max
if (query) params.append("query", query);
return await client.get(`/contacts?${params}`);
}
```
---
## Phase 4: Lazy-Loaded Resources ⭐ NEW
### 4.1 When to Use Resources vs Tools
**Use resources for:**
- Large datasets (contact lists, transaction history)
- Frequently changing data (real-time dashboards)
- Reference data (documentation, schemas)
- User-specific data (per-user settings, dashboards)
**Use tools for:**
- Operations with parameters (search, filter, create)
- One-time fetches
- Mutations (create, update, delete)
### 4.2 Resource Setup
```typescript
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
// Declare resources capability
const server = new Server(
{ name: "myservice-mcp", version: "1.0.0" },
{ capabilities: { tools: {}, resources: {} } } // ✅ Enable resources
);
// List available resources (metadata only)
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "myservice://contacts/all",
name: "All Contacts",
description: "Complete contact database (lazy-loaded)",
mimeType: "application/json",
},
{
uri: "myservice://analytics/dashboard",
name: "Analytics Dashboard",
description: "Real-time analytics data",
mimeType: "application/json",
},
],
};
});
// Fetch resource content on-demand
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
switch (uri) {
case "myservice://contacts/all": {
const contacts = await client.get("/contacts?limit=1000"); // Fetch when requested
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(contacts, null, 2),
}],
};
}
case "myservice://analytics/dashboard": {
const analytics = await client.get("/analytics/dashboard");
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(analytics, null, 2),
}],
};
}
default:
throw new Error(`Unknown resource: ${uri}`);
}
});
```
### 4.3 Resource Templates (Dynamic URIs)
```typescript
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resourceTemplates: [
{
uriTemplate: "myservice://contact/{id}",
name: "Contact Details",
description: "Full contact record by ID",
mimeType: "application/json",
},
],
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
const contactMatch = uri.match(/^myservice:\/\/contact\/(.+)$/);
if (contactMatch) {
const contactId = contactMatch[1];
const contact = await client.get(`/contacts/${contactId}`);
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(contact, null, 2),
}],
};
}
throw new Error(`Unknown resource: ${uri}`);
});
```
---
## Phase 5: MCP Apps (If Applicable)
### 5.1 Should You Build Apps?
Build MCP Apps if you have:
- ✅ Visual data (grids, cards, dashboards)
- ✅ 3+ UI opportunities identified in Phase 1
- ✅ Complex data relationships (better shown than described)
- ✅ Interactive workflows (drag-drop, forms)
Skip apps if:
- ❌ Simple CRUD operations only
- ❌ All operations return small JSON objects
- ❌ No visual benefit
### 5.2 App Architecture
See `mcp-apps-integration` skill for full details. Quick checklist:
- [ ] Create `src/apps/index.ts` — MCPAppsManager class
- [ ] Create `src/ui/` directory — HTML components
- [ ] Register resource handlers for UI files
- [ ] Add app tools with `_meta.ui.resourceUri`
- [ ] Implement `ListResourcesRequestSchema` handler
- [ ] Implement `ReadResourceRequestSchema` handler
- [ ] Add `build:ui` script to copy HTML to `dist/app-ui/`
### 5.3 App Tool Naming
**Pattern:** `view_` or `show_` prefix
```typescript
{
name: "view_contact_grid",
description: "Display contact search results in a data grid (visual UI component)",
inputSchema: { /* ... */ },
_meta: {
labels: { category: "contacts", access: "read", complexity: "simple" },
ui: { resourceUri: "ui://myservice/contact-grid" },
},
}
```
### 5.4 Common App Patterns
- **Contact Grid** — Search results table
- **Dashboard** — Multi-widget analytics view
- **Pipeline Board** — Kanban with drag-drop
- **Opportunity Card** — Detail view for single record
- **Calendar View** — Appointment/event calendar
- **Timeline** — Activity feed
Reference: 11 production GHL apps in `/Users/jakeshore/.clawdbot/workspace/mcp-diagrams/ghl-mcp-apps-only/`
---
## Phase 6: Progress & Logging
### 6.1 Progress Notifications (For Long Operations)
Any operation taking >5 seconds MUST send progress updates:
```typescript
if (name === "import_contacts") {
const progressToken = request.params._meta?.progressToken;
if (progressToken) {
await server.notification({
method: "notifications/progress",
params: {
progressToken,
progress: 0.3, // 30%
total: 1.0,
},
});
}
// ... do work
if (progressToken) {
await server.notification({
method: "notifications/progress",
params: { progressToken, progress: 1.0, total: 1.0 },
});
}
}
```
### 6.2 Structured Logging
Log important operations for debugging:
```typescript
import { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
await server.notification({
method: "notifications/message",
params: {
level: LoggingLevel.Info,
logger: "myservice",
data: {
operation: "create_contact",
contactId: newContact.id,
timestamp: new Date().toISOString(),
},
},
});
```
**When to log:**
- Info: Successful operations (create, update, delete)
- Warning: Rate limits, retries, fallbacks
- Error: API failures, validation errors
- Debug: Detailed request/response data (dev only)
---
## Phase 7: Error Handling (Production-Ready)
### 7.1 Tool Handler Error Wrapping
```typescript
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const result = await handleTool(client, name, args || {});
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
// Log the error
await server.notification({
method: "notifications/message",
params: {
level: LoggingLevel.Error,
logger: "myservice",
data: { tool: name, error: message },
},
});
return {
content: [{ type: "text", text: `Error: ${message}` }],
isError: true,
};
}
});
```
### 7.2 API Client Error Handling
```typescript
async request(endpoint: string, options: RequestInit = {}) {
const response = await fetch(url, options);
if (!response.ok) {
const errorText = await response.text();
// Parse API error if JSON
try {
const errorJson = JSON.parse(errorText);
throw new Error(
`API error: ${response.status} - ${errorJson.message || errorJson.error || errorText}`
);
} catch {
throw new Error(
`API error: ${response.status} ${response.statusText} - ${errorText}`
);
}
}
return response.json();
}
```
---
## Phase 8: Prompts (Optional but Recommended)
### 8.1 When to Add Prompts
Add prompts for:
- Common analysis workflows (e.g., "Analyze pipeline health")
- Report generation (e.g., "Generate contact summary")
- Quick actions (e.g., "Find overdue tasks")
- Data exploration (e.g., "Show top performers")
### 8.2 Prompt Implementation
```typescript
import { ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [
{
name: "contact_summary",
description: "Generate comprehensive contact summary with recent activity",
arguments: [
{ name: "contactId", description: "Contact ID", required: true },
],
},
],
};
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "contact_summary") {
const { contactId } = args;
const contact = await client.get(`/contacts/${contactId}`);
const activities = await client.get(`/contacts/${contactId}/activities`);
return {
description: `Summary for ${contact.name}`,
messages: [{
role: "user",
content: {
type: "text",
text: `Generate a comprehensive summary:\n\n${JSON.stringify({ contact, activities }, null, 2)}`,
},
}],
};
}
throw new Error(`Unknown prompt: ${name}`);
});
```
---
## Phase 9: Testing Checklist
### 9.1 Local Testing
- [ ] All tools compile without errors (`npm run build`)
- [ ] Server starts successfully (`npm start`)
- [ ] Environment variables validated on startup
- [ ] Test each tool in Claude Desktop
- [ ] Test pagination (page 1, page 2)
- [ ] Test error cases (invalid IDs, missing params)
- [ ] Test apps render correctly (if applicable)
- [ ] Check logs in Claude Desktop console
### 9.2 Performance Testing
- [ ] List operations return in <2 seconds
- [ ] Lazy-loaded resources only fetch when requested
- [ ] No unnecessary API calls
- [ ] Pagination caps at API maximum
- [ ] Progress notifications for operations >5 seconds
---
## Phase 10: Documentation
### 10.1 README.md Structure
```markdown
# MCP Server for MyService
MCP integration for MyService. Enables Claude Desktop to [core value prop].
## Features
- ✅ List/search/CRUD contacts
- ✅ Analytics dashboard (MCP App)
- ✅ Pipeline visualization (MCP App)
- ✅ Progress tracking for imports
## Installation
[npx / manual / docker options]
## Configuration
[Environment variables with .env.example]
## Available Tools
[List of tools with descriptions]
## MCP Apps (Rich UI)
[List of app tools with screenshots]
## Development
[Build/dev instructions]
```
### 10.2 .env.example
```bash
# MyService API Credentials
MY_SERVICE_API_KEY=your_api_key_here
MY_SERVICE_API_SECRET=your_secret_here
# Optional: Override base URL
# MY_SERVICE_BASE_URL=https://sandbox.api.myservice.com
# Optional: Logging
# LOG_LEVEL=debug
```
---
## Phase 11: Deployment
### 11.1 Docker
- [ ] Multi-stage Dockerfile
- [ ] .dockerignore file
- [ ] Test build locally
- [ ] Test run locally
### 11.2 Railway
- [ ] railway.json with build + start commands
- [ ] Environment variables documented
- [ ] Test deployment
### 11.3 npm Publishing
- [ ] `bin` field in package.json
- [ ] `files` field includes only dist/
- [ ] .npmignore excludes src/, .env
- [ ] Keywords for discoverability
- [ ] Test `npx` installation locally
### 11.4 GitHub
- [ ] README.md complete
- [ ] LICENSE file
- [ ] .gitignore excludes node_modules, dist, .env
- [ ] GitHub Actions for CI/CD (optional)
---
## Production Checklist (Final Review)
### Code Quality
- [ ] All tools have `_meta.labels`
- [ ] All parameters have descriptions
- [ ] Required fields marked explicitly
- [ ] Pagination implemented for list operations
- [ ] Error handling in all tool handlers
- [ ] No hardcoded API keys or secrets
- [ ] Logging for important operations
### Features
- [ ] Lazy-loaded resources for large datasets
- [ ] Progress notifications for long operations (>5s)
- [ ] MCP Apps for visual data (if applicable)
- [ ] Prompts for common workflows (if applicable)
### Documentation
- [ ] README with installation instructions
- [ ] .env.example with all required variables
- [ ] Tool descriptions clear and helpful
- [ ] Examples in README
### Deployment
- [ ] Compiles without errors
- [ ] Runs in Claude Desktop
- [ ] Docker image builds (if using Docker)
- [ ] Railway deploys successfully (if using Railway)
- [ ] npm package installs via npx (if publishing)
---
## Anti-Patterns to Avoid
**No labels on tools** — Always add `_meta.labels`
**Loading all data upfront** — Use lazy-loaded resources
**No pagination** — Every list operation needs page/pageSize
**Silent failures** — Always log errors and return clear messages
**No progress for slow ops** — Add progress notifications for >5s operations
**Building apps when not needed** — Only build apps if visually beneficial
**Missing descriptions** — Every parameter needs a description
**No environment validation** — Check env vars on startup
**Skipping error handling** — Wrap all tool handlers in try-catch
**Generic error messages** — Be specific ("Contact not found" not "Error")
---
## Reference Materials
- **Skills:**
- `mcp-server-development` — Full TypeScript patterns
- `mcp-apps-integration` — MCP Apps guide
- `mcp-deployment` — Docker/Railway/npm
- **Example Servers:**
- `/Users/jakeshore/.clawdbot/workspace/mcp-diagrams/mcp-servers/`
- 30 production servers with all patterns
- **Example Apps:**
- `/Users/jakeshore/.clawdbot/workspace/mcp-diagrams/ghl-mcp-apps-only/`
- 11 production apps with UI components
---
## TL;DR — The Golden Rules
1. **Labels on every tool** — category, access, complexity
2. **Lazy-load large datasets** — Use resources, not tools
3. **Paginate everything** — page/pageSize on all lists
4. **Progress for slow ops** — >5 seconds = progress notifications
5. **Apps for visual data** — Grids, dashboards, cards, timelines
6. **Log important operations** — Info, Warning, Error levels
7. **Handle errors gracefully** — Clear messages, no silent failures
8. **Document thoroughly** — README, .env.example, descriptions
9. **Test before shipping** — All tools work in Claude Desktop
10. **Deploy with confidence** — Docker, Railway, npm ready to go
**Follow this blueprint and your MCP servers will be production-ready, usable, and optimized for February 2026.**