mcp-builder

v2026.09.24

Guide developers in creating Model Context Protocol (MCP) servers. Use for building MCP tools that enable LLMs to interact with external services. Covers TypeScript (primary) and Python FastMCP (secondary), tool annotations, Zod/Pydantic validation, and evaluation question creation.

GitHub
安装命令
npx skhub add oimiragieo/mcp-builder
Markdown
SKILL.md

MCP Server Development Guide

"The quality of an MCP server is measured by how well it enables LLMs to accomplish real-world tasks."

Overview

This skill guides you through creating Model Context Protocol (MCP) servers — tools that enable LLMs to interact with external services. MCP servers expose capabilities as typed tools that agents can discover and invoke.

When to Invoke

  • User asks to "build an MCP server" or "create MCP tools"
  • Integrating an external API with Claude
  • Exposing local system capabilities (files, databases, services) to an agent
  • Creating tools for an agent to call

Four-Phase Development Workflow

Phase 1: Deep Research and Planning

Before writing any code, understand the target API or service thoroughly.

  1. Study MCP protocol documentation

    • Visit https://modelcontextprotocol.io/sitemap.xml for current docs
    • Understand tool definitions, resource types, and prompt patterns
  2. Analyze the target service

    • What operations does the API support?
    • Which operations are read-only vs destructive?
    • What are the authentication requirements?
    • What are the rate limits and pagination patterns?
  3. Design tool coverage

    • Balance: API coverage (breadth) vs workflow tools (depth, combining multiple API calls)
    • Prefer workflow tools for common agent tasks
    • Design tool names as snake_case verb-noun pairs: get_user, create_issue, list_repos
  4. Choose transport

    • Remote servers: Streamable HTTP transport
    • Local servers: stdio transport
    • Default to stdio unless there's a specific need for HTTP

Phase 2: Implementation

Language Selection

TypeScript (recommended) — high-quality SDK, strong type safety, better IDE support:

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({
  name: 'my-service',
  version: '1.0.0',
});

Python (FastMCP) — simpler syntax, good for Python-native services:

from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel

mcp = FastMCP("my-service")

Project Structure (TypeScript)

mcp-server/
├── src/
│   ├── index.ts          # Server entry point
│   ├── client.ts         # API client + auth
│   ├── tools/
│   │   ├── read.ts       # Read-only tools
│   │   └── write.ts      # Mutating tools
│   └── utils/
│       ├── pagination.ts
│       └── errors.ts
├── package.json
├── tsconfig.json
└── README.md

Tool Annotations (MANDATORY)

Every tool MUST declare intent annotations:

server.tool(
  'get_repository',
  'Fetch a GitHub repository by owner and name',
  {
    owner: z.string().describe('Repository owner (username or org)'),
    repo: z.string().describe('Repository name'),
  },
  {
    // Annotations
    readOnlyHint: true, // Does not modify external state
    destructiveHint: false, // Not destructive
    idempotentHint: true, // Safe to call multiple times
    openWorldHint: true, // Makes external network calls
  },
  async ({ owner, repo }) => {
    // implementation
  }
);

Annotation reference:

AnnotationTypeMeaning
readOnlyHintbooleanDoes not modify external state
destructiveHintbooleanMay destroy data irreversibly
idempotentHintbooleanMultiple identical calls have same effect
openWorldHintbooleanInteracts with external systems (network, disk)

Input Validation with Zod (TypeScript)

// Good: explicit, descriptive schemas
const schema = {
  query: z.string().min(1).max(500).describe('Search query'),
  limit: z.number().int().min(1).max(100).default(20).describe('Max results'),
  cursor: z.string().optional().describe('Pagination cursor from previous call'),
};

Input Validation with Pydantic (Python)

from pydantic import BaseModel, Field
from typing import Optional

class SearchParams(BaseModel):
    query: str = Field(..., min_length=1, max_length=500, description="Search query")
    limit: int = Field(20, ge=1, le=100, description="Max results")
    cursor: Optional[str] = Field(None, description="Pagination cursor")

Response Format

Include both text (for human-readable output) and structured data (for agent parsing):

return {
  content: [
    {
      type: 'text',
      text: `Found ${results.length} results for "${query}"`,
    },
    {
      type: 'text',
      text: JSON.stringify(results, null, 2),
    },
  ],
};

Shared Utilities

Always build these utilities before implementing tools:

  1. API client — centralized HTTP client with auth headers, retry logic
  2. Error handler — converts API errors to MCP-safe error messages
  3. Pagination helper — cursor/offset/page-based pagination abstraction

Error Handling Pattern

try {
  const result = await apiClient.getResource(id);
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
} catch (error) {
  if (error instanceof NotFoundError) {
    return {
      content: [{ type: 'text', text: `Resource ${id} not found` }],
      isError: true,
    };
  }
  throw error; // Re-throw unexpected errors
}

Phase 3: Review and Test

Code Review Checklist

  • DRY: No duplicated API call logic across tools
  • All tools have meaningful descriptions
  • All inputs have describe() annotations
  • All tools have intent annotations (readOnlyHint etc.)
  • Error messages are user-friendly, not stack traces
  • TypeScript: full type coverage, no any types
  • Python: all functions typed, Pydantic models for inputs

Build Verification

TypeScript:

npm run build            # Must succeed with 0 errors
npx tsc --noEmit        # Type-check without output

Python:

python -m py_compile src/server.py   # Syntax check
mypy src/                             # Type check

Testing with MCP Inspector

# TypeScript
npx @modelcontextprotocol/inspector node dist/index.js

# Python
npx @modelcontextprotocol/inspector python src/server.py

Use MCP Inspector to:

  1. Verify all tools appear with correct descriptions
  2. Test each tool with sample inputs
  3. Verify error handling with invalid inputs
  4. Check response format is parseable

Phase 4: Create Evaluations

Design 10 independent evaluation questions per server. These test whether the MCP server enables real-world tasks.

Evaluation question requirements:

  • Each question is independent (no prior context needed)
  • Questions use realistic scenarios an agent would encounter
  • Questions are specific enough to have verifiable answers
  • Mix of simple (1 tool call) and compound (2-3 tool calls) tasks
  • At least 3 questions test read-only operations
  • At least 2 questions test error handling

Output format:

<evaluations>
  <question id="1">
    <task>Get the description of the 'anthropics/claude-code' repository</task>
    <expected_tools>get_repository</expected_tools>
    <answer>Claude Code is Anthropic's official CLI for Claude</answer>
  </question>
  <question id="2">
    <task>List the open issues labeled 'bug' in 'anthropics/claude-code', limit to 5</task>
    <expected_tools>list_issues</expected_tools>
    <answer>Returns up to 5 open issues with 'bug' label</answer>
  </question>
  <!-- ... 8 more questions -->
</evaluations>

Memory Protocol

Before starting MCP server development:

cat .claude/context/memory/learnings.md | grep -i "mcp\|model context"
cat .claude/context/memory/issues.md | grep -i "mcp\|model context"

After completing the server, record findings:

  • Working patterns → .claude/context/memory/learnings.md
  • API integration gotchas → .claude/context/memory/issues.md
  • Architecture decisions → .claude/context/memory/decisions.md

Official MCP Server Templates

The modelcontextprotocol/servers repository provides production-ready reference implementations. Use these as starting templates before building custom servers.

PostgreSQL MCP Server

# Install and run directly
npx -y @modelcontextprotocol/server-postgres postgresql://localhost/mydb

Claude Desktop config:

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"]
    }
  }
}

Tools provided: query (read-only SQL), list_tables, describe_table

Key implementation patterns from the reference server:

// Read-only query tool pattern
server.tool(
  'query',
  'Run a read-only SQL query',
  { sql: z.string().describe('SQL SELECT statement') },
  { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
  async ({ sql }) => {
    // Enforce read-only by setting transaction to read-only
    const result = await pool.query('BEGIN READ ONLY; ' + sql + '; COMMIT');
    return {
      content: [{ type: 'text', text: JSON.stringify(result.rows, null, 2) }],
    };
  }
);

SQLite MCP Server

# Install and run
npx -y @modelcontextprotocol/server-sqlite /path/to/database.db

Claude Desktop config:

{
  "mcpServers": {
    "sqlite": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/database.db"]
    }
  }
}

Tools provided: read_query, write_query, create_table, list_tables, describe_table, insert_row, delete_rows

Schema introspection pattern:

server.tool(
  'describe_table',
  'Get the schema for a table',
  { table_name: z.string().describe('Name of the table') },
  { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
  async ({ table_name }) => {
    const rows = db.prepare(`PRAGMA table_info(?)`).all(table_name);
    return {
      content: [{ type: 'text', text: JSON.stringify(rows, null, 2) }],
    };
  }
);

Filesystem MCP Server

npx -y @modelcontextprotocol/server-filesystem /allowed/path

Tools provided: read_file, write_file, list_directory, create_directory, move_file, search_files, get_file_info

Path safety pattern (copy this for any filesystem tool):

function validatePath(requestedPath: string, allowedDir: string): string {
  const resolved = path.resolve(requestedPath);
  if (!resolved.startsWith(path.resolve(allowedDir))) {
    throw new Error(`Path ${requestedPath} is outside allowed directory`);
  }
  return resolved;
}

GitHub MCP Server

npx -y @modelcontextprotocol/server-github
# Requires: GITHUB_PERSONAL_ACCESS_TOKEN env var

Tools provided: create_repository, get_file_contents, push_files, create_issue, create_pull_request, search_repositories, search_code, fork_repository, list_commits

When to Use Official Servers vs Custom

ScenarioUse Official ServerBuild Custom
Standard PostgreSQL/SQLite accessYESNo
GitHub repo operationsYESNo
Custom API integrationNoYES
Business-specific logicNoYES
Combining multiple APIsNoYES
Existing service with SDKNoYES

Common Pitfalls

PitfallDescriptionFix
Missing annotationsTools without readOnlyHint etc.Always declare all 4 annotations
Overly broad toolsdo_everything(action, params)One tool per distinct operation
Missing input descriptionsquery: z.string()Always add .describe()
Leaking auth tokens in errorsLogging API key in error messageSanitize error messages
No paginationReturning all results at onceAdd cursor/limit to list operations
Blocking event loopSynchronous I/O in Node.jsAlways use async/await

Cross-IDE Agent Detection (Inspired by Skill_Seekers AgentDetector)

After building an MCP server, auto-detect which AI coding agents are installed on the user's system and generate the correct configuration for each. This eliminates the manual "copy this JSON into your settings" step.

Supported agents and their config locations:

AgentTransportConfig Path (Windows)Config Path (macOS/Linux)
Claude Codestdio~/.claude.json~/.claude.json
CursorHTTP%APPDATA%\Cursor\mcp_settings.json~/Library/Application Support/Cursor/mcp_settings.json
WindsurfHTTP%APPDATA%\Windsurf\mcp_config.json~/Library/Application Support/Windsurf/mcp_config.json
VS Code + Clinestdio%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json~/.config/Code/User/globalStorage/...
IntelliJ IDEAHTTP%APPDATA%\JetBrains\IntelliJIdea2024.3\mcp.xml~/Library/Application Support/JetBrains/...

Detection protocol:

  1. Check if each config file exists at the platform-appropriate path
  2. For each detected agent, generate the MCP server config snippet in the agent's format
  3. Present all detected agents to the user with one-click install instructions
  4. For agents using HTTP transport, include the default port and health check URL

Config generation template (Claude Code stdio example):

{
  "mcpServers": {
    "<server-name>": {
      "command": "node",
      "args": ["<path-to-server>/dist/index.js"],
      "env": {}
    }
  }
}

This step should be part of Phase 4 (Testing & Deployment) — after the server is built and tested, offer to install it into all detected agents.

Reference Documentation

  • MCP Protocol: https://modelcontextprotocol.io/
  • TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk
  • Python SDK: https://github.com/modelcontextprotocol/python-sdk
  • FastMCP: https://github.com/jlowin/fastmcp
  • MCP Inspector: npx @modelcontextprotocol/inspector

Related Skills

  • typescript-expert — TypeScript type system, SDK patterns
  • api-development-expert — REST API design, authentication patterns
  • tdd — Test-driven development for MCP tool testing
  • verification-before-completion — Pre-completion quality gates
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

未指定

源路径

.claude/skills/mcp-builder

默认分支

main

最新提交

64b580e

Tree SHA

42a1df4