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

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

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

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

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

スポンサーリンク

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

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

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

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

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

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

環境構築

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

# uvを使う場合(推奨)
uv add "mcp[cli]"

# pipを使う場合
pip install "mcp[cli]"

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

ツール定義のコード全文

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

weather_server.py

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Weather Tool")

@mcp.tool()
def get_weather(city: str) -> str:
    """指定した都市の現在の天気を返す。

    Args:
        city: 天気を調べたい都市名(例: "東京", "大阪")
    """
    # ダミー実装。実際は外部の天気APIを呼び出す
    return f"{city}の天気は晴れ、気温は22度です"

if __name__ == "__main__":
    mcp.run()

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

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

ローカルでの起動確認

動作確認

python weather_server.py

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

Claude Codeに接続する

サーバーの登録

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

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

環境変数つきの登録例

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

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

動作確認

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

Codexに接続する

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

config.toml

[mcp_servers.weather-tool]
command = "python"
args = ["weather_server.py"]

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

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

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

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

外部API呼び出しの例

import httpx

@mcp.tool()
def check_inventory(product_code: str) -> str:
    """指定した商品コードの在庫数を社内在庫システムから取得する。

    Args:
        product_code: 商品コード(例: "SKU-1234")
    """
    response = httpx.get(
        f"https://internal-api.example.com/inventory/{product_code}",
        headers={"Authorization": f"Bearer {os.environ['INVENTORY_API_KEY']}"},
    )
    response.raise_for_status()
    data = response.json()
    return f"{product_code}の在庫数は{data['stock']}個です"

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

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

エラーハンドリングの例

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

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

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

セキュリティの注意点

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

よくあるハマりどころ

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

よくある質問

Q. TypeScriptでも作れますか?

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

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

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

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

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

まとめ

本記事のポイント

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

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

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

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