オンラインで議論

XXBlueskyBlueskyRedditRedditFacebookFacebookEmail

MCP実装の始め方|Pythonでサーバーを作る手順と安全な接続方法

約2分で読めます

AIで要約:

オンラインで議論

XXBlueskyBlueskyRedditRedditFacebookFacebookEmail

MCP実装の始め方|Pythonでサーバーを作る手順と安全な接続方法

MCP実装は、まず読み取り専用の小さなサーバーを作り、MCP対応クライアントからツールの一覧取得と実行を確認するところから始めます。Pythonの公式SDKを使えば、通信処理を一から書かずにツールを公開できますが、入力検証やアクセス権限、安全な停止は別途設計が必要です。

この記事では、ローカルで動く検索ツールのコードから、クライアント接続、テスト、本番運用に向けた注意点まで解説します。

この記事の要点

  • まずは一つの操作を実装する:読み取り専用の検索を公開し、入力から結果まで確認します。
  • SDKのバージョンをそろえる:この記事はPython公式SDKのv2系を使います。v1系とはクラス名やインポートが異なります。
  • 通信方式を選ぶ:ローカルの検証には標準入出力、共有するリモート接続にはStreamable HTTPを検討します。
  • 安全性は別に実装する:認証、対象ごとの権限確認、入力検証、重要操作の承認を設けます。
  • 正常系と異常系を試す:空結果、無効な入力、接続失敗、権限外のアクセスを区別します。

MCP実装で作るものと全体の仕組み

MCP(Model Context Protocol)は、アプリケーションと外部のツールや情報を提供するサーバーの間で、機能を見つけて利用するためのプロトコルです。公式のアーキテクチャ解説では、主な役割を次のように分けています。

  • ホスト:利用者が操作するAIアプリケーションです。接続やツール利用を管理します。
  • クライアント:ホスト内でMCPサーバーとの通信を担当します。
  • サーバー:利用できるツールや情報を公開し、要求に応じて処理します。

例えば、ホストが商品検索ツールを見つけ、クライアントを通して検索語を渡すと、サーバーが検索結果を返します。サーバーの先に既存のサービスAPIを置くこともできます。境界ごとの役割は、MCPとAPIの違いも参考にしてください。

サーバーが公開できる主な要素は、実行するツール、参照する情報を提供するリソース、再利用できるプロンプトです。すべてを同時に実装する必要はありません。今回の例ではツールだけを作り、外部APIや実データには接続しません。

通信方式は利用場所で選ぶ

stdioは、クライアントがローカルのサーバープロセスを起動し、標準入力と標準出力で通信する方式です。手元で最初のツールを検証する際に使えます。

Streamable HTTPは、HTTPを使ってサーバーに接続する方式です。離れた環境や複数の利用者からのアクセスを検討できますが、公開時には通信の保護、認証、認可、運用監視が必要です。MCP対応を名乗るクライアントでも、対応する方式や機能は確認してください。

PythonでMCPサーバーを実装する手順

ここでは、架空の商品データを名前の部分一致で検索するツールを作ります。実際の商品APIにアクセスせず、送信や更新もしないため、接続の基本を試す用途に適しています。

1. 実行環境を用意する

以下はmacOSまたはLinuxでの例です。Pythonの公式SDK v2系はPython 3.11以上を必要とします。Windowsでは仮想環境の有効化に.venv\Scripts\activateを使用してください。公式SDKの導入手順も参照できます。

mkdir mcp-sample
cd mcp-sample
python3 -m venv .venv
source .venv/bin/activate
python -m pip install 'mcp[cli]>=2,<3'

Inspectorでの確認にはNode.jsのnpxも必要です。依存関係は開発環境で確認したうえで固定し、更新時には再テストしてください。この例はSDK v2系の構文です。旧バージョンのFastMCPを使うコードとは混ぜません。

2. ツールを定義する

次の内容をserver.pyとして保存します。

from mcp.server import MCPServer

mcp = MCPServer("product-search")

PRODUCTS = [
    {"id": "p-001", "name": "方眼ノート"},
    {"id": "p-002", "name": "無地ノート"},
    {"id": "p-003", "name": "黒ボールペン"},
]


@mcp.tool()
def search_products(query: str) -> list[dict[str, str]]:
    """商品名の部分一致で、架空の商品データを検索する。

    Args:
        query: 検索したい商品名の一部。前後の空白を除き1〜50文字。
    """
    normalized = query.strip()
    if not normalized or len(normalized) > 50:
        raise ValueError("検索語は1〜50文字で指定してください")

    return [
        product for product in PRODUCTS
        if normalized in product["name"]
    ]


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

@mcp.tool()で関数をツールとして公開します。型注釈と説明は、クライアントが入力を組み立てる際の手がかりです。空文字と長すぎる検索語は、説明だけに頼らず関数内でも拒否しています。公式のサーバー実装ガイドにも、ツールの登録とstdioでの起動例があります。

この例で正常な空配列が意味するのは、架空の3件の中に一致がなかったことです。実際のAPIに置き換えるときは、取得失敗や一部しか取得できなかった状態を、該当なしと区別してください。

3. Inspectorで一覧と実行を確認する

同じ仮想環境で次を実行します。

mcp dev server.py

表示される案内に従ってMCP Inspectorを開き、ツール一覧からsearch_productsを選びます。queryにノートを入力して実行し、p-001とp-002が返ることを確認します。ペンならp-003、消しゴムなら空の結果が期待されます。

空白のみ、51文字以上、文字列以外の入力も試し、正常な空結果と入力エラーを区別してください。Inspectorの表示形式はバージョンにより変わり得ます。終了するときは、起動したターミナルでCtrl+Cを押します。

MCPクライアントから自作サーバーに接続する

Inspectorで動作を確認した後は、実際に利用するMCP対応アプリケーションから接続します。ここではClaude Desktopのローカル設定を例にします。設定方法や対応機能はアプリケーションの更新に伴って変わるため、利用する版の案内も確認してください。

Claude Desktopの設定例

macOSでは~/Library/Application Support/Claude/claude_desktop_config.json、Linuxでは~/.config/Claude/claude_desktop_config.jsonを開きます。ファイルがなければ作成し、既存の設定がある場合は内容を消さず、mcpServersの中に追加します。公式の接続手順ではWindowsの設定も案内しています。

以下はmacOS・Linux向けの設定例です。二つのパスは、作成した仮想環境のPythonとserver.pyの実際の絶対パスに置き換えてください。

{
  "mcpServers": {
    "product-search": {
      "command": "/実際のパス/mcp-sample/.venv/bin/python",
      "args": ["/実際のパス/mcp-sample/server.py"]
    }
  }
}

アプリを完全に終了してから再起動し、コネクタや利用可能なツールの表示でsearch_productsを確認します。例えば「ノートを検索して」と依頼し、ツール実行が必要な場合の確認画面と結果を見ます。クライアントやモデルの判断によっては、依頼文だけで必ずツールが呼び出されるとは限りません。

接続できない場合の切り分け

  1. 起動コマンド:指定したPythonとスクリプトが実在し、同じ環境にSDKがインストールされているか確認します。
  2. 設定ファイル:JSONの括弧やカンマ、絶対パス、既存設定との重複を確認します。
  3. ログ:クライアントの接続ログとサーバーのエラーを照合します。機密情報をログに含めないようにします。
  4. 通信の混入:stdioでは標準出力をプロトコル通信に使います。print()でデバッグ情報を出すと通信を壊すため、ログは標準エラーに出します。
  5. 互換性:クライアントとSDKの対応するプロトコルや機能を確認し、必要に応じてInspectorで単独テストします。

作業ディレクトリや環境変数は、ターミナルで直接起動した場合と異なることがあります。確認箇所は公式のデバッグガイドにも整理されています。

既存APIをMCPツールにつなぐときの設計

実際の業務では、先ほどの架空データの代わりに既存APIやデータベースを呼び出せます。その際は、APIの全機能をそのまま公開するのではなく、利用者が行う業務操作を一つずつ定義することから始めます。

例えば「商品名から候補を探す」なら、ツールには検索語を受け取り、APIに定められた検索条件へ変換し、商品IDと名前を返す役割を持たせます。先に次の対応を決めておきます。

  1. 入力:必須項目、文字数、検索方法、許可する条件を決めます。名前と商品IDは別の入力として扱います。
  2. 呼び出し先:接続先のURLや操作はサーバー側で管理し、モデルが任意のURLを指定できないようにします。
  3. 認証情報:APIキーをコードやツール引数に埋め込まず、実行環境側で保護します。
  4. 応答:必要な項目のほか、検索範囲、続きの有無や取得時点を、APIが提供する範囲で返します。
  5. 失敗:入力不備、権限不足、接続失敗、正常な該当なしを分けます。内部の秘密情報や詳細な例外をクライアントに返さないようにします。

例えばAPIが1ページ分しか返していないのに、ツールが「全件」と説明すれば、呼び出しは成功しても結果の意味は誤ります。入力と出力の契約が実装に一致するよう、サーバー側の変換と接続先の仕様を一緒にテストしましょう。

ツールの名前や説明に「読み取り専用」と書くだけではアクセス制限にはなりません。実際に使う認証情報と接続先の権限も読み取りに限定します。書き込みを追加する場合は、検索とは別ツールにし、対象、操作内容、承認条件を設けます。

MCP実装を安全に運用するための注意点

ローカルでツールが動くことは、共有環境にそのまま公開してよいことを意味しません。誰が、何にアクセスし、どの操作を実行できるかを、モデルの判断とは別に制御します。

認証と権限を分けて確認する

リモートサーバーでは、通信を保護したうえで、利用者を認証し、個々の操作と対象データへの権限を確認します。MCPの認可ガイドは、HTTPベースのサーバーを保護するOAuthの流れを解説しています。トークンが有効でも、対象の顧客や資料にアクセスできるかは別に判断します。

stdioのローカルサーバーも無条件に安全ではありません。サーバープロセスが利用できるファイルや環境変数、接続先を確認し、必要のない認証情報を渡さないようにします。信頼できる提供元のコードを使い、実行権限と依存パッケージを管理してください。

入力と外部情報を信用しすぎない

ツールの引数は外部入力として扱い、型や長さだけでなく、許可された対象かを実行側で確認します。取得した文書やAPI応答の中に別の命令が書かれていても、それだけを理由に機密情報を送信したり、権限を広げたりしないようにします。

検索結果はモデルが参考にするデータであり、サーバーの権限を変更する指示ではありません。判断の文言に頼るだけでなく、許可していない操作は実行側で拒否します。

書き込みには承認と停止条件を設ける

送信、更新、削除などを公開するときは、読み取りから分け、影響に応じて人が対象と内容を確認します。承認後に内容が変われば再承認し、承認がない場合は実行しません。実行直前にも利用者と対象の権限を確認します。

書き込み後にタイムアウトした場合は、未実行と断定せず、操作IDなどで状態を照会します。確認できなければ自動再送を止めて担当者に引き継ぎます。再送の条件は、接続先の仕様を確認し、APIタイムアウトの対処法も参照してください。認証、権限、承認の分担は安全なAPI連携の設計で詳しく解説しています。

本番前に確認したいテストと運用項目

MCPサーバーの評価では、接続成功だけでなく、ツールの選択、入力、処理結果、失敗時の対応を一続きで確認します。まずはテスト用データと権限を用意し、本番の送信や更新を伴わない環境で試してください。

機能と結果のテスト

  • 正常系:ツール一覧に意図した名前と説明が表示され、正しい入力で期待する結果が返るか。
  • 該当なし:正常な空結果を、接続失敗と区別して扱えるか。
  • 無効な入力:必須値の不足、型の誤り、空文字、長すぎる値を拒否できるか。
  • 結果の範囲:ページの続きや取得失敗がある場合に、取得できた範囲を誤解なく伝えられるか。
  • 接続障害:APIのタイムアウトや認証エラーを、該当なしとして返していないか。
  • 権限外の要求:別の利用者や組織のデータ、許可されていない操作を拒否できるか。

利用者の依頼文を使った確認では、正しい答えだけでなく、別のツールを誤って選ばないか、引数を勝手に補わないか、必要な場合に確認を求めるかも見ます。ツールの説明を変えたら同じケースで再評価します。

運用前に決めること

監視では、誰の依頼でどのツールを呼び出し、許可・拒否・失敗のどれになったかを追えるようにします。認証情報や不要な個人情報を記録しないこと、ログの閲覧権限と保存期間を決めることも必要です。

あわせて、SDKと接続先APIの更新担当、変更時の互換性確認、異常時にツールを無効化する方法を決めます。書き込みを扱う構成では、承認拒否、停止、応答不明からの状態確認も運用手順に含めます。

まとめ

MCP実装では、まず公開する操作を一つ決め、公式SDKでサーバーを作り、Inspectorと利用するクライアントの両方から動作を確認します。読み取り専用の小さな例でも、入力エラー、空結果、接続失敗を分けて扱うことが、実務へ広げるための出発点です。

次に実際のAPIと対応付け、利用者ごとの権限、秘密情報の管理、書き込みの承認、障害時の停止を設計します。接続方式を変えて共有する前に、誰が何を実行でき、問題が起きたときにどこで止めるかを確認してください。

よくある質問

MCP実装には何が必要ですか?

公開したい操作、MCPサーバーを動かす環境、対応するクライアントが必要です。Pythonでこの記事の例を試す場合は、Python 3.11以上と公式SDKのv2系を用意します。Inspectorを使う場合はnpxも必要です。

MCPサーバーは自作しなければなりませんか?

必須ではありません。利用するクライアントから必要な機能を提供する既存サーバーに接続できるなら、それを利用する方法もあります。独自の業務ルールや接続先を扱いたい場合は、自作する構成を検討できます。

MCPサーバーとAPIはどちらか一方を選ぶものですか?

いいえ。MCPサーバーがツールを公開し、その内部から既存のサービスAPIを呼び出す構成にできます。APIが提供していない機能まで、MCPを介するだけで使えるようになるわけではありません。

ローカルで動いたサーバーをそのまま公開できますか?

通信方式を変更できても、それだけでは共有環境に適した構成にはなりません。利用者の認証、データごとの権限、通信保護、ログ、停止方法を実装し、接続先も含めてテストしてください。

ツールの呼び出しが失敗したら再試行すればよいですか?

操作と失敗の内容によります。読み取りでも再試行の条件と上限を確認し、書き込みの応答が得られなかった場合は、実行済みの可能性を考えて先に状態を照会します。確認できなければ自動再送せず、人に引き継ぎます。

記事一覧