🚀

@modelcontextprotocol/create-serverを使って、MCPサーバーを自作してみよう

に公開

この記事では、ClaudeやCursor、ClineなどでAIアシスタントの機能を拡張できる Model Context Protocol (MCP) Server を自作する方法の一つとして、@modelcontextprotocol/create-serverを利用する方法を紹介します。

MCP とは何か?

Model Context Protocol は、AI モデルにコンテキストを提供するための仕組みです。Claude などの AI アシスタントに機能を追加できる拡張システムと考えると分かりやすいでしょう。例えば:

  • 自分のドキュメントを Claude に読み込ませる
  • Claude に特定のツールを使ってもらう
  • 特定のタスクに特化したプロンプトを定義する

などが可能になります。

MCP Server を作ってみる

どんなファイルやコマンド・設定が必要かを理解するためにも、できるだけ簡単な方法でセットアップを試みてみましょう。

準備

Node.js と npm が必要です。インストールされていることを前提に進めます。

1. プロジェクト作成

まずは MCP Server のプロジェクトを作成します:

npx @modelcontextprotocol/create-server first-mcp-server

コマンドを実行すると、以下のような対話式の質問があります:

? What is the name of your MCP server? first-mcp-server
? What is the description of your server? A Model Context Protocol server
? Would you like to install this server for Claude.app? Yes
✔ MCP server created successfully!
✓ Successfully added MCP server to Claude.app configuration

このプロセスでは、以下のような設定を行なっています:

  1. first-mcp-server という名前のプロジェクトを作成
  2. 簡単な説明を追加
  3. Claude.app の設定に自動的に登録

特に最後の「Claude.app の設定に自動的に登録」が便利です。Claude DesktopアプリにMCPを登録するJSONファイルに、以下のような設定が自動的に追加されます:

{
  "mcpServers": {
    "first-mcp-server": {
      "command": "node",
      "args": [
        "/Users/sandboxes/first-mcp-server/build/index.js"
      ]
    }
  }
}

あとはClaude Desktopを再起動することで、この MCP Server を認識できるようになりました。

2. SDKを最新版に更新

生成されたプロジェクトは古いバージョンの SDK を使用しています。最新版に更新しましょう:

npm install @modelcontextprotocol/sdk@latest

これにより、SDK が v0.6.0 から v1.8.0 などの最新バージョンにアップデートされます。

また、package.jsonzodを追加する必要があります:

npm install zod

3. コードを最新のSDKに対応させる

生成されたコードは古いバージョンの SDK 用に書かれているため、新しい SDK に対応するように修正が必要です。src/index.ts を以下のように変更します:

#!/usr/bin/env node

/**
 * This is a template MCP server that implements a simple notes system.
 * It demonstrates core MCP concepts like resources, tools, and prompts.
 */

import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/transports";
import { z } from "zod";

/**
 * Type alias for a note object.
 */
type Note = { title: string, content: string };

/**
 * Simple in-memory storage for notes.
 * In a real implementation, this would likely be backed by a database.
 */
const notes: { [id: string]: Note } = {
  "1": { title: "First Note", content: "This is note 1" },
  "2": { title: "Second Note", content: "This is note 2" }
};

/**
 * Create an MCP server
 */
const server = new McpServer({
  name: "first-mcp-server",
  version: "0.1.0",
});

/**
 * Define notes resource
 */
server.resource(
  "notes",
  new ResourceTemplate("note:///{id}", {
    list: async () => {
      return {
        resources: Object.entries(notes).map(([id, note]) => ({
          uri: `note:///${id}`,
          mimeType: "text/plain",
          name: note.title,
          description: `A text note: ${note.title}`
        }))
      };
    }
  }),
  async (uri, params) => {
    const id = String(params.id);
    const note = notes[id];
    
    if (!note) {
      throw new Error(`Note ${id} not found`);
    }
    
    return {
      contents: [{
        uri,
        mimeType: "text/plain",
        text: note.content
      }]
    };
  }
);

/**
 * Define create_note tool
 */
server.tool(
  "create_note",
  {
    title: z.string().min(1, "Title is required"),
    content: z.string().min(1, "Content is required")
  },
  async ({ title, content }) => {
    const id = String(Object.keys(notes).length + 1);
    notes[id] = { title, content };
    
    return {
      content: [{
        type: "text",
        text: `Created note ${id}: ${title}`
      }]
    };
  }
);

/**
 * Define summarize_notes prompt
 */
server.prompt(
  "summarize_notes",
  {},
  async () => {
    const embeddedNotes = Object.entries(notes).map(([id, note]) => ({
      type: "resource" as const,
      resource: {
        uri: `note:///${id}`,
        mimeType: "text/plain",
        text: note.content
      }
    }));
    
    return {
      messages: [
        {
          role: "user",
          content: {
            type: "text",
            text: "Please summarize the following notes:"
          }
        },
        ...embeddedNotes.map(note => ({
          role: "user" as const,
          content: note
        })),
        {
          role: "user",
          content: {
            type: "text",
            text: "Provide a concise summary of all the notes above."
          }
        }
      ]
    };
  }
);

/**
 * Start the server using stdio transport.
 * This allows the server to communicate via standard input/output streams.
 */
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

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

4. サーバーの実行

サーバーを実行するには、まずビルドする必要があります:

npm run build

その後、Claude Desktop を起動または再読み込みすると、自動的に MCP サーバーが読み込まれます。

また、以下のコマンドでインスペクターを起動すると、MCPサーバーをテストできます:

npm run inspector

インスペクターは http://localhost:5173 で確認できます。

実際に使ってみる

Claude Desktop でMCPを使ってみましょう。

確実に動かすには、体感としてはツール名を指定してやると良いかなと思います。そのため、「create_noteツールを使って、ノートを作成してみて」のように伝えると、下の画像のようにMCP経由でcreate_noteツールが実行されます。

あとは作成されたテンプレートの実装やMCP SDKのドキュメントを参考にしながら、自分の業務で使いたいMCPを作ってみましょう。

まとめ

MCP は AI アシスタントの可能性を大きく広げる仕組みといえます。残念ながら公式のボイラープレートコマンドはメンテナンスが中断されている状態ですが、内容自体はシンプルですので、forkしてメンテナンスするなど、やり方自体はいくつかありそうです。

もし他にも便利なMCPサーバーをセットアップするコマンドやツールがあれば、ぜひコメントでお教えください。

デジタルキューブ

Discussion