MCPサーバーを自作する手順まとめ【TypeScript】

こんにちは。Enjoy IT Life管理人の@nishina555です。

AI エージェントの文脈で注目されている**MCP(Model Context Protocol)**ですが、自分でMCPサーバーを作成できると、AIに任意のツールやリソースを提供できるようになります。

この記事では、TypeScript SDKを使ったMCPサーバーの自作手順をまとめます。

プロジェクトの作成

まずはプロジェクトの初期化と必要なパッケージのインストールを行います。

mkdir example-mcp
cd example-mcp
npm install @modelcontextprotocol/sdk
npm install --save-dev typescript @types/node
npx tsc --init

package.json

{
  "name": "example-mcp",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.9.0"
  },
  "devDependencies": {
    "@types/node": "^22.14.1",
    "typescript": "^5.8.3"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "node",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

MCPサーバーの基本構造

MCPサーバーの実装は大きく3つのステップで構成されます。

  1. McpServer(またはServer)インスタンスの作成
  2. サーバーに機能(tool / resource / prompt)を登録
  3. トランスポートとサーバーを接続して起動
// McpServerインスタンスの作成
const server = new McpServer({
  name: "Echo",
  version: "1.0.0",
  capabilities: {
    resources: {},
    tools: {},
    prompts: {},
  },
});

// serverに機能を登録(後述)

// transportとserverの連携
const transport = new StdioServerTransport();
await server.connect(transport);

サーバーの3つの機能

MCPサーバーには以下の3つの機能を登録できます。

  • tool — 外部APIを利用した検索や登録のように、何かしらのアクションを実行させる場合に使います
  • resource — ドキュメント等のリソースを参照させる場合に使います
  • prompt — プロンプトをテンプレート化する場合に使います

MCPサーバーの実装方法

MCPサーバーの実装には2つのスタイルがあります。

register形式(McpServerクラスの利用)

McpServerクラスを使うと、server.tool()server.resource()server.prompt()のように直感的にメソッドチェーンで機能を登録できます。

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

const server = new McpServer({
  name: "Echo",
  version: "1.0.0",
  capabilities: {
    resources: {},
    tools: {},
    prompts: {},
  },
});

server.resource(
  "echo",
  new ResourceTemplate("echo://{message}", { list: undefined }),
  async (uri, { message }) => ({
    contents: [
      {
        uri: uri.href,
        text: `Resource echo: ${message}`,
      },
    ],
  })
);

server.tool("echo", { message: z.string() }, async ({ message }) => ({
  content: [{ type: "text", text: `Tool echo: ${message}` }],
}));

server.prompt("echo", { message: z.string() }, async ({ message }) => ({
  messages: [
    {
      role: "user",
      content: {
        type: "text",
        text: `Please process this message: ${message}`,
      },
    },
  ],
}));

const transport = new StdioServerTransport();
await server.connect(transport);

request_handler形式(Serverクラスの利用)

Serverクラスを使う場合は、setRequestHandlerメソッドでリクエストスキーマごとにハンドラを登録します。記述はやや冗長になりますが、より細かいコントロールが可能です。

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  {
    name: "Hello Resources",
    version: "1.0.0",
  },
  {
    capabilities: {
      resources: {},
    },
  }
);

server.setRequestHandler(ListResourcesRequestSchema, async () => {
  return {
    resources: [
      {
        uri: "hello://world",
        name: "Hello World Message",
        description: "A simple greeting message",
        mimeType: "text/plain",
      },
    ],
  };
});

server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  if (request.params.uri === "hello://world") {
    return {
      contents: [
        {
          uri: "hello://world",
          text: "Hello, World! This is my first MCP resource.",
        },
      ],
    };
  }
  throw new Error("Resource not found");
});

const transport = new StdioServerTransport();
await server.connect(transport);

McpServerクラスとServerクラスの違い

Serverクラスは低レベルなAPIで、細かいコントロールが可能な分、記述は冗長になります。公式のmodelcontextprotocol/serversリポジトリのコードを見るとServerクラスで実装されているものが多く、またMcpServerクラスはloggingに対応していないなどの制約があります。公式ドキュメントのResources、Prompts、ToolsのページのサンプルコードもいずれもServerクラスによる実装です。

McpServerクラスは細かい制御はできないものの記述が簡潔で、シンプルなMCPサーバーであればこちらで十分です。

ビルド

TypeScriptのコンパイルを実行します。

npx tsc

MCPクライアントに設定

ビルドしたJSファイルをMCPクライアント(Claude Desktop、Cursorなど)の設定に追加します。

{
  "mcpServers": {
    "example-mcp": {
      "command": "node",
      "args": ["/path/to/example-mcp/build/register-format/echo.js"]
    }
  }
}

argsにはビルド後のJSファイルのパスを指定してください。

MCPサーバーのデプロイ方法

作成したMCPサーバーを公開・配布する方法はいくつかあります。

  • パターン1: npmで公開npxコマンドで実行してもらう方法です
  • パターン2: GitHubで公開 — ソースコードをGitHubで公開し、cloneしてもらってDockerビルドやローカルビルドで起動してもらう方法です
  • パターン3: ホスティングサービスに登録SmitheryなどのMCPサーバーのホスティングサービスに登録する方法です1

サンプルリポジトリ

この記事のサンプルコードは以下のリポジトリで公開しています。

参考

Footnotes

  1. 【MCPのトリセツ #1】MCPの概要と導入方法

タグ: MCP , TypeScript , AI