MCPサーバーを自作してみた|最小構成で作る入門

MCPサーバー|MCPサーバーを実装してみた(簡易版)— 最小構成で作るブログ検索サーバー のアイキャッチ画像 生成AI・LLM

※この記事には広告・アフィリエイトリンクを含む場合があります。

「MCPサーバー、結局どう作るの?」に答える回

最近あちこちで「MCP」「MCPサーバー」って言葉を見かけるけど、いざ自分で作るとなると身構えちゃうよね。今回はその一番小さいやつを実際に動かしてみた記録。「あ、MCPサーバーってこれだけで作れるんだ」を体験してもらうのがゴールだ。

ヤモリくん(困り)の立ち絵

ヤモリくん「先生、MCPサーバーって名前からして難しそうで、身構えちゃうんですけど…」
トカゲ先生「わかるわ。でも最小のMCPサーバーなら、実質20行くらいで動く。今日はそれをオレと一緒に組んでいこうぜ」

トカゲ先生(笑顔)の立ち絵

なお、この記事は軽量版。Claude Desktopへの登録まで含めた完全な手順は別途 note の詳細版でやる。ここでは「作る流れ」と「全体像」を掴むのが目的だ。

そもそもMCPって何?

MCP(Model Context Protocol)は、AIアプリを外部のデータやツールにつなぐためのオープンな標準規格。公式サイトでは「AIアプリ版のUSB-Cポート」と表現されてる。USB-Cが機器同士をつなぐ共通の差込口なら、MCPはAIと外部システムをつなぐ共通の差込口ってわけ(modelcontextprotocol.io)。

登場人物は3つ。

  • ホスト: Claude Desktop や Claude Code みたいなAIアプリ本体
  • クライアント: ホストの中で、サーバー1つにつき1つ作られる接続係
  • サーバー: 「こういう道具あるよ」とAIに機能を提供するプログラム ← 今回作るのはコレ

ホストからクライアントを経由してMCPサーバーに接続し、Tools・Resources・Promptsの3種類の機能を提供する構成図

そしてサーバーがAIに渡せる機能は、公式に3種類ある。Tools(AIが呼び出せる関数)/Resources(読み込めるデータ)/Prompts(定型テンプレ)だ。今回はこの中で一番わかりやすい「Tools」だけを使う(公式アーキテクチャ解説)。

ヤモリくん(通常)の立ち絵

ヤモリくん「Toolsって、AIが自分で呼び出す関数のことですか?」
トカゲ先生「そう。公式の言い方だと『AIアプリが実行できる関数』。天気を調べる、DBを検索する、みたいな“行動”をAIに持たせるやつだな」

トカゲ先生(通常)の立ち絵

今回作るMCPサーバー

題材は「自分のブログ記事を検索できるMCPサーバー」。ツールは2つだけにした。

  • search_articles(keyword): キーワードで記事を探す
  • get_article(title): タイトル指定で要約とURLを取る

これをAIに登録すると、AIが自分のブログを検索して答えられるようになる、という寸法。使ったのは公式のPython SDKに入ってる FastMCP。環境は mcp SDK 1.28.1 / Python 3.10 だ。

サーバー本体はこれだけ

いきなり核心。実際に動かしたコードから、大事なところだけ抜き出すとこうなる。

from mcp.server.fastmcp import FastMCP

# サーバーに名前を付けて作る。この名前がクライアント側の表示名になる。
mcp = FastMCP("blog-tools")

# ARTICLES は記事のサンプルデータ(辞書のリスト)。ここでは省略。

@mcp.tool()
def search_articles(keyword: str) -> list[dict]:
    """キーワードでブログ記事を検索する。

    タイトル・タグ・要約のいずれかにキーワードを含む記事を返す。

    Args:
        keyword: 検索したいキーワード(例: "RAG"、"エージェント")
    """
    kw = keyword.lower()
    hits = []
    for a in ARTICLES:
        haystack = (a["title"] + a["summary"] + " ".join(a["tags"])).lower()
        if kw in haystack:
            hits.append({"title": a["title"], "url": a["url"]})
    return hits


if __name__ == "__main__":
    mcp.run()
ヤモリくん(驚き)の立ち絵

ヤモリくん「…え、これだけ?普通のPython関数に `@mcp.tool()` を付けただけに見えます」
トカゲ先生「それがFastMCPの美味しいとこ。関数の“型ヒント”と“docstring”を、そのままAI向けの説明書に変換してくれるんだ」

トカゲ先生(ひらめき)の立ち絵

ここがポイント。keyword: str という型ヒントが、そのまま「この道具は文字列を1個受け取る」という仕様(スキーマ)になる。docstringはそのまま「この道具が何をするか」の説明になる。公式SDKの言葉を借りると『JSONスキーマを書く必要はない。型ヒント付きの関数とdocstring、それだけ』だ(MCP Python SDK)。手書きのJSON定義や、リクエストの解析コードは一切いらない。

最後の mcp.run() は、transportを省略すると stdio(標準入出力) で待ち受ける。stdioはローカルのプロセス同士を標準入出力でつなぐ通信方式で、Claude Desktop や Claude Code はこの stdio 経由でサーバーを起動して繋ぐ(公式アーキテクチャ解説)。ローカルで動かす分にはこれで十分だ。

動かして確かめる

サーバーだけあっても喋る相手がいない。そこで Claude Desktop がやってることを手動で真似する小さなクライアントを書いて、繋いでみた。中でやってるのはこの4ステップだけ。

  1. サーバーを stdio で起動して接続
  2. initialize(あいさつ・機能の交換)
  3. list_tools(どんな道具ある?)
  4. call_tool(実際に道具を呼ぶ)

クライアントがサーバーに接続してinitialize・list_tools・call_toolの順に通信する4ステップのフロー

実行した結果がこれ(実際のログ)。

[接続成功] サーバー名: blog-tools v1.28.1
[ツール一覧] ['search_articles', 'get_article']

--- search_articles(keyword='RAG') ---
  text: {
  "title": "RAGって何?",
  "url": "https://example.com/rag"
}

--- get_article(title='ハルシネーションとは?') ---
  text: {
  "title": "ハルシネーションとは?",
  "url": "https://example.com/hallucination",
  "summary": "AIがもっともらしい嘘を自信満々に答えてしまう現象と、その対策。"
}

ちゃんと動いた。search_articles に “RAG” を渡したら RAG の記事が返り、get_article にタイトルを渡したら要約まで返ってきてる。

ヤモリくん(驚き)の立ち絵

ヤモリくん「先生、`v1.28.1` ってサーバーのバージョンですか?そんなの指定してました?」
トカゲ先生「鋭いな。オレは `FastMCP(“blog-tools”)` で名前しか付けてない。バージョンを省くと、入れてるSDKのバージョン(1.28.1)がそのまま表示されるんだ。細かいけど『あ、これはSDK由来なんだ』って気付けると理解が一段深まるぞ」

トカゲ先生(笑顔)の立ち絵

中では何が起きてるのか

流れをもう一段ほどこう。クライアントとサーバーは JSON-RPC 2.0 というルールでメッセージをやり取りしてる。最初の initialize でお互いの機能をすり合わせ、tools/list で道具の一覧を取り、tools/call で実行する(公式アーキテクチャ解説)。

面白いのはツールの戻り値だ。さっきのログで text: の中身がJSONになってたよね。MCPではツールの結果は content という配列で返り、その中の各要素に type(今回は "text")と本文が入る。だからクライアント側は result.content[0].text で中身を読んでる。AIが実際に読むのも、このテキスト部分だ(公式アーキテクチャ解説)。

ヤモリくん(笑顔)の立ち絵

ヤモリくん「関数を書いてデコレータを付けるだけで、ここまでの仕組みに乗っかれるんですね…」
トカゲ先生「そういうこと。プロトコルの面倒な部分はFastMCPが全部やってくれる。オレたちは『どんな道具を出すか』だけ考えればいい」

トカゲ先生(通常)の立ち絵

ちなみに今回サンプルに入れてた「RAGって何?」は、実際にこのブログでも解説してる。気になる人はRAGって何?もどうぞ。

まとめ

  • MCPサーバーは「AIに道具を渡す」ための小さなプログラム。最小なら数十行で動く
  • FastMCP を使えば、普通のPython関数に @mcp.tool() を付けるだけ。型ヒントとdocstringがそのまま仕様になる
  • mcp.run() は stdio で待ち受け、Claude Desktop / Claude Code から呼び出せる
  • 実行すると initialize → tools/list → tools/call の流れで、AIが道具を使える
ヤモリくん(笑顔)の立ち絵

ヤモリくん「思ってたより全然コワくなかったです!」
トカゲ先生「な?まずは動くMCPサーバーを1個持っとくと、あとは道具を足すだけで育つ。次は自分のブログや好きなAPIに繋いでみようぜ」

トカゲ先生(ひらめき)の立ち絵

完全な手順やClaude Desktopへの登録方法は、note の詳細版で解説予定。まずは「最小のMCPサーバー、自分で作れた」って感覚を持ち帰ってもらえたら十分だ。

参考文献

RAGって何--AIに「カンペ」を渡す技術を5分で理解する
RAGとは、AIが回答を生成する前に外部の信頼できる文書を検索し、その結果を参考資料としてAIに渡す技術である。この仕組みにより、学習データに含まれない最新情報や専門的な社内情報にも対応でき、AIがもっともらしい嘘をつく「ハルシネーション」...

プログラミングを基礎から学びたい方へ

手を動かして作る楽しさが分かってきたら、次はプログラミングの土台を固めるのが近道です。未経験からでも実践形式で学べる講座を紹介します。

プログラミングを基礎から学ぶ(スキルハックス)

コメント

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