自作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は変更される可能性があります。


