skills/mcp-creator/SKILL.md
Expert MCP (Model Context Protocol) server developer creating safe, performant, production-ready servers with proper security, error handling, and developer experience. Activate on 'create MCP', 'MCP server', 'build MCP', 'custom tool server', 'MCP development', 'Model Context Protocol'. NOT for using existing MCPs (just invoke them), general API development (use backend-architect), or skills/agents without external state (use skill-coach/agent-creator).
npx skillsauth add curiositech/windags-skills mcp-creatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert in building production-ready Model Context Protocol servers with security boundaries, robust error handling, and excellent developer experience.
Tool complexity assessment:
├── Multiple API endpoints needed?
├── Yes → Design separate tools for each endpoint
│ └── Example: get_user, create_user, update_user
└── No → Single tool with action parameter
└── Example: manage_user(action: "get"|"create"|"update")
Tool execution pattern:
├── Operation takes >5 seconds?
├── Yes → Design as async with polling tool
│ └── start_analysis() → check_status() → get_results()
└── No → Direct synchronous tool
└── analyze_data() returns results immediately
External service interaction:
├── Requires authentication?
├── Yes → Bundle related operations in one MCP
│ └── Share auth config across tools
└── No → Consider standalone tools or scripts
Data access pattern:
├── Read-only structured data? → Use Resources
│ └── Templates, configs, documentation
├── Actions that modify state? → Use Tools
│ └── API calls, database writes, file creation
└── Interactive operations? → Use Tools with prompts
└── Guided workflows, form filling
Deployment context:
├── Local CLI integration?
└── Use StdioTransport (simplest, most secure)
├── Multiple client support needed?
└── Use SSE Transport (HTTP-based)
├── Custom protocol requirements?
└── Implement custom Transport class
└── Production server deployment?
└── SSE with proper auth middleware
Symptom: Tools accept any type or overly permissive schemas
Detection: If schema validation catches <90% of invalid inputs
Fix: Implement strict Zod schemas with constraints
// ❌ Detection rule: Schema too permissive
{ type: "object" }
// ✅ Fix: Strict validation
const schema = z.object({
id: z.string().uuid(),
count: z.number().min(1).max(100)
})
Symptom: Server becomes unresponsive after extended use Detection: If connection pools show >90% utilization or memory usage grows continuously Fix: Implement proper resource cleanup with try/finally blocks
// ❌ Detection: No cleanup
const client = await pool.connect();
const result = await client.query(sql);
// ✅ Fix: Guaranteed cleanup
const client = await pool.connect();
try {
return await client.query(sql);
} finally {
client.release();
}
Symptom: API keys visible in logs, error messages, or responses Detection: If grep finds credentials in logs or error responses Fix: Sanitize all outputs and use secure environment variable loading
// ❌ Detection rule: Secrets in error messages
throw new Error(`API call failed with key ${apiKey}`);
// ✅ Fix: Sanitized errors
throw new Error(`API call failed: ${error.message.replace(apiKey, '[REDACTED]')}`);
Symptom: External API returns 429 errors, causing tool failures Detection: If >10% of external API calls return rate limit errors Fix: Implement exponential backoff and circuit breaker pattern
// ❌ Detection: No rate limiting
await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } });
// ✅ Fix: Rate limiting with backoff
if (!rateLimiter.canProceed(url, 100, 60000)) {
await exponentialBackoff(() => fetch(url, options));
}
Symptom: Tools succeed but produce incorrect or incomplete results Detection: If tool success rate is >95% but user reports failures Fix: Implement structured error handling with proper propagation
// ❌ Detection rule: Silent failures
try {
await riskyOperation();
} catch (e) {
// Silent failure
}
// ✅ Fix: Proper error handling
try {
return await riskyOperation();
} catch (error) {
throw new McpError(ErrorCode.InternalError, `Operation failed: ${error.message}`);
}
Scenario: Create an MCP tool for querying a PostgreSQL database with user data.
Step 1 - Tool Design Decision Looking at requirements: multiple query types (get user, list users, search users). Following the decision tree:
Decision: Create database-mcp with tools: get_user, list_users, search_users
Step 2 - Security Schema Design
const getUserSchema = z.object({
userId: z.string().uuid(), // Prevent SQL injection
includeDeleted: z.boolean().default(false)
});
Novice mistake: Using z.string() without validation → allows SQL injection
Expert addition: UUID validation prevents malicious input
Step 3 - Connection Management
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 10, // Connection limit
idleTimeoutMillis: 30000
});
Novice mistake: Creating new connection per request → resource exhaustion Expert pattern: Connection pooling with limits
Step 4 - Error Handling Strategy
async function getUser(args: unknown) {
const { userId, includeDeleted } = getUserSchema.parse(args);
const client = await pool.connect();
try {
const query = includeDeleted
? 'SELECT * FROM users WHERE id = $1'
: 'SELECT * FROM users WHERE id = $1 AND deleted_at IS NULL';
const result = await client.query(query, [userId]);
if (result.rows.length === 0) {
return { content: [{ type: "text", text: JSON.stringify({ found: false }) }] };
}
return { content: [{ type: "text", text: JSON.stringify({
found: true,
user: result.rows[0]
}) }] };
} catch (error) {
throw new McpError(ErrorCode.InternalError, `Database query failed`);
} finally {
client.release(); // Always cleanup
}
}
Novice mistake: No connection cleanup → connection leaks Expert pattern: try/finally ensures cleanup even on error
Do NOT use MCP Creator for:
skill-coach insteadagent-creator insteadscript-writer insteadbackend-architect insteadDelegate to other skills when:
security-auditorsite-reliability-engineeragent-creator for integrationtools
Building resilient distributed systems with circuit breakers, retries with full-jitter exponential backoff, retry budgets (per-request 3-attempt + per-client 10% ratio per Google SRE), deadline propagation, and the cascading-failure math (4 layers × 3 retries = 64x amplification). Grounded in Resilience4j, Microsoft Cloud Patterns, AWS Architecture Blog (Marc Brooker), and Google SRE Book.
testing
Designing HTTP cache headers that work correctly across browsers, CDNs, and shared proxies — `Cache-Control` directives per RFC 9111, `stale-while-revalidate` and `stale-if-error` per RFC 5861, the Vary header for varying responses, and surrogate keys for tag-based purging. Grounded in IETF RFCs and Cloudflare/Fastly docs.
development
Use when designing or fixing a Content Security Policy on a real site, choosing between nonce-based and hash-based CSP, adding strict-dynamic, debugging "Refused to execute inline script" errors, deploying CSP in report-only mode first, configuring report-to / report-uri, or auditing an existing policy for unsafe-inline / unsafe-eval / wildcards. Triggers: "CSP blocks legitimate inline script", strict-dynamic, nonce-{RANDOM}, sha256-{HASH}, object-src none, base-uri none, frame-ancestors, Trusted Types, X-Content-Security-Policy obsolete, report-only vs enforced. NOT for general HTTP security headers (HSTS, COOP/COEP), Trusted Types deep dive, CORS configuration, or building a WAF.
tools
Choosing and operating an HTTP API versioning strategy that doesn't break clients — Stripe's date-based pinned versions, the Deprecation/Sunset header pair (RFC 9745 + RFC 8594), URI vs header vs media-type approaches, and the version-transformer pattern. Grounded in Stripe's published architecture and IETF RFCs.