SuperAPI AI Directory

mcp-server-builder

Agents & models

View repository

Updated 10-15 21:33

About this skill

MCP Server Builder Skill

This skill makes it easy to build Model Context Protocol (MCP) servers.

Overview

This skill automatically generates servers compliant with Anthropic's **Model Context Protocol (MCP)**. MCP is an open standard that allows LLMs (large language models) to dynamically interact with external tools, databases, and APIs through a standardized interface.

Key Features

  • Full-featured MCP server generation: Supports Python, TypeScript, C#, and Go
  • Implementation of the 3 MCP features: Tools、Resources、Prompts
  • Application of best practices: Logging, error handling, schema validation
  • Production-ready: Considers security, performance, and maintainability
  • Claude Desktop integration: Automatic configuration file generation
  • Includes test code: Comprehensive test suite
  • Debug Support: Logging and error tracing features
  • Automatic documentation generation: README, API specification, usage guide

What is MCP?

**Model Context Protocol (MCP)** is a standardized way to connect LLM applications and data sources.

The 3 main features of MCP

  1. Tools: Functions that LLMs can call

    • API calls, database queries, computational processing, etc.
  2. Resources: File-like data

    • Documents, configuration files, database contents, etc.
  3. Prompts: Predefined templates

    • Reusable prompts, workflow templates

Supported Languages

TypeScript

  • Recommended: Latest features are implemented first
  • SDK: @modelcontextprotocol/sdk
  • Validation: Zod (built-in)
  • Logging: pino、winston

Python

  • SDK: mcp
  • Validation: Pydantic
  • Logging: logging (standard library)
  • Asynchronous: asyncio

Other

  • C#: Official SDK available (released April 2025)
  • Go: Official SDK available (features lag behind)

Usage

Basic MCP server generation

MCPサーバーを作成してください:

名前: weather-server
言語: TypeScript
機能:
- 天気予報を取得するツール
- 天気アラートを取得するツール

Python server generation

Python MCPサーバーを作成:

名前: github-analyzer
機能:
- GitHubリポジトリを分析するツール
- コミット履歴を取得するリソース
- PR レビューテンプレートのプロンプト
依存関係: requests, PyGithub

Full-featured server

本番環境向けMCPサーバーを生成:

名前: database-manager
言語: TypeScript
Tools:
- データベースクエリ実行
- スキーマ情報取得
Resources:
- テーブル一覧
- インデックス情報
Prompts:
- SQLクエリ最適化
- スキーマ設計レビュー
認証: API Key
ロギング: 詳細
エラーハンドリング: 包括的
テスト: 含む

Generation Patterns

1. TypeScript server (basic)

Input:

TypeScript MCPサーバーを作成:
名前: calculator
ツール: add, subtract, multiply, divide

Generated file structure:

calculator-mcp/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts          # メインサーバー
│   ├── tools.ts          # ツール実装
│   └── types.ts          # 型定義
├── tests/
│   └── tools.test.ts     # テスト
├── README.md
└── .env.example

src/index.ts:

#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// ツールスキーマ定義
const AddArgsSchema = z.object({
  a: z.number().describe("First number"),
  b: z.number().describe("Second number"),
});

const server = new Server(
  {
    name: "calculator-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// ツール一覧を返す
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "add",
        description: "Add two numbers together",
        inputSchema: {
          type: "object",
          properties: {
            a: { type: "number", description: "First number" },
            b: { type: "number", description: "Second number" },
          },
          required: ["a", "b"],
        },
      },
      {
        name: "subtract",
        description: "Subtract second number from first",
        inputSchema: {
          type: "object",
          properties: {
            a: { type: "number" },
            b: { type: "number" },
          },
          required: ["a", "b"],
        },
      },
    ],
  };
});

// ツール実行ハンドラー
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "add": {
      const validated = AddArgsSchema.parse(args);
      const result = validated.a + validated.b;
      return {
        content: [
          {
            type: "text",
            text: `Result: ${validated.a} + ${validated.b} = ${result}`,
          },
        ],
      };
    }

    case "subtract": {
      const validated = AddArgsSchema.parse(args);
      const result = validated.a - validated.b;
      return {
        content: [
          {
            type: "text",
            text: `Result: ${validated.a} - ${validated.b} = ${result}`,
          },
        ],
      };
    }

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// サーバー起動
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);

  // CRITICAL: stderr にログ出力(stdout はプロトコル通信用)
  console.error("Calculator MCP server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error:", error);
  process.exit(1);
});

2. Python server (with resources)

Input:

Python MCPサーバーを作成:
名前: file-manager
機能:
- ファイル一覧取得(リソース)
- ファイル読み込み(ツール)
- ファイル書き込み(ツール)

Generated file structure:

file-manager-mcp/
├── pyproject.toml
├── src/
│   └── file_manager/
│       ├── __init__.py
│       ├── server.py       # メインサーバー
│       ├── tools.py        # ツール実装
│       └── resources.py    # リソース実装
├── tests/
│   └── test_server.py
└── README.md

src/file_manager/server.py:

import asyncio
import logging
from pathlib import Path
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import (
    Tool,
    Resource,
    TextContent,
    CallToolRequest,
    ListResourcesRequest,
    ListToolsRequest,
    ReadResourceRequest,
)
from pydantic import BaseModel, Field

# CRITICAL: stderr にログ出力(stdout はプロトコル通信用)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[logging.StreamHandler()],  # デフォルトで stderr
)
logger = logging.getLogger("file-manager")


class ReadFileArgs(BaseModel):
    """ファイル読み込み引数"""
    path: str = Field(description="Path to the file to read")


class WriteFileArgs(BaseModel):
    """ファイル書き込み引数"""
    path: str = Field(description="Path to the file to write")
    content: str = Field(description="Content to write to the file")


class FileManagerServer:
    def __init__(self, base_path: str = "."):
        self.base_path = Path(base_path)
        self.server = Server("file-manager")
        self._setup_handlers()

    def _setup_handlers(self):
        """ハンドラー登録"""

        @self.server.list_tools()
        async def list_tools() -> list[Tool]:
            return [
                Tool(
                    name="read_file",
                    description="Read contents of a file",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "path": {
                                "type": "string",
                                "description": "File path to read",
                            }
                        },
                        "required": ["path"],
                    },
                ),
                Tool(
                    name="write_file",
                    description="Write content to a file",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "path": {"type": "string"},
                            "content": {"type": "string"},
                        },
                        "required": ["path", "content"],
                    },
                ),
            ]

        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict) -> list[TextContent]:
            if name == "read_file":
                args = ReadFileArgs(**arguments)
                file_path = self.base_path / args.path

                if not file_path.exists():
                    raise ValueError(f"File not found: {args.path}")

                content = file_path.read_text()
                return [TextContent(type="text", text=content)]

            elif name == "write_file":
                args = WriteFileArgs(**arguments)
                file_path = self.base_path / args.path

                file_path.parent.mkdir(parents=True, exist_ok=True)
                file_path.write_text(args.content)

                return [
                    TextContent(
                        type="text",
                        text=f"Successfully wrote {len(args.content)} bytes to {args.path}",
                    )
                ]

            raise ValueError(f"Unknown tool: {name}")

        @self.server.list_resources()
        async def list_resources() -> list[Resource]:
            """ディレクトリ内のファイル一覧"""
            resources = []
            for file_path in self.base_path.rglob("*"):
                if file_path.is_file():
                    relative_path = file_path.relative_to(self.base_path)
                    resources.append(
                        Resource(
                            uri=f"file://{relative_path}",
                            name=str(relative_path),
                            mimeType="text/plain",
                            description=f"File: {relative_path}",
                        )
                    )
            return resources

        @self.server.read_resource()
        async def read_resource(uri: str) -> str:
            """リソース読み込み"""
            if not uri.startswith("file://"):
                raise ValueError(f"Invalid URI: {uri}")

            path = uri.replace("file://", "")
            file_path = self.base_path / path

            if not file_path.exists():
                raise ValueError(f"Resource not found: {uri}")

            return file_path.read_text()

    async def run(self):
        """サーバー起動"""
        async with stdio_server() as (read_stream, write_stream):
            logger.info("File Manager MCP server running")
            await self.server.run(
                read_stream,
                write_stream,
                self.server.create_initialization_options(),
            )


async def main():
    server = FileManagerServer()
    await server.run()


if __name__ == "__main__":
    asyncio.run(main())

3. Server with prompt features

Input:

MCPサーバーにプロンプト機能を追加:
名前: code-review-assistant
プロンプト:
- セキュリティレビュー
- パフォーマンスレビュー
- コード品質レビュー

Prompt implementation example (TypeScript):

import { ListPromptsRequestSchema, GetPromptRequestSchema } from "@modelcontextprotocol/sdk/types.js";

// プロンプト一覧
server.setRequestHandler(ListPromptsRequestSchema, async () => {
  return {
    prompts: [
      {
        name: "security-review",
        description: "Comprehensive security code review",
        arguments: [
          {
            name: "code",
            description: "Code to review",
            required: true,
          },
          {
            name: "language",
            description: "Programming language",
            required: false,
          },
        ],
      },
      {
        name: "performance-review",
        description: "Performance analysis and optimization suggestions",
        arguments: [
          {
            name: "code",
            description: "Code to analyze",
            required: true,
          },
        ],
      },
    ],
  };
});

// プロンプト取得
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "security-review") {
    const code = args?.code as string;
    const language = (args?.language as string) || "unknown";

    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Please perform a comprehensive security review of the following ${language} code:

\`\`\`${language}
${code}
\`\`\`

Check for:
1. SQL Injection vulnerabilities
2. XSS (Cross-Site Scripting) risks
3. Authentication and authorization issues
4. Data validation and sanitization
5. Encryption and hashing
6. Hardcoded secrets
7. CSRF protection
8. Input validation

Provide specific recommendations with code examples.`,
          },
        },
      ],
    };
  }

  if (name === "performance-review") {
    const code = args?.code as string;

    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: `Analyze the following code for performance issues:

\`\`\`
${code}
\`\`\`

Focus on:
1. Time complexity (Big O)
2. Unnecessary loops or nesting
3. Database query optimization (N+1 problems)
4. Memory leaks
5. Inefficient data structures
6. Caching opportunities
7. Async/await usage

Provide optimization suggestions with improved code examples.`,
          },
        },
      ],
    };
  }

  throw new Error(`Unknown prompt: ${name}`);
});

Best Practices

1. Logging (CRITICAL)

TypeScript:

// ❌ 絶対にNG: stdout はプロトコル通信用
console.log("Server started");

// ✅ 正しい: stderr にログ出力
console.error("Server started");

// ✅ ロガーライブラリ使用(推奨)
import pino from "pino";
const logger = pino({ level: "info" });
logger.info("Server started");

Python:

# ✅ 標準loggingライブラリ(デフォルトで stderr)
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("Server started")

2. Schema validation

TypeScript (Zod):

import { z } from "zod";

const UserSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
});

// 検証
const validated = UserSchema.parse(args);

Python (Pydantic):

from pydantic import BaseModel, EmailStr, Field

class User(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    email: EmailStr
    age: int | None = Field(None, gt=0)

# 検証
user = User(**args)

3. Error Handling

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    const { name, arguments: args } = request.params;

    // 入力検証
    if (!name) {
      throw new Error("Tool name is required");
    }

    // ツール実行
    const result = await executeTool(name, args);

    return {
      content: [{ type: "text", text: JSON.stringify(result) }],
    };
  } catch (error) {
    // エラーログ(stderr)
    console.error("Tool execution failed:", error);

    // ユーザーフレンドリーなエラーメッセージ
    if (error instanceof z.ZodError) {
      throw new Error(`Validation failed: ${error.message}`);
    }

    throw error;
  }
});

4. Tool registration timing

// ✅ 正しい: トランスポート接続前にツール登録
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return { tools: [...] };
});

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  // ツール実行
});

// この後で接続
await server.connect(transport);

5. Asynchronous processing

Python:

# ✅ async/await を適切に使用
async def call_tool(name: str, arguments: dict):
    if name == "fetch_data":
        # 非同期API呼び出し
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                data = await response.json()
                return [TextContent(type="text", text=str(data))]

Claude Desktop Integration

Configuration file generation

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "calculator": {
      "command": "node",
      "args": ["/path/to/calculator-mcp/dist/index.js"]
    },
    "file-manager": {
      "command": "python",
      "args": ["-m", "file_manager.server"],
      "env": {
        "BASE_PATH": "/Users/username/documents"
      }
    }
  }
}

Startup and verification

  1. Restart Claude Desktop
  2. In chat /mcp Type to check the server
  3. Use the tool: "Use the calculator to add 5 and 3"

Debugging Methods

TypeScript

// 詳細ログ有効化
const logger = pino({ level: "debug" });

// リクエスト/レスポンスのログ
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  logger.debug({ request }, "Received tool call");

  const result = await executeTool(request.params.name, request.params.arguments);

  logger.debug({ result }, "Tool call completed");
  return result;
});

Python

# デバッグレベルのログ
logging.basicConfig(level=logging.DEBUG)

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    logger.debug(f"Tool call: {name} with args {arguments}")
    result = await execute_tool(name, arguments)
    logger.debug(f"Tool result: {result}")
    return result

Troubleshooting

Problem: Server does not start

  • Cause: Logging output to stdout
  • Solution: Use console.error or stderr

Problem: Tool not found

  • Cause: Tool name mismatch
  • Solution: Match the name returned by ListToolsRequestSchema with the name used in CallToolRequestSchema

Problem: Schema validation error

  • Cause: Argument types do not match
  • Solution: Check the Zod/Pydantic schema

Templates

Minimal TypeScript Server

#!/usr/bin/env node
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", description: "Say hello", inputSchema: { type: "object", properties: {} } }],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "hello") {
    return { content: [{ type: "text", text: "Hello, World!" }] };
  }
  throw new Error(`Unknown tool: ${request.params.name}`);
});

const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Server running");

Minimal Python Server

import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

server = Server("my-server")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [Tool(name="hello", description="Say hello", inputSchema={"type": "object", "properties": {}})]

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "hello":
        return [TextContent(type="text", text="Hello, World!")]
    raise ValueError(f"Unknown tool: {name}")

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream, server.create_initialization_options())

asyncio.run(main())

Advanced Features

Authentication

// API Key 認証
const API_KEY = process.env.API_KEY;

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  // ヘッダーから認証情報取得(実装依存)
  const authHeader = request.params._meta?.auth;

  if (authHeader !== API_KEY) {
    throw new Error("Unauthorized");
  }

  // ツール実行
});

Rate Limiting

import rateLimit from "express-rate-limit";

const limiter = new Map<string, { count: number; resetAt: number }>();

async function checkRateLimit(clientId: string): Promise<void> {
  const now = Date.now();
  const limit = limiter.get(clientId);

  if (!limit || now > limit.resetAt) {
    limiter.set(clientId, { count: 1, resetAt: now + 60000 });
    return;
  }

  if (limit.count >= 100) {
    throw new Error("Rate limit exceeded");
  }

  limit.count++;
}

Caching

from functools import lru_cache
import asyncio

# シンプルなメモリキャッシュ
@lru_cache(maxsize=100)
def get_cached_data(key: str) -> str:
    # 重い処理
    return expensive_operation(key)

# 非同期キャッシュ
cache = {}

async def get_cached_async(key: str) -> str:
    if key in cache:
        return cache[key]

    result = await expensive_async_operation(key)
    cache[key] = result
    return result

Implementation Examples

Example 1: GitHub Integration

GitHub統合MCPサーバーを作成:
- リポジトリ情報取得ツール
- PR作成ツール
- Issue検索ツール
- コミット履歴リソース
- PR レビュープロンプト

Example 2: Database Management

PostgreSQL MCP サーバー:
- クエリ実行ツール
- スキーマ情報リソース
- クエリ最適化プロンプト
認証: 接続文字列
エラーハンドリング: トランザクションロールバック

Example 3: File System

ファイル操作MCPサーバー:
- ファイル読み書きツール
- ディレクトリ一覧リソース
- ファイル検索ツール
セキュリティ: パストラバーサル対策

Documentation Generation

Automatically generated documentation:

README.md

  • Server Overview
  • Installation Steps
  • Usage
  • Tool List
  • Configuration Examples

API.md

  • API specifications for all tools
  • Input Schemas
  • Output Examples
  • Error Codes

CONTRIBUTING.md

  • Development Environment Setup
  • How to Run Tests
  • Contribution Guidelines

Limitations

  • Network: stdio-based works only within the same machine
  • Security: Input validation is mandatory (do not trust user input)
  • Performance: Consider timeouts for long-running tasks
  • Version: The latest SDK features are led by TypeScript/Python

Reference Resources

Version Information

  • Skill Version: 1.0.0
  • Supported MCP specification: 2025-01-01
  • Last updated: 2025-11-22

Usage Examples:

TypeScript MCPサーバーを作成してください:

名前: slack-integration
機能:
- メッセージ送信ツール
- チャンネル一覧リソース
- 定型メッセージプロンプト

設定:
- Slack API Token 使用
- エラーハンドリング完備
- レート制限対応
- Claude Desktop設定含む

With this prompt, a production-ready MCP server will be generated!