4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘録】今更MCPサーバを勉強した

Posted at

記事の概要

Google Cloud Next 25でGCPがMCP (Model Context Protocol) を取り入れているのを見て、今更ながらMCPを勉強してみました。
本記事は、勉強過程の記録です。

勉強過程

STEP1: MCPサーバの概要を学ぶ

下記の「松尾研究所テックブログ」が分かりやすかったです。

STEP2: 公開されているMCP Serverをたててみる

MCPサーバ

公開されているMCPサーバは主に以下2種類に大別されます。

  • TypeScript製
  • Python製

私はDockerを用いてサーバをたてており、サーバごとにコンテナを分けています。
TypeScript製・Python製いずれもDockerコンテナで立ちあげてしまえば、サーバごとに環境を分離できて便利です。

下記に実際にたてたMCPサーバを記載します。

公開されているMCPサーバを利用する際は悪意あるコードが潜んでいないか十分注意しましょう。

参考: 最小限のPython MCPサーバを作ってLLMを騙してみる

MCPクライアント

MCPクライアントにはClaude Desktopを利用しました。
MCPの設定を記載するclaude_desktop_config.jsonのパスはMac OSでは/Users/<ユーザ名>/Library/Application Support/Claude/claude_desktop_config.jsonです。

実際にたてたMCPサーバ

Anthoropic公式 MCPサーバ (Filesystem MCP Server)

GitHubリポジトリ:

解説記事:

GitHub

解説記事:

GitHubリポジトリ:

AWS Documentation MCP Server

解説記事:

公式ページ:

Notion

解説記事:

GitHubリポジトリ:

LINE

解説記事:

GitHubリポジトリ:

STEP3: MCPサーバを作って学ぶ

基本、下記Anthoropic公式Quick Startが参考になります。

Python

FastMCPを利用します。

前提:
Claude Desktopアプリは立ち上がっていない状態

手順:

  1. 下記の通り各種ファイルを作成
  2. プロジェクトのルートディレクトリ (Dockerfileと同じ階層) でdocker build -t my-mcp-server-python . を実行し、Docker image作成
  3. Claude Desktopを立ち上げる
ディレクトリ構造
.
├── Dockerfile
├── app.py
└── pyproject.toml
Dockerfile
FROM python:3.12-slim-bookworm

WORKDIR /app

COPY pyproject.toml ./
COPY app.py ./

RUN pip install uv
RUN uv venv
RUN . .venv/bin/activate
RUN uv add "mcp[cli]"

CMD ["uv", "run", "app.py"]
pyproject.toml
[project]
name = "my-mcp-server-python"
version = "0.1.0"
dependencies = [
    "mcp[cli]"
]
app.py
# server.py
from mcp.server.fastmcp import FastMCP

# Create an MCP server with debug enabled
mcp = FastMCP("my-mcp-server-python", debug=True)


# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    return a + b

# Add this part to run the server
if __name__ == "__main__":
    # stdioトランスポートを使用
    print("Starting MCP server in stdio mode")
    mcp.run(transport="stdio")
claude_desktop_config.json
{
  "mcpServers": {
    "my-mcp-server-python": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "my-mcp-server-python"
      ]
    }
  }
}

コード参考;

TypeScript

@modelcontextprotocol/sdkを利用します。

前提:

  • Claude Desktopアプリは立ち上がっていない状態
  • node, npmのバージョンは下記の通り
    • node: v22.7.0
    • npm: 10.8.2

手順:

  1. プロジェクトのルートディレクトリ (Dockerfileと同じ階層) でnpm init -yを実行
  2. プロジェクトのルートディレクトリで下記を実行
    npm install @modelcontextprotocol/sdk zod
    npm install -D @types/node typescript vitest
    
  3. 下記の通りDockerfile, package.json, tsconfig.json, src/index.test.ts, src/index.tsを作成 (package-lock.jsonは自動生成されたものをそのまま使用)
  4. プロジェクトのルートディレクトリでnpm run testを実行し、パスするのを確認
  5. プロジェクトのルートディレクトリでnpm run buildを実行し、正常にビルドできることを確認 (buildディレクトリ配下にindex.jsindex.test.jsが作成される)
  6. プロジェクトのルートディレクトリでdocker build -t my-mcp-server-typescript . を実行し、Docker image作成
  7. Claude Desktopを立ち上げる
ディレクトリ構造
.
├── Dockerfile
├── build
│   ├── index.js
│   └── index.test.js
├── package-lock.json
├── package.json
├── src
│   ├── index.test.ts
│   └── index.ts
└── tsconfig.json
Dockerfile
FROM node:22-slim

WORKDIR /app

# 全てのファイルを一度にコピー
COPY . .

# 依存関係のインストール
RUN npm install

# TypeScriptのビルドとdistディレクトリの作成を確認
RUN npm run build && \
    if [ -d "build" ]; then \
        chmod +x build/*.js || true; \
    else \
        echo "Error: build directory was not created" && exit 1; \
    fi

# コンテナ起動時のコマンド
CMD ["node", "build/index.js"]
package.json
{
  "name": "my-mcp-server-typescript",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "bin": {
    "my-mcp-server": "./build/index.js"
  },
  "files": [
    "build"
  ],
  "scripts": {
    "test": "vitest",
    "build": "tsc && chmod 755 build/index.js"
  },
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.10.2",
    "zod": "^3.24.3"
  },
  "devDependencies": {
    "@types/jest": "^29.5.14",
    "@types/node": "^22.15.3",
    "typescript": "^5.8.3",
    "vitest": "^3.1.2"
  }
}
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"],
  "types": ["jest", "node"],
}
src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as z from "zod";

export const server = new McpServer({
  name: "my-mcp-server",
  version: "1.0.0",
});

server.tool(
  "add",  // 提供するtoolの名前
  "足し算をする",  // toolの説明
  {
    a: z.number().describe("数値1"),
    b: z.number().describe("数値2"),
  },  // toolのパラメータ定義
  ({ a, b }) => ({ content: [{ type: "text", text: (a + b).toString() }] }),  // toolが提供する機能の本体
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Example MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});
src/index.test.ts
import { describe, it, expect } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { server } from "./index.js";
 
describe("my-mcp-serverのテスト", () => {
  it("add: 足し算の結果を返す", async () => {
    // テスト用クライアントの作成
    const client = new Client({
      name: "test client",
      version: "0.1.0",
    });
 
    // インメモリ通信チャネルの作成
    const [clientTransport, serverTransport] =
      InMemoryTransport.createLinkedPair();
      
    // クライアントとサーバーを接続
    await Promise.all([
      client.connect(clientTransport),
      server.connect(serverTransport),
    ]);
 
    // 6面サイコロを振る
    const result = await client.callTool({
      name: "add",
      arguments: {
        a: 3, b: 8
      },
    });
 
    // 結果が1-6の範囲の数字であることを確認
    expect(result).toEqual({
      content: [
        {
          type: "text",
          text: "11",
        },
      ],
    });
  });
});
claude-desktop-config.json
{
  "mcpServers": {
    "my-mcp-server-typescript": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "my-mcp-server-typescript"
      ]
    }
  }
}

コード参考:

余談:uvについて

気がついたらPythonのuvというライブラリが一世を風靡していた。

感想

MCPとはなにか?はAnthoropicがMCPを公開した際に簡単に勉強しましたが、ここまで人気になるとは思わず、長らく放置してしまいました。
Node.js(TypeScript)があまり得意ではないというのも放置の原因ではありました。Pythonでも書けるのなら、もっと早く手をつけておけばよかったです(Node.jsも勉強しろ)。
個人的には、もっといろんなサービスが公式MCPを作成してくれると安心して使えるのになぁ...と思いました。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?