skills/mcp-builder/SKILL.md
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP/MCP SDK) or Node/TypeScript (MCP SDK). Covers tool design, output schemas, Streamable HTTP transport, authentication patterns, evaluation creation, and common debugging.
npx skillsauth add ckorhonen/claude-skills mcp-builderInstall 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.
Create MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. The quality of an MCP server is measured by how well it enables LLMs to accomplish real-world tasks.
Creating a high-quality MCP server involves four main phases:
API Coverage vs. Workflow Tools: Balance comprehensive API endpoint coverage with specialized workflow tools. Workflow tools can be more convenient for specific tasks, while comprehensive coverage gives agents flexibility to compose operations. Performance varies by client—some clients benefit from code execution that combines basic tools, while others work better with higher-level workflows. When uncertain, prioritize comprehensive API coverage.
Tool Naming and Discoverability:
Clear, descriptive tool names help agents find the right tools quickly. Use consistent prefixes (e.g., github_create_issue, github_list_repos) and action-oriented naming.
Context Management: Agents benefit from concise tool descriptions and the ability to filter/paginate results. Design tools that return focused, relevant data. Some clients support code execution which can help agents filter and process data efficiently.
Actionable Error Messages: Error messages should guide agents toward solutions with specific suggestions and next steps.
Navigate the MCP specification:
Start with the sitemap to find relevant pages: https://modelcontextprotocol.io/sitemap.xml
Then fetch specific pages with .md suffix for markdown format (e.g., https://modelcontextprotocol.io/specification/draft.md).
Key pages to review:
Recommended stack:
authorization.md for the canonical OAuth flow.Load framework documentation:
For TypeScript (recommended):
https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.mdFor Python:
https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.mdUnderstand the API: Review the service's API documentation to identify key endpoints, authentication requirements, and data models. Use web search and WebFetch as needed.
Tool Selection: Prioritize comprehensive API coverage. List endpoints to implement, starting with the most common operations.
See language-specific guides for project setup:
Create shared utilities:
For each tool:
Input Schema:
Output Schema:
outputSchema where possible for structured datastructuredContent in tool responses (TypeScript SDK feature)Tool Description:
Implementation:
Annotations:
readOnlyHint: true/falsedestructiveHint: true/falseidempotentHint: true/falseopenWorldHint: true/falseReview for:
TypeScript:
npm run build to verify compilationnpx @modelcontextprotocol/inspectorPython:
python -m py_compile your_server.pySee language-specific guides for detailed testing approaches and quality checklists.
After implementing your MCP server, create comprehensive evaluations to test its effectiveness.
Load ✅ Evaluation Guide for complete evaluation guidelines.
Use evaluations to test whether LLMs can effectively use your MCP server to answer realistic, complex questions.
To create effective evaluations, follow the process outlined in the evaluation guide:
Ensure each question is:
Create an XML file with this structure:
<evaluation>
<qa_pair>
<question>Find discussions about AI model launches with animal codenames. One model needed a specific safety designation that uses the format ASL-X. What number X was being determined for the model named after a spotted wild cat?</question>
<answer>3</answer>
</qa_pair>
<!-- More qa_pairs... -->
</evaluation>
MCP servers can be exposed to the internet and handle sensitive operations. Follow these guidelines:
// Use Zod with strict schemas
const DeleteUserSchema = z.object({
userId: z.string().uuid(), // UUID format enforced
reason: z.enum(["spam", "policy_violation", "user_request"]), // Enum limits
});
// Never allow arbitrary code execution
const QuerySchema = z.object({
table: z.enum(["users", "orders", "products"]), // No arbitrary tables
id: z.number().int().positive(),
});
// Add rate limiting to destructive operations
const rateLimiter = new Map<string, number[]>();
function checkRateLimit(clientId: string, limit: number, windowMs: number): boolean {
const now = Date.now();
const calls = rateLimiter.get(clientId) ?? [];
const windowCalls = calls.filter(t => now - t < windowMs);
if (windowCalls.length >= limit) return false;
rateLimiter.set(clientId, [...windowCalls, now]);
return true;
}
// Log all tool calls for sensitive operations
server.registerTool("delete_record", {
// ...
handler: async ({ id }) => {
console.error(JSON.stringify({ // stderr for logging
event: "tool_call",
tool: "delete_record",
params: { id },
timestamp: new Date().toISOString(),
}));
// ... implementation
}
});
MCP server development encounters several predictable failure modes. These gotchas represent real problems discovered during implementation.
Symptoms:
npm start fails with exit code 1Root Causes:
.npmrc or tsconfig.jsonDebugging Commands:
# Verify dependencies are installed
npm list @modelcontextprotocol/sdk
# Check if port is in use
lsof -i :3000 # Replace 3000 with your port
netstat -an | grep LISTEN | grep 3000
# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
# Run with verbose output to see initialization errors
npm start -- --debug
NODE_DEBUG=* npm start # Maximum verbosity for Node.js internals
# For TypeScript, verify compilation first
npm run build
npx tsc --noEmit # Check for type errors without building
# Check for missing env variables
env | grep MCP # See what MCP-related vars are set
Prevention:
npm install after generating project.env.examplenpm run buildSymptoms:
Root Causes:
type annotationsDebugging Commands:
# Validate schema syntax
npx @modelcontextprotocol/inspector # GUI debugger, shows schema errors clearly
# Test schema compilation directly (TypeScript)
cat > test-schema.ts << 'EOF'
import { z } from 'zod';
const schema = z.object({
// your schema here
});
console.log(JSON.stringify(schema.safeParse({}), null, 2));
EOF
npx ts-node test-schema.ts
# For Python, test Pydantic directly
python3 << 'EOF'
from pydantic import BaseModel
class MyTool(BaseModel):
# your fields here
pass
print(MyTool.model_json_schema())
EOF
# Print actual schema being sent to client
# In your server code, log before registration:
console.log(JSON.stringify(toolSchema, null, 2));
# Test with invalid inputs to see error messages
# In MCP Inspector, try calling tool with:
# - Missing required fields
# - Wrong types (string instead of number)
# - Values outside constraints
Prevention:
strict: true in tsconfig.json)npm run buildSymptoms:
Root Causes:
Debugging Commands:
# Verify environment variables are accessible
node -e "const k = process.env.API_KEY; console.log('Key set:', !!k, '| Length:', k?.length, '| Prefix:', k?.substring(0, 4) + '***')"
# Check if file-based credentials exist
test -f ~/.aws/credentials && echo "AWS creds found" || echo "AWS creds missing"
test -f ~/.config/github-cli/hosts.yml && echo "GitHub token found" || echo "GitHub token missing"
# Validate API key format before use
node -e "const key = process.env.API_KEY; console.log('Key length:', key?.length); console.log('Key prefix:', key?.substring(0, 10));"
# Test API authentication directly
curl -H "Authorization: Bearer $API_KEY" https://api.example.com/v1/test
curl -H "X-API-Key: $API_KEY" https://api.example.com/v1/test # Alternative header
# Check OAuth token expiration
node -e "const payload = JSON.parse(Buffer.from(process.env.OAUTH_TOKEN.split('.')[1], 'base64url').toString()); console.log('Expires:', new Date(payload.exp * 1000));"
# For deployed servers, check what environment was actually loaded
# Add this to your server initialization:
console.error('Auth check:', {
hasApiKey: !!process.env.API_KEY,
keyLength: process.env.API_KEY?.length,
keyPrefix: process.env.API_KEY?.substring(0, 10) + '***'
});
Prevention:
.env.example with placeholder values for all required credentials.env file in developmentcurl with credentials firstSymptoms:
Root Causes:
Debugging Commands:
# For stdio transport, test directly
npx @modelcontextprotocol/inspector stdio node dist/index.js
# For HTTP transport, verify server is listening
lsof -i -n -P | grep LISTEN # See all listening ports
curl http://localhost:3000/ -v # Try to reach server directly
# Monitor server startup time
time npm start # Measure total startup duration
# Check if process is using all CPU (deadlock indicator)
top -n1 | grep node # See if CPU% is stuck at 100
# For stdio issues, run server directly to see output
node dist/index.js # If it hangs here, initialization is blocking
# Try interrupt (Ctrl+C) to see if it's truly stuck
# Check for port binding issues
ss -tlnp | grep 3000 # Linux
netstat -tlnp | grep 3000 # Linux alternative
lsof -i TCP:3000 # macOS/BSD
# Test server connectivity with timeout
timeout 5 curl -v http://localhost:3000/ || echo "Connection failed or timeout"
Prevention:
GET /health returns 200 OKnpx @modelcontextprotocol/inspector stdio <command>/health and test it independentlyWhen your MCP server isn't working:
Check logs first: Most failures are logged. Run with verbose output:
NODE_DEBUG=* npm start 2>&1 | head -50
Use MCP Inspector: Start it independently and select your server:
npx @modelcontextprotocol/inspector
It shows schema errors, connection issues, and tool execution failures clearly.
Test in isolation: Before testing with a client, verify server works standalone:
npm run build && node dist/index.js
Check external dependencies: Verify API credentials, network access, firewall:
curl -v https://api.example.com/ # Does API respond?
Add instrumentation: When stuck, add logging to understand what's happening:
server.setRequestHandler(Tool, async (request) => {
console.error('Tool called:', request.params.name);
try {
// implementation
console.error('Tool succeeded');
} catch (error) {
console.error('Tool failed:', error);
throw error;
}
});
Load these resources as needed during development:
https://modelcontextprotocol.io/sitemap.xml, then fetch specific pages with .md suffixhttps://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.mdhttps://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md🐍 Python Implementation Guide - Complete Python/FastMCP guide with:
@mcp.tool⚡ TypeScript Implementation Guide - Complete TypeScript guide with:
server.registerTooldocumentation
Create or expand an Idea.md / IDEA.md file from a rough description, existing repo, conversation history, notes, or other early-stage product inputs. Use when the user asks to "write an Idea.md", "turn this into an idea file", "capture this product idea", "expand this concept", or wants a repo-grounded concept brief before validation, PRD, or implementation work.
development
Write structured implementation plans from specs or requirements before touching code. Use when given a spec, requirements doc, or feature description, when user says "plan this out", "write a plan for", "how should we implement", or before starting any multi-step coding task.
testing
Expert guidance for video editing with ffmpeg, encoding best practices, and quality optimization. Use when working with video files, transcoding, remuxing, encoding settings, color spaces, or troubleshooting video quality issues.
development
Opinionated constraints for building better interfaces with agents. Use when building UI components, implementing animations, designing layouts, reviewing frontend accessibility, or working with Tailwind CSS, motion/react, or accessible primitives like Radix/Base UI.