Skip to main content
Glama

独自のMCPサーバーを構築する(そしてデプロイする)

LLM APIをMCPツールに変換する、完全に動作するMCPサーバー — 教えるために作られました。 ローカルではstdio経由でClaude Desktop / Claude Code上で動作し、リモートではHTTP経由で デプロイ後に動作します。

Groqをバックエンドに採用 — 高速推論、OpenAI互換API、ワークショップ中に学生が一斉に 叩いても耐えられる無料枠。

すべてが1つのファイルに収まっています:server.py。コメント込みで約170行。


パート0 — MCPとは何か、1分で

MCP(Model Context Protocol)は、AIクライアントに新しい機能を与えるための標準的な方法です。 サーバーを書けば、どんなMCPクライアントでもそれを使えます。

サーバーは3つのものを公開できます:

プリミティブ

それが何か

誰が制御するか

ツール

モデルが呼び出せる関数

モデルが決定

リソース

クライアントが取り込める読み取り専用データ

クライアント/アプリが決定

プロンプト

再利用可能なプロンプトテンプレート

ユーザーが選択

2つのトランスポート:

  • stdio — クライアントがサーバーをサブプロセスとして起動し、stdin/stdoutで通信します。 ローカルのみ。ネットワークは不要。これがMCPサーバーの90%の動作方法です。

  • streamable HTTP — サーバーがURL上のWebサービスになります。これは、他の人(またはホストされた クライアント)が使用できるようにデプロイするものです。

同じserver.pyが両方を行います。それがすべての仕掛けです。


Related MCP server: mcp-cli-catalog

パート1 — 構築するもの

llm-toolkit:任意のMCPクライアントに4つのLLM搭載ツールを提供するMCPサーバー。

ツール

機能

ask_llm

質問をする、簡潔/詳細/子ども向けを選択

summarize

テキスト → N個の箇条書き

translate

翻訳、マークダウンとコードブロックを保持

extract_json

非構造化テキスト → 構造化JSON

加えて1つのリソース(config://server-info)と1つのプロンプト(code_review)があり、学生が 3つのプリミティブすべてを確認できます。


パート2 — ローカルで実行する

セットアップ

python -m venv .venv

Windows:.\venv\Scripts\activate — macOS/Linux:source .venv/bin/activate

pip install -r requirements.txt

console.groq.comで無料キーを取得 → API Keys。その後 .env.exampleを.envにコピーして貼り付けます:

cp .env.example .env

.envはgitignoreされています。サーバーは自身のディレクトリから自動的に読み込むため、 クライアントがどこから起動しても動作します。

接続前に検査する

MCP Inspectorは最高の教育ツールです — ツールリストを表示し、AIクライアントなしで 手動でツールを呼び出せます。

npx @modelcontextprotocol/inspector python server.py

表示されたURLを開き、Connect、次にList Toolsをクリックします。4つすべてが表示されます。


パート3 — クライアントに接続する

Claude Code

claude mcp add llm-toolkit -e GROQ_API_KEY=gsk_... -- python /absolute/path/to/server.py

または、プロジェクトルートに.mcp.jsonをコミットしてチーム全体で共有します — .mcp.json.exampleを参照。

Claude Desktop

claude_desktop_config.jsonを編集します:

  • macOS — ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows — %APPDATA%\Claude\claude_desktop_config.json

.mcp.json.exampleからmcpServersブロックを貼り付け、Claude Desktopを完全に終了して再起動します。 ツールアイコンの下にツールが表示されます。

絶対パスのみ。 ローカルMCPサーバーが「表示されない」一番の原因は 相対パスです — クライアントの作業ディレクトリはあなたのものとは異なります。 pythonバイナリ(.venv/bin/python)とserver.pyの両方にフルパスを使用してください。


パート4 — デプロイする

1つのフラグでHTTPモードに切り替えます:

python server.py --http

サーバーはhttp://localhost:8000/mcpにあります。同じコマンドをコンテナに詰め込みます。

オプションA — Render、Dockerなし(推奨)

RenderにはネイティブのPythonランタイムがあります。Dockerfileもコンテナビルドも不要です。 requirements.txtをインストールし、スタートコマンドを直接実行します。これがラップトップから 公開URLへの最速のパスです。

ステップ1 — コードをGitHubにプッシュします。

git init && git add -A && git commit -m "MCP server"

github.com/newで空のリポジトリを作成し、次に:

git remote add origin https://github.com/<you>/llm-toolkit-mcp.git && git push -u origin main

ステップ2 — サービスを作成します。

Renderダッシュボード → New → Web Service → リポジトリを接続します。Renderは render.yamlを読み取り、自動設定します:

設定

値

ランタイム

Python(Dockerではない)

ビルドコマンド

pip install -r requirements.txt

スタートコマンド

python server.py --http

ステップ3 — キーを設定します。 ダッシュボード → Environment → GROQ_API_KEYを追加します。 render.yamlではsync: falseとマークされているため、ダッシュボード内のみに存在し、 gitには決して保存されません。

ステップ4 — デプロイします。 公開エンドポイントはhttps://<your-app>.onrender.com/mcpです。

意図的にヘルスチェックなし。 GET /mcpは、設計上開いたままのSSEストリームを開きます。 そこに向けられたヘルスチェックはハングし、Renderはタイムアウトをデッドサービスと見なして 再起動ループします。healthCheckPathを省略すると、Renderはプロセスが$PORTにバインドされていることだけを 確認します — このサーバーにとって正しいチェックです。

無料枠のインスタンスは約15分のアイドル後にスリープします。スリープ後の最初の呼び出しは、 起動に約30〜50秒かかります。一部のMCPクライアントはその前にタイムアウトし、サーバーを 壊れていると報告します。クラスの開始前にcurlでウォームアップしてください。

オプションB — その他のDocker不要のホスト

ホスト

方法

Railway

リポジトリを接続。NixpacksがPythonを自動検出。スタートコマンドをpython server.py --httpに設定。

Hugging Face Spaces

無料、スリープなし。Docker Space、またはカスタムapp.pyシムを使ったGradio Space。

Google Cloud Run

gcloud run deploy --source . — ソースからビルド、Dockerfile不要。

任意のVPS

pip install -r requirements.txt、その後systemdまたはtmuxで実行。

オプションC — Fly.io

fly launch --no-deploy
fly secrets set GROQ_API_KEY=gsk_...
fly deploy

エンドポイント:https://<your-app>.fly.dev/mcp

オプションD — 任意のコンテナホスト

Dockerfileは、コンテナを必要とするホストのために保持されています。Railway、 Cloud Run、ECS、VPSで動作します:

docker build -t llm-toolkit-mcp .
docker run -p 8000:8000 -e GROQ_API_KEY=gsk_... llm-toolkit-mcp

デプロイを確認する

1回のcurlでサーバーが生きていてMCPを話していることを確認できます:

curl -X POST https://your-app.onrender.com/mcp -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

serverInfoブロックにllm-toolkitが含まれているはずです。

デプロイされたサーバーにクライアントを接続する

claude mcp add --transport http llm-toolkit https://your-app.onrender.com/mcp

学生はその1行を貼り付けるだけで、すぐに4つのツールを使えるようになります。これがワークショップ全体の 見せ場です — インストール不要、キー不要、自分のマシンにPython不要。

公開共有 — 最初にこれを読んでください

認証のないデプロイされたMCPサーバーはインターネット全体に公開されています。 URLを知った誰でもあなたのツールを呼び出すことができ、すべての呼び出しであなたのGroqクォータが 消費されます。

ワークショップでは通常これで問題なく、Groqの無料枠がそれを可能にしています:クォータが 尽きるとHTTP 429エラーが発生するだけで、請求書が届くわけではありません。障害モードは 「ツールが応答しなくなる」であり、「驚きの請求書」ではありません。

その背後に有料キーを置いた瞬間に、それは問題になります。その場合は、URLを共有する前に 認証を追加してください — MCP SDKのauthパラメータ、または前面のAPIゲートウェイを使用します。

どちらにしても守る価値のある2つの習慣:

  • URLを半機密として扱う。クラス内で共有し、公に投稿しない。

  • ワークショップ後にキーをローテーションする。ダッシュボードの1クリックです。

パート5 — 明示的に教える価値のあること

docstringがAPIです。 モデルはdocstringと型ヒントを読んでツールを選択します。 あいまいなdocstringは、決して呼び出されないツールを意味します。これはファイル全体の中で 最も効果の高い単一の要素です。

1つの関数がプロバイダーを所有します。 すべてのツールはcall_llm()を呼び出します。Groqを OpenAI、Anthropic、またはローカルのOllamaに交換するということは、その1つの関数を編集することを意味します — 4つのツールは決して変更されません。これをライブでデモすると、強く印象づけられます。

エラーは文字列として返し、raiseしない。 call_llmはGroqErrorをキャッチし、 メッセージをテキストとして返します。クライアントはデッドなツール呼び出しの代わりに、ユーザーに 実際のエラーを表示します。

stateless_http=True はスティッキーセッションがないことを意味し、サーバーがロードバランサーの 背後でスケールできるようにします。セッションごとの状態を追加する場合のみオフにします。

キーを決してコミットしない。 .envはgitignoreされ、render.yamlはsync: falseを使用し、 Flyはfly secretsを使用します。

公開HTTPサーバーはデフォルトでオープンです。 これには認証がありません — デモには問題ありませんが、 本番環境には適しません。実際のデプロイでは、SDKのauthパラメータを介したOAuthを追加するか、 APIゲートウェイの背後に配置します。

バージョンのずれは現実です。 MCP Python SDK 2.0はFastMCPをMCPServerにリネームしました。 オンラインのほとんどのチュートリアルはまだFastMCPを示しており、新しいインストールでは 失敗します。ブログ記事を信頼する代わりに、インストールされたパッケージを読むことを教える良い機会です。


パート6 — クラス向けの演習

  1. sentiment(text)ツールを追加します。(summarizeをコピーし、システムプロンプトを変更。)

  2. ask_llmがmax_tokens引数を受け入れるようにし、Inspectorでスキーマが自動的に 更新されるのを確認します。

  3. どのツールにも触れずに、call_llmを別のプロバイダーに向けます。

  4. プロセスが処理したツール呼び出しの数を報告するリソースconfig://usageを追加します。 (ヒント:モジュールレベルのカウンター。)

  5. 意図的にdocstringを壊し、モデルにそのツールを使用するよう依頼します。ツールの選択に 失敗するのを観察します。それが教訓です。


パート7 — 他の人に使ってもらう

ツールを他の人に渡すことは、3つの別々の問題です:到達可能、接続可能、 発見可能。この順序で解決します。

1. 到達可能。 localhost上のサーバーは、1人だけが使用できます。デプロイし(パート4)、 公開URLを持ちます。これが完了するまで、以下のどれも機能しません。

2. 接続可能。 人々にUSING-IT.mdを渡します — Claude Code、Claude Desktop、 Cursor用のコピペ設定とトラブルシューティング表を含むスタンドアロンページです。 ワークショップでは、リモートルートが使用するものです:学生は1行を貼り付けるだけで、 Pythonもリポジトリも自分のAPIキーもなしに動作するツールを手に入れます。

3. 発見可能。 クラスだけでなく、見知らぬ人にも見つけてもらいたい場合のみ:

チャンネル

得られるもの

GitHubトピック mcp、mcp-server、model-context-protocol

無料の検索トラフィック

公式MCPレジストリ

クライアントの「サーバーを閲覧」UIに表示

awesome-mcp-serversコミュニティリスト

リポジトリを追加するPR

Smithery / Glamaおよび類似のディレクトリ

ホスト型インストールボタン

レジストリの要件は急速に変化します — 公開する前に、現在のMCPレジストリドキュメントで マニフェスト形式を確認してください。

正直な限界についての注意。 人々は、自分がまだできないことをするときにMCPサーバーを 採用します。これは汎用LLMをラップしたものであり、ほとんどのクライアントはすでに 組み込んでいます — プロトコルを教えるには完璧ですが、製品としては弱いです。あなたの データベース、あなたの内部API、またはあなたの独自データに到達するサーバーこそが、 実際のユーザーを獲得するものです。クラスに声を大にして言う価値があります。


パート8 — 本番環境対応にするもの

ワークショップ版と本番版は、MCPとはまったく関係のない点で異なります。 これがそのリストであり、各項目はこのサーバーの構築中に実際に発生した障害が原因で存在します。

キーの保護

公開MCPエンドポイントは公開支出エンドポイントです:すべての呼び出しはあなたにコストがかかります。

Guard

Env var

Default

Why

Bearer auth

MCP_AUTH_TOKEN

empty = open

Gate access once a paid key is behind it

Rate limit

RATE_LIMIT_PER_MIN

30/IP

One script cannot drain your quota

Input cap

MAX_INPUT_CHARS

20000

A pasted novel is rejected before it costs tokens

Body cap

MAX_BODY_BYTES

1 MB

Oversized payloads die before parsing

認証はデフォルトでオフなので、サーバーは無料キーを使ったワークショップのために開いたままにできます。有料キーをパブリックURLに向ける前にオンにしてください:

MCP_AUTH_TOKEN=$(python -c "import secrets;print(secrets.token_urlsafe(32))") python server.py --http

その後、クライアントは Authorization: Bearer <token> を送信します。

プロバイダーを乗り切る

モデルは予告なく廃止されます。 Groqは開発中に llama-3.3-70b-versatile を削除しました — 07:15には動作していたのに、1時間後には404が返りました。すべてのツールが一度に壊れ、404は「サーバーが壊れている」と読めるのであって、「ベンダーが移動した」とは読めません。

MODEL_CHAIN がこれを修正します:モデルが見つからないエラーの場合、呼び出しは失敗せずに次のモデルにロールします。その他のエラー — 不正なキーやレート制限 — は即座に失敗します。なぜなら、それらを5つのモデルでリトライしても時間の無駄だからです。

タイムアウト(LLM_TIMEOUT_SECONDS)とリトライ(LLM_MAX_RETRIES)はベンダーSDKに委ねられており、これらはすでに適切なバックオフを実装しています。

ヘルスチェック

/health はプレーンなJSONを返します。決して /mcp をヘルスチェックしないでください — それは設計上開いたままのSSEストリームであり、プローブがハングし、プラットフォームがサービスを停止と判断し、クラッシュのように見える再起動ループが発生します。これにより実際にデバッグサイクルが1回無駄になりました。

ロギング

すべてはstderrに送られます。決してstdoutには送りません。stdioモードでは、stdoutがJSON-RPCストリームを運ぶため、1つの迷子の print() がプロトコルを破壊します。これは、MCPサーバーをデバッグ中に壊す最も一般的な方法です。

MCPレベルのミドルウェアは、各メソッドをその所要時間とともにログに記録し、両方のトランスポートで動作します。

テストとCI

pytest tests/ はAPIキーなしでオフラインで実行され、費用はかかりません。レートリミッターのウィンドウ期限、入力上限、モデルフォールバック、ツールスキーマの保持、設定の検証をカバーします。

GitHub Actionsは、3.11と3.12でスイートを実行し、サーバーを起動し、gitの全履歴をコミットされたAPIキーについてスキャンします — 回復不可能な障害です。なぜなら、プッシュされたキーはその瞬間に公開されるからです。

既知の制限

クラスに対してまだ不足しているものについて正直である価値があります:

  • レート制限はプロセスごとです。 N個のインスタンスにスケールすると、制限のN倍を許可します。それが問題になる前にRedisに交換してください。

  • 共有トークン1つで、ユーザーごとのキーはありません。 クラスには十分ですが、顧客向けではありません。

  • 使用量の計測はありません。 誰が何を使ったかを知ることはできません。

  • 無料枠のコールドスタート はアイドル後も30〜50秒かかります。


ファイルマップ

File

Why it exists

server.py

The entire server — tools, resource, prompt

requirements.txt

mcp[cli] + groq + python-dotenv

Dockerfile

Container for any host

render.yaml

One-click Render deploy

fly.toml

Fly.io deploy

.env.example

Which env vars exist

.mcp.json.example

Client config to copy

USING-IT.md

Standalone page to hand to users

guards.py

Auth, rate limiting, size caps, logging

tests/

Offline test suite, no API key needed

.github/workflows/

CI: tests, boot check, secret scan

Related MCP Connectors

Related MCP Servers