自作MCPサーバーの作り方|独自ツールをClaude Code・Codexに接続する実践ガイド【2026年】

中央のサーバーブロックと左右のターミナルが接続線で繋がり、工具アイコンが添えられたイメージ
\n\n
\n
\n\n
\n実践チュートリアル\n

自作MCPサーバーの作り方
独自ツールをClaude Code・Codexに接続する実践ガイド

\n

社内API・独自システムをAIエージェントから呼び出せるようにする、ゼロからの実装手順を解説します。

\n
\n\n

これまで当サイトでは「既存のMCPサーバーに接続する」方法を紹介してきました。本記事はその逆——自分でMCPサーバーを実装し、Claude CodeやCodexから呼び出せるようにする側のガイドです。「社内の在庫システムをAIから直接操作したい」「独自のスクレイピングツールをエージェントの手足にしたい」といったニーズに応えます。

\n\n\n\n

MCPサーバーの構造を3分で理解する

\n\n

MCPサーバーが公開できる要素は主に3つですが、実務で使う頻度が最も高いのはTools(ツール)です。本記事もToolsの実装に絞って解説します。

\n\n

2つのトランスポート方式

\n\n\n\n\n\n\n\n\n\n
方式特徴向いている用途
stdioローカルプロセスとして起動し、標準入出力で通信個人のPC上で動かすツール、社内ネットワーク限定のツール
HTTP常時稼働するサーバーとして公開し、ネットワーク経由で通信チームで共有したいツール、クラウド上に置きたいツール
\n\n

まずはローカルで完結するstdio方式から始めるのがおすすめです。本記事もstdio方式で進めます。

\n\n

最小のMCPサーバーを作る(Python編)

\n\n

環境構築

\n\n
\n

パッケージのインストール

\n
# uvを使う場合(推奨)\nuv add "mcp[cli]"\n\n# pipを使う場合\npip install "mcp[cli]"
\n
\n\n

公式PythonSDKにはFastMCPという高レベルインターフェースが含まれており、デコレータを使うだけでボイラープレートなしにサーバーを実装できます。

\n\n

ツール定義のコード全文

\n\n

「都市名を渡すと天気を返す」という最小限のツールを例に実装します。

\n\n
\n

weather_server.py

\n
from mcp.server.fastmcp import FastMCP\n\nmcp = FastMCP("Weather Tool")\n\n@mcp.tool()\ndef get_weather(city: str) -> str:\n    """指定した都市の現在の天気を返す。\n\n    Args:\n        city: 天気を調べたい都市名(例: "東京", "大阪")\n    """\n    # ダミー実装。実際は外部の天気APIを呼び出す\n    return f"{city}の天気は晴れ、気温は22度です"\n\nif __name__ == "__main__":\n    mcp.run()
\n
\n\n
\n

型ヒントとdocstringがそのままAIへの説明になる

\n

@mcp.tool()デコレータは、関数の型ヒントから自動的に入力スキーマを生成し、docstringをそのままツールの説明文として使います。関数の書き方=AIへの説明の質だと考え、引数の説明まで丁寧に書くことが、AIの呼び出し精度に直結します。

\n
\n\n

ローカルでの起動確認

\n\n
\n

動作確認

\n
python weather_server.py
\n
\n\n

エラーなく起動すればOKです。次はこのサーバーをClaude Code・Codexそれぞれから呼び出せるように登録します。

\n\n

Claude Codeに接続する

\n\n
\n

サーバーの登録

\n
claude mcp add weather-tool --transport stdio -- python weather_server.py
\n
\n\n

--より前がClaude Code側のオプション、後ろが実際にサーバーを起動するコマンドです。環境変数を渡したい場合は--envオプションを使います。

\n\n
\n

環境変数つきの登録例

\n
claude mcp add weather-tool --transport stdio \\\n  --env WEATHER_API_KEY=your_key_here \\\n  -- python weather_server.py
\n
\n\n

登録後、/mcpコマンドで接続状況を確認できます。チームで共有したい場合は--scope projectを付けると、設定が.mcp.jsonとしてプロジェクトに保存され、リポジトリ経由で共有できます。

\n\n
\n

動作確認

\n
/mcp\n# weather-tool が接続済みとして表示されればOK
\n
\n\n

Codexに接続する

\n\n

Codexの設定ファイル(~/.codex/config.toml)に、同じサーバーを登録します。

\n\n
\n

config.toml

\n
[mcp_servers.weather-tool]\ncommand = "python"\nargs = ["weather_server.py"]
\n
\n\n

1つのサーバー実装を、Claude Code・Codexの両方から呼び出せるのがMCPの利点です。ツールを一度作れば、使うエージェントを問いません。

\n\n

実用レベルに引き上げる3つのポイント

\n\n

① 外部APIを呼ぶツールにする

\n\n

ダミー実装を、実際の社内APIやSaaSのAPIを呼び出す処理に置き換えます。

\n\n
\n

外部API呼び出しの例

\n
import httpx\n\n@mcp.tool()\ndef check_inventory(product_code: str) -> str:\n    """指定した商品コードの在庫数を社内在庫システムから取得する。\n\n    Args:\n        product_code: 商品コード(例: "SKU-1234")\n    """\n    response = httpx.get(\n        f"https://internal-api.example.com/inventory/{product_code}",\n        headers={"Authorization": f"Bearer {os.environ['INVENTORY_API_KEY']}"},\n    )\n    response.raise_for_status()\n    data = response.json()\n    return f"{product_code}の在庫数は{data['stock']}個です"
\n
\n\n

② エラーハンドリングを丁寧にする

\n\n

例外を握りつぶさず、AIが理解できるメッセージとして返すことが重要です。エラー内容が具体的であれば、AIはリトライしたり、別の言い方でユーザーに確認したりできます。

\n\n
\n

エラーハンドリングの例

\n
@mcp.tool()\ndef check_inventory(product_code: str) -> str:\n    """指定した商品コードの在庫数を取得する。"""\n    try:\n        response = httpx.get(f"...{product_code}", timeout=5.0)\n        response.raise_for_status()\n    except httpx.TimeoutException:\n        return "エラー: 在庫システムへの接続がタイムアウトしました。時間をおいて再試行してください。"\n    except httpx.HTTPStatusError as e:\n        if e.response.status_code == 404:\n            return f"エラー: 商品コード「{product_code}」が見つかりません。コードを確認してください。"\n        return f"エラー: 在庫システムでエラーが発生しました(ステータス{e.response.status_code})"\n    return f"{product_code}の在庫数は{response.json()['stock']}個です"
\n
\n\n

③ 入力スキーマをきちんと書く

\n\n

型・説明・必須/任意の指定が曖昧だと、AIは誤った値でツールを呼び出しがちです。特に複数の引数がある場合、それぞれに具体例を含めた説明を書くことをおすすめします。

\n\n

セキュリティの注意点

\n\n
    \n
  • 認証情報は環境変数で渡す:APIキーをコードに直接書き込まず、--env.envファイル経由で渡す
  • \n
  • 破壊的操作は別ツールとして明示的に分ける:「削除」「送信」といった不可逆な操作は、名前だけで分かるツールに独立させ、確認フローを挟めるようにする
  • \n
  • リモート公開する場合は認証を必須にする:HTTP方式で外部に公開するサーバーは、誰でも呼び出せる状態にしない
  • \n
  • プロンプトインジェクションを意識する:ツールが返す外部データ(Web検索結果など)に悪意ある指示文が含まれている可能性を念頭に、ツールの権限は必要最小限にとどめる
  • \n
\n\n

よくあるハマりどころ

\n\n\n\n\n\n\n\n\n\n\n
症状主な原因
サーバーが認識されない起動コマンドのパスが間違っている、Pythonの実行環境が異なる
ツールが呼ばれないdocstringの説明が曖昧で、AIがいつ使うべきか判断できていない
日本語の出力が文字化けする標準出力のエンコーディング設定を確認する
\n\n

よくある質問

\n\n
\n

Q. TypeScriptでも作れますか?

\n

A. はい。MCPの公式SDKはPythonだけでなくTypeScriptでも提供されており、同じくデコレータ的な書き方でツールを定義できます。既存のNode.jsプロジェクトに組み込みたい場合はTypeScript版が便利です。

\n
\n\n
\n

Q. 既存のREST APIをそのままMCP化できますか?

\n

A. REST APIのエンドポイントごとに、それを呼び出す薄いラッパー関数を書いて@mcp.tool()を付ける形になります。エンドポイントの数が多い場合は、よく使うものから優先的にツール化するのが現実的です。

\n
\n\n
\n

Q. 複数のツールは1つのサーバーにまとめるべきですか?

\n

A. 関連性の高いツール群(例:在庫システム関連)は1サーバーにまとめ、性質の異なるツール(例:在庫システムと画像生成)は別サーバーに分けるのが管理しやすいです。1ファイルに複数の@mcp.tool()を並べるだけで、同じサーバー内に複数ツールを持たせられます。

\n
\n\n

自作サーバーを運用するなら、2026年7月28日版の仕様変更への対応が必要です。影響範囲と移行手順はMCP 2026-07-28仕様の破壊的変更まとめをご覧ください。

まとめ

\n\n
\n

本記事のポイント

\n
    \n
  • MCPサーバーはFastMCPの@mcp.tool()デコレータだけで最小構成が作れる
  • \n
  • 作ったサーバーはClaude Code(claude mcp add)とCodex(config.toml)の両方から呼び出せる
  • \n
  • 実用化には外部API連携・丁寧なエラーハンドリング・具体的な入力スキーマの3点が効く
  • \n
  • 認証情報の扱いと破壊的操作の分離など、セキュリティ面の設計も忘れずに
  • \n
\n
\n\n

最初から複雑なツールを作る必要はありません。まずは読み取り専用の小さなツール1つから始めて、動作を確認しながら少しずつ育てていくのがおすすめです。

\n\n
\n

※本記事の情報は2026年7月時点のものです。MCPの仕様・各SDKのAPIは変更される可能性があります。

\n
\n\n
\n
\n
タイトルとURLをコピーしました