MCP Builder Skill
Version: 1.2.0 Category: Development Last Updated: 2026-01-02
Overview
This skill teaches how to build high-quality MCP servers that allow large language models to interact with external services through well-designed tools.
Quick Start
Create and setup MCP server project
mkdir my-mcp-server && cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node
Create source directory
mkdir src
// src/index.ts - Minimal MCP Server import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
const server = new Server( { name: "my-server", version: "1.0.0" }, { capabilities: { tools: {} } } );
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [{ name: "hello_world", description: "Returns a greeting message", inputSchema: { type: "object", properties: { name: { type: "string" } }, required: ["name"] } }] }));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "hello_world") {
return { content: [{ type: "text", text: Hello, ${request.params.arguments?.name}! }] };
}
throw new Error(Unknown tool: ${request.params.name});
});
const transport = new StdioServerTransport(); await server.connect(transport);
When to Use
-
Creating integrations for Claude Code with external APIs
-
Building custom tooling for AI-assisted workflows
-
Extending Claude's capabilities with domain-specific tools
-
Automating interactions with third-party services
-
Building reusable MCP servers for team sharing
Four-Phase Development Process
Phase 1: Deep Research and Planning
Study MCP Design Principles:
-
Balance "API coverage vs. workflow tools"
-
Review MCP protocol at modelcontextprotocol.io
-
Learn framework specifics (TypeScript recommended)
-
Analyze target API endpoints
Key Questions:
-
What actions does the user want to perform?
-
What API endpoints are available?
-
Which operations are read-only vs destructive?
-
How should errors be handled?
Phase 2: Implementation
Project Setup (TypeScript):
mkdir my-mcp-server cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node
tsconfig.json:
{ "compilerOptions": { "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "outDir": "./dist", "strict": true, "esModuleInterop": true }, "include": ["src/**/*"] }
Basic Server Structure:
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
const server = new Server( { name: "my-server", version: "1.0.0" }, { capabilities: { tools: {} } } );
// List available tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "my_tool", description: "Description of what this tool does", inputSchema: { type: "object", properties: { param1: { type: "string", description: "First parameter" }, param2: { type: "number", description: "Second parameter" } }, required: ["param1"] } } ] }));
// Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params;
if (name === "my_tool") { // Tool implementation return { content: [{ type: "text", text: "Result" }] }; }
throw new Error(Unknown tool: ${name});
});
// Start server const transport = new StdioServerTransport(); await server.connect(transport);
Phase 3: Review and Test
Code Quality Checklist:
-
No code duplication
-
Full type coverage
-
Proper error handling
-
Input validation
-
Rate limiting (if needed)
Testing with MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.js
Phase 4: Create Evaluations
Generate 10 complex, realistic test questions:
-
Independent (no dependencies between questions)
-
Read-only (don't modify external state)
-
Verifiable (clear expected answers)
Key Recommendations
Language Choice
TypeScript preferred for:
-
High-quality SDK support
-
Good compatibility
-
Strong typing
Transport Selection
-
Streamable HTTP: Remote servers
-
stdio: Local servers
Tool Naming
Use consistent, action-oriented prefixes:
github_create_issue github_list_repos slack_send_message slack_list_channels
Error Messages
Provide actionable messages with specific next steps:
throw new Error(
Failed to fetch repository: ${error.message}. +
Check that the repository exists and you have access.
);
Tool Annotations
{ name: "delete_file", description: "Permanently delete a file", inputSchema: { /* ... */ }, annotations: { destructiveHint: true, readOnlyHint: false, confirmationHint: "Are you sure you want to delete this file?" } }
Advanced Patterns
Pagination
async function listAllItems(apiClient: Client): Promise<Item[]> { const items: Item[] = []; let cursor: string | undefined;
do { const response = await apiClient.list({ cursor, limit: 100 }); items.push(...response.items); cursor = response.nextCursor; } while (cursor);
return items; }
Rate Limiting
import Bottleneck from "bottleneck";
const limiter = new Bottleneck({ maxConcurrent: 1, minTime: 100 // 10 requests per second });
const rateLimitedFetch = limiter.wrap(fetch);
Authentication
const API_KEY = process.env.MY_API_KEY;
if (!API_KEY) { throw new Error( "MY_API_KEY environment variable is required. " + "Get your API key from https://example.com/settings" ); }
Integration with Claude Code
Add to claude_desktop_config.json:
{ "mcpServers": { "my-server": { "command": "node", "args": ["/path/to/dist/index.js"], "env": { "MY_API_KEY": "your-api-key" } } } }
Project Configuration (New in 2025)
.mcp.json for Team Sharing
Commit MCP server configs to your repository:
// .mcp.json (project root) { "mcpServers": { "project-db": { "command": "node", "args": ["./tools/mcp-server/dist/index.js"], "env": { "DB_PATH": "./data/project.db" } } } }
Scope changes (2025):
-
project scope -> Now called local (per-project)
-
global scope -> Now called user (user-wide)
-
New: Checked-in .mcp.json files for team sharing
Permission Wildcards
Use wildcard syntax for server permissions:
Allow all tools from a server
mcp__my-server__*
In settings.json allowlist
{ "permissions": { "allow": ["mcp__database__", "mcp__github__"] } }
Timeout Configuration
Set MCP server startup timeout (default: 30s)
export MCP_TIMEOUT=60000 # 60 seconds
Debug mode for troubleshooting
claude --mcp-debug
Usage Examples
Example 1: Database Query Tool
server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "query_database") { const { sql, params } = request.params.arguments; const result = await db.query(sql, params); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } });
Example 2: API Integration
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "fetch_weather") {
const { city } = request.params.arguments;
const response = await fetch(https://api.weather.com/v1/${city});
const data = await response.json();
return { content: [{ type: "text", text: Temperature: ${data.temp}F }] };
}
});
Best Practices
Do
-
Use TypeScript for type safety
-
Validate all inputs with Zod
-
Provide clear, actionable error messages
-
Use environment variables for secrets
-
Implement rate limiting for external APIs
-
Add tool annotations for destructive operations
Don't
-
Hardcode API keys or secrets
-
Skip input validation
-
Return raw error stack traces
-
Create overly complex tool schemas
-
Ignore rate limits on external services
Error Handling
Common Errors
Error Cause Solution
ECONNREFUSED
Server not running Start the MCP server process
Tool not found
Incorrect tool name Check ListToolsRequestSchema handler
Invalid arguments
Schema mismatch Validate against inputSchema
Authentication failed
Missing/invalid API key Set environment variable correctly
Timeout
Slow response Increase MCP_TIMEOUT or optimize handler
Error Template
try {
const result = await operation();
return { content: [{ type: "text", text: JSON.stringify(result) }] };
} catch (error) {
throw new Error(
Operation failed: ${error.message}. +
Please check your configuration and try again.
);
}
Execution Checklist
-
Project initialized with TypeScript and MCP SDK
-
tsconfig.json configured for ES2020/NodeNext
-
Server implements ListToolsRequestSchema handler
-
Server implements CallToolRequestSchema handler
-
Input validation with Zod schemas
-
Error handling with actionable messages
-
Environment variables for sensitive config
-
Tested with MCP Inspector
-
Added to claude_desktop_config.json or .mcp.json
-
Documentation complete
Metrics
Metric Target Description
Response Time <500ms Tool execution latency
Error Rate <1% Percentage of failed tool calls
Type Coverage 100% TypeScript strict mode compliance
Test Coverage
80% Unit test coverage
Security Best Practices
-
Use trusted servers - Only from official/verified sources
-
Least privilege - Grant minimal required permissions
-
Audit logs - Maintain comprehensive access logs
-
Environment isolation - Use containers for high-risk automation
-
No hardcoded secrets - Always use environment variables
Resources
-
MCP Documentation: https://modelcontextprotocol.io
-
MCP TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
-
MCP Inspector: https://github.com/modelcontextprotocol/inspector
-
Top MCP Servers: https://mcpcat.io/guides/best-mcp-servers-for-claude-code/
Related Skills
-
yaml-workflow-executor - Workflow automation
-
webapp-testing - Testing web integrations
-
git-worktree-workflow - Parallel development
Version History
-
1.2.0 (2026-01-02): Upgraded to SKILL_TEMPLATE_v2 format with Quick Start, Error Handling, Metrics, Execution Checklist
-
1.1.0 (2025-12-30): Added .mcp.json project config, permission wildcards, MCP_TIMEOUT, security best practices
-
1.0.0 (2025-10-15): Initial release with four-phase development process