0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

連載:AIに仕事を奪われる不安から始めるハーネス作成入門 第8回 MCPサーバーの最小実装 — FastMCPで最初のツールを作る

0
Last updated at Posted at 2026-06-25

MCPサーバーの最小実装 — FastMCPで最初のツールを作る

連載:AIに仕事を奪われる不安から始めるハーネス作成入門
全24回(週2回・12週間)|第8回 / 24
この連載は、AIに仕事を奪われるかもしれないという不安を「ハーネス(AIエージェント制御の仕組み)」を自分で作る行動に変えるシリーズです。

はじめに:この記事で扱う不安

「MCPサーバーを作ってみたいけど、何から書けばいいのかわからない」——前回(第7回)でMCPサーバーの分割設計を行い、MCP責務表まで作りました。しかし、設計図があっても「最初の1行目」が書けないと手が止まります。

今回は、その不安を解消するためにFastMCPを使った最小実装を体験します。「思ったより少ないコードで動く」という感覚を掴むことが目標です。

対象読者

  • SE・プログラマ経験3年以上の方
  • Pythonの基本構文(関数、デコレータ、型ヒント)を理解している方
  • MCPサーバーを実際に作りたい方
  • 前回までの連載を読んでいる方(未読でもOK)

結論

MCPサーバーの最小実装は、Pythonの関数にデコレータを付けるだけです。@mcp.tool() を付けた関数が、そのままAIエージェントから呼び出せるツールになります。SE経験者がAPI設計で培った「引数の型を決める」「戻り値の形式を統一する」スキルがそのまま活きます。

なぜ「最小実装」から始めるべきか

第7回で作ったMCP責務表には4つのMCPサーバーがありました。全部を一度に作ろうとすると、以下の問題が起きます。

  • 設計の迷い: 最初から完璧なインターフェースを設計しようとして手が止まる
  • 動作確認の遅れ: コードが増えるほど、最初の動作確認までの時間が長くなる
  • モチベーションの低下: 成果が見えないまま設計だけが進む

SEなら「まずプロトタイプを作って動かしてから改善する」アプローチに馴染みがあるはずです。MCPサーバーでも同じです。まず1つのMCPサーバーに2つのツールを実装して動かすことから始めましょう。

既存のSE / プログラマ経験をどう活かせるか

SE経験 MCPサーバー実装への活用
API設計(RESTful) ツールの引数・戻り値設計
インターフェース定義(型定義) 型ヒントによるツールシグネチャ設計
エラーハンドリング設計 dictベースの統一エラー応答
ドキュメント作成 docstringによるツール説明
単体テスト設計 ツール単位のテスタビリティ確保

FastMCPとは何か

FastMCPは、MCPサーバーをPythonで手軽に構築するためのフレームワークです。最小5行でMCPサーバーが動きます。

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-first-mcp")

@mcp.tool()
def hello() -> str:
    """最初のMCPツール"""
    return "Hello from MCP!"

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

これだけで、AIエージェントから hello ツールを呼び出せるMCPサーバーが完成します。

重要なポイント

  • FastMCP("サーバー名") でサーバーインスタンスを作る
  • @mcp.tool() デコレータで関数をツールとして登録
  • mcp.run() でstdio(標準入出力)トランスポートとして起動
  • HTTPサーバーを自分で立てる必要がない

最小MCPサーバーの実装(今回の主要成果物)

第7回で設計した article_mcp(記事の作成・管理)の最初のツールを実装します。

import os
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("article-mcp")

@mcp.tool()
def health_check() -> dict:
    """MCPサーバーの状態を確認する。"""
    return {
        "ok": True,
        "data": {
            "server_name": "article-mcp",
            "version": "0.1.0",
            "status": "running"
        }
    }

@mcp.tool()
def create_article_project(
    platform: str,
    title_working: str,
    purpose: str = "",
    language: str = "ja"
) -> dict:
    """記事プロジェクトを新規作成する。

    Args:
        platform: 投稿先プラットフォーム('qiita' または 'note')
        title_working: 仮タイトル
        purpose: 記事の目的(オプション)
        language: 言語コード(デフォルト: 'ja'"""
    # バリデーション
    valid_platforms = ["qiita", "note", "both"]
    if platform not in valid_platforms:
        return {
            "ok": False,
            "error": {
                "message": f"platformは {valid_platforms} のいずれかを指定してください"
            }
        }

    article_id = f"art_{platform}_{title_working[:10]}"
    return {
        "ok": True,
        "tool": "create_article_project",
        "article_id": article_id,
        "data": {
            "platform": platform,
            "title_working": title_working,
            "purpose": purpose,
            "language": language
        }
    }

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

コード解説

ツールのシグネチャ設計のポイント

MCPツールのシグネチャ(引数と戻り値)は、AIエージェントとの契約です。SE的にはAPIのインターフェース定義と同じです。

設計要素 役割 SE的対応
関数名 AIがツールを選ぶ手がかり APIエンドポイント名
docstring AIがツールの用途を判断する APIドキュメント
型ヒント AIが渡すべきデータ型を知る リクエストスキーマ
デフォルト値 AIが省略可能な引数を知る オプショナルパラメータ
戻り値のdict AIが結果を解釈する レスポンススキーマ

注意すべき3つのルール

  1. 戻り値は常にdictで統一する: {"ok": True/False, ...} の形式にすると、成功/失敗の判定がAIにもプログラムにも容易
  2. 例外を投げずにdictでエラーを返す: MCPではJSON-RPCの仕様上、Python例外よりもdictベースのエラー応答が安全
  3. docstringは必ず書く: AIエージェントはdocstringを読んでツールを選ぶ。空だと呼ばれない可能性がある

ローカルでの動作確認

stdioモードでの起動

# 仮想環境を有効化
python -m venv .venv
.venv\Scripts\activate  # Windows
# source .venv/bin/activate  # Mac/Linux

# 依存パッケージをインストール
pip install mcp

# サーバー起動テスト(起動後 Ctrl+C で停止)
python src/server.py

AI開発環境への接続

AI開発環境(Antigravity、Claude Desktopなど)にMCPサーバーを登録するには、設定ファイルにサーバー情報を追加します。

{
  "mcpServers": {
    "article-mcp": {
      "command": "python",
      "args": ["src/server.py"],
      "cwd": "/path/to/your/project"
    }
  }
}

登録後、AIエージェントに「health_checkを呼んで」と指示すると、サーバーの状態が返ります。自分が書いたPython関数がAIから呼び出される——この体験が、ハーネス開発の第一歩です。

実装・設計時の注意点

注意点 理由 対策
最初のツールは2〜3個に絞る ツールが多すぎるとAIが迷う health_check + 業務ツール1〜2個
型ヒントを省略しない AIが引数の型を推測できなくなる 全引数にstr/int/bool/dictを明記
docstringを省略しない AIがツールの用途を判断できない 1行でツールの目的を書く
グローバル変数に依存しない テスト時にモックが困難 引数で必要な情報を受け取る
非同期は後回し asyncの複雑さで手が止まる まず同期関数で動かす

今回の小さな成果物(チェックリスト)

  • FastMCPで health_check ツールを持つMCPサーバーを作る
  • もう1つ、自分の用途に合った業務ツールを追加する
  • ローカルでstdioモード起動を確認する
  • AI開発環境に接続し、ツールが呼び出されることを確認する
  • (発展)第7回のMCP責務表の他のMCPサーバーも最小実装してみる

まとめ

今回は、FastMCPを使ったMCPサーバーの最小実装を体験しました。

キーポイント

  • MCPサーバーの実装は「関数 + デコレータ」だけで始められる
  • ツールのシグネチャ設計は、SE経験のAPI設計スキルがそのまま使える
  • 戻り値はdictで統一、エラーもdictで返す
  • docstringと型ヒントが、AIエージェントとの「契約」になる
  • まず2〜3ツールで動かし、育てていくアプローチが現実的

次回予告

第9回「RAG / Knowledge MCPがSE経験者に向いている理由」 では、知識基盤の世界に踏み込みます。

扱う内容

  • RAG(Retrieval-Augmented Generation)の基本概念
  • Knowledge MCPがハーネスに果たす役割
  • SE経験者が「DB設計」の知見でRAGを理解できる理由

得られる成果物

  • 知識管理方針(何を、どう登録し、どう検索するか)

試しておくこと

  • 今回作ったMCPサーバーを手元に残しておく(次回以降、knowledge_mcpを追加する際の参考にします)
  • 「ドキュメントを検索する」経験を振り返る(社内Wiki、Confluence、Notionなど)
  • 「あの情報どこだっけ」と困った経験をメモしておく

連載目次

タイトル 状態
第1回 AIに仕事を奪われる不安から「ハーネス作成」を学ぶ理由
第2回 最小構成のAIハーネスをMermaidで設計してみる
第3回 AIにコードを書かせるだけでは足りない理由
第4回 task_request / task_resultでAIエージェントの仕事を契約化する
第5回 SEの設計経験をAIエージェント制御に転用する
第6回 ハーネス用のディレクトリ構成とREADMEを作る
第7回 MCPサーバーを「道具箱」として分割する考え方
第8回 MCPサーバーの最小実装 — FastMCPで最初のツールを作る 📖
第9回 RAG / Knowledge MCPがSE経験者に向いている理由 次回
第10回 設計メモをKnowledge MCPへ登録する前提のMarkdownテンプレート
第11回 ローカルLLMを使うべき場面と外部LLMに任せる場面
第12〜24回 (以降の回)

連載:AIに仕事を奪われる不安から始めるハーネス作成入門
著者: @singula00991 | 週2回更新
次回は「RAG / Knowledge MCPがSE経験者に向いている理由」を予定しています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?