4.1 KiB
4.1 KiB
Foundation Build Agent Prompt
Build the foundation for the {{NAME}} MCP server at {{DIR}}.
What to Build
1. package.json
{
"name": "@mcpengine/{{NAME}}",
"version": "1.0.0",
"type": "module",
"main": "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"
}
}
2. 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"]
}
3. src/types/index.ts
- Define TypeScript interfaces for ALL API entities
- Use branded types for IDs:
type CustomerId = string & { __brand: 'CustomerId' } - Use discriminated unions for status fields
- Export everything
4. src/clients/{{NAME}}.ts
API client with:
- Auth: {{AUTH_TYPE}} (Bearer token / API key / OAuth2)
- Base URL: {{API_BASE}}
- Retry with exponential backoff (3 retries, 1s/2s/4s)
- Rate limit awareness (respect 429 + Retry-After header)
- Automatic pagination (abstract cursor/offset)
- Request interceptors for logging
- Response interceptors for error normalization
- Timeout: 30s default
- OAuth token auto-refresh if applicable
import axios, { AxiosInstance, AxiosError } from 'axios';
export class {{PascalName}}Client {
private client: AxiosInstance;
private rateLimitRemaining = Infinity;
private rateLimitReset = 0;
constructor(config: { apiKey?: string; accessToken?: string; baseUrl?: string }) {
this.client = axios.create({
baseURL: config.baseUrl || '{{API_BASE}}',
timeout: 30000,
headers: { /* auth headers */ }
});
// Response interceptor for rate limiting
this.client.interceptors.response.use(
(res) => {
this.rateLimitRemaining = parseInt(res.headers['x-ratelimit-remaining'] || 'Infinity');
this.rateLimitReset = parseInt(res.headers['x-ratelimit-reset'] || '0');
return res;
},
async (error: AxiosError) => {
if (error.response?.status === 429) {
const retryAfter = parseInt(error.response.headers['retry-after'] || '5');
await this.sleep(retryAfter * 1000);
return this.client.request(error.config!);
}
throw this.normalizeError(error);
}
);
}
// Paginated fetch helper
async paginate<T>(endpoint: string, params?: Record<string, unknown>): Promise<T[]> { ... }
// Retry with exponential backoff
private async withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> { ... }
}
5. src/server.ts
MCP server with:
- Lazy-loaded tool modules (dynamic import)
- Resource handlers for UI apps
- Structured error responses
- Request logging
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
// Lazy load tool modules
const toolModules = {
'customers': () => import('./tools/customers-tools.js'),
'orders': () => import('./tools/orders-tools.js'),
// ... etc
};
// Tools loaded on first call
const loadedTools = new Map<string, any>();
async function getToolHandler(category: string) {
if (!loadedTools.has(category)) {
const mod = await toolModules[category]();
loadedTools.set(category, mod);
}
return loadedTools.get(category);
}
6. src/main.ts
Entry point with:
- Environment validation (fail fast)
- Dual transport (stdio + HTTP/SSE)
- Graceful shutdown handlers
- Health check endpoint
7. .env.example + .gitignore + README.md
Rules
- DO NOT create tool files or app files — only foundation
- TSC must compile clean with
strict: true - Run
npm installandnpx tsc --noEmitto verify - Commit when done