🗓️ この記事の前提(2026-09-03 時点)
  • Python 3.11.9 / MCP 公式 SDK mcp 2.1.1 / Windows 11 で書いています
  • SDK は 1.x と 2.x で名前が変わりましたFastMCPMCPServer にリネームされています。1.x 向けのコードをそのまま写すと動きません
  • コードは全文を載せています。ハードウェアは不要で、PC だけで最後まで通ります

🧭 はじめに:使う側から、作る側へ

これまでこのブログでは、MCP サーバを 使う側として2本書いてきました。ESP-IDF 6.0 に内蔵された MCP サーバに Claude Code をつなぐ話と、それが Windows で詰まったのでスキルで idf.py を直接叩いた話です。どちらも「誰かが用意したサーバ」が前提でした。

用意されていなければ、どうするか。自分で書きます。 そして書いてみると、拍子抜けするくらい短いことがわかります。

MCP サーバの正体は、普通の関数に飾りを1行つけて、起動するだけです。フレームワークを覚える必要も、プロトコルの中身を読む必要もありません。JSON-RPC も、初期化のハンドシェイクも、ツール一覧の返し方も、全部 SDK の向こう側にあります。こちらがやるのは「この関数を使わせたい」と宣言することだけです。

この記事では、題材として 電子工作の計算3つを選びました。このサイトには LED 抵抗・555・RC フィルタの計算ツールが置いてあり、その計算ロジックをそのまま MCP サーバに移植します。純粋な計算なので、外部サービスにも実機にもつながりません。オフラインで、PC だけで、最後まで動きます。

そして計算ツールという題材は、MCP でいちばん大事なところを露骨に見せてくれます。あなたが書いた関数の説明文が、そのまま AI への指示書になるという点です。ここを外すと、サーバは繋がっているのに AI が正しく呼べない、という一番しんどい失敗をします。

全体像はこうです。

flowchart TD U["あなた
「LED の抵抗は?」"] --> CC["Claude Code"] CC -->|"stdio で
ツールを呼ぶ"| S["自作サーバ
server.py"] S --> T1["led_resistor()"] S --> T2["ne555_astable()"] S --> T3["rc_filter_cutoff()"] T1 -->|"structured_content
計算結果と注意点"| A["回答
「300Ω・E24 の直近上位」"]

🔍 MCP サーバは何でできているか

いきなり3つ書く前に、ツール1個だけのサーバを作って動かします。ここが分かれば、あとは関数を増やすだけです。

用意するもの

Python 3.10 以上と、公式 SDK が1つ。仮想環境を切って入れます。

py -m venv .venv
.venv\Scripts\python.exe -m pip install mcp
💡 ワード解説:stdio という繋ぎ方

MCP のサーバとクライアントは、標準入出力(stdio) でやり取りできます。クライアントがサーバを子プロセスとして起動し、その stdin / stdout を通して JSON をやり取りする方式です。

つまり、あなたのサーバはネットワークを一切扱いません。ポートを開くことも、認証を実装することもありません。ローカルで完結するツールなら、これがいちばん簡単で安全な選択肢です。

(もう一方の選択肢が HTTP で、こちらはリモートのサーバを複数人で共有するときに使います。ESP-IDF の公式ドキュメント検索サーバがこの形です。)

最小のサーバ

これで動きます。

from mcp.server import MCPServer

server = MCPServer("hello")


@server.tool()
def add(a: float, b: float) -> float:
    """2つの数を足す。"""
    return a + b


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

要点は3つだけです。

  • MCPServer("hello") — サーバの名前。クライアント側で表示される識別子になります
  • @server.tool() — この飾り(デコレータ)を付けた関数が、AI から呼べる道具になります
  • server.run() — 既定で stdio 待ち受けに入ります。引数なしでかまいません

server.run() は同期関数です。asyncio.run() で包む必要はありません(ツール側を async def で書くこともできますが、そのときも起動は server.run() のままです)。

動いていることを確かめる

起動しても、画面には何も出ません。stdio サーバはクライアントに起動されてはじめて意味を持つので、単体で走らせても入力待ちで黙っているだけです。ここで「動いていないのでは」と不安になるところですが、黙っているのが正常です。

Claude Code に登録する前に、SDK のクライアント API で自分で叩いてしまうのが確実です。サーバを子プロセスとして起動し、ツール一覧を取って、実際に呼びます。

import asyncio
import sys
from mcp import ClientSession, StdioServerParameters, stdio_client


async def main():
    params = StdioServerParameters(command=sys.executable, args=["server.py"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            init = await session.initialize()
            print("server:", init.server_info.name)

            for tool in (await session.list_tools()).tools:
                print(" -", tool.name, "|", list(tool.input_schema["properties"]))

            result = await session.call_tool("add", {"a": 2, "b": 3})
            print("result:", result.structured_content)


asyncio.run(main())
server: hello
 - add | ['a', 'b']
result: {'result': 5.0}

この確認は、Claude Code に持っていく前に必ずやっておく価値があります。 登録してから「繋がらない」と悩むと、原因がサーバ側なのか登録の書き方なのか切り分けられません。ここで通っていれば、以後のトラブルは登録側だと確定できます。このスクリプトは、後で作る3ツール版をひととおり呼んで検証する check.py としてリポジトリに入れてあります。

⚠️ フィールド名は 2.x で snake_case になりました

上のコードで init.server_inforesult.structured_content と書いているところは、1.x では serverInfo / structuredContent でした。ネットに転がっているサンプルは 1.x のものが多いので、AttributeError が出たらここを疑ってください。

親切なことに、SDK は 'InitializeResult' object has no attribute 'serverInfo'. Did you mean: 'server_info'? と正解を教えてくれます。


✍️ ツールの説明は、AI 向けに書く

@server.tool() を付けると、SDK は関数の見た目をそのまま読み取ってクライアントに渡します。渡るのは次の4つです。

関数の要素 AI に何として届くか
関数名 ツールの名前(led_resistor
引数名 パラメータの名前(vcc_v, vf_v, if_ma
型注釈 パラメータの型と必須/任意の区別
docstring ツールの説明文そのもの

つまり、docstring は人間へのコメントではなく、AI への仕様書です。ここが薄いと、サーバは正常に繋がっているのに AI が引数を取り違える、という失敗をします。

単位は引数名に埋め込む

いちばん効くのがこれです。電子工作の計算では、mA と A、µF と F、kΩ と Ω を取り違えると結果が3桁ずれます。型注釈は float としか言えないので、単位は名前で伝えるしかありません。

def led_resistor(vcc: float, vf: float, current: float):   # ← A? mA?
def led_resistor(vcc_v: float, vf_v: float, if_ma: float): # ← 迷いようがない

current と書かれた引数に、AI は 20 を渡すべきか 0.02 を渡すべきか判断できません。if_ma なら 20 一択です。曖昧さを docstring で補うより、名前で消すほうが確実です。

Before / After

同じ関数の説明を、雑に書いた場合と丁寧に書いた場合で並べます。

✗ 雑な説明 ✓ AI が正しく呼べる説明
1行目 """LED の計算""" """LED を定電流で光らせるための電流制限抵抗を計算する。"""
何をするか 書いていない R = (Vcc - Vf) / If で理論値を出し、E 系列の直近上位に丸めた値と実電流・消費電力・推奨定格まで返す
引数 書いていない vcc_v: 電源電圧 [V]。マイコンのピンで駆動するならその出力電圧(5 や 3.3)。
既定値 書いていない series: "E12" か "E24" を指定する。既定は "E24"。
使えない場面 書いていない 単安定モードのパルス幅を求めたい場合、このツールは使えない。
失敗条件 書いていない ToolError: Vcc が Vf 以下(LED が点灯しない)とき。

右側で効いているのは、「何ができるか」だけでなく「何ができないか」を書いている点です。555 の計算ツールに単安定モードの計算を頼まれたとき、docstring に「このツールは使えない」と書いてあれば、AI は無理に呼ばずに済みます。書いていなければ、それらしい引数をでっち上げて呼びます。

選択肢は文字列で殴らず、候補を書く

series: str = "E24" のような引数は、取りうる値を docstring に列挙します。列挙しておけば AI はそこから選び、外した値を渡してきたらこちらで弾けます。

    if series not in ("E12", "E24"):
        raise ToolError('series は "E12" か "E24" を指定してください。')

🔧 実物に育てる:電子工作の計算3つ

説明の書き方が決まったので、中身を本物にします。サイトの計算ツールと同じロジックを移植して、3つのツールにします。

ツール 計算
led_resistor R = (V_{CC} - V_F) / I_F と、E 系列への丸め・実電流・消費電力・推奨定格
ne555_astable f = 1 / ((t_H + t_L))t_H = \ln 2 \cdot (R_1 + R_2) Ct_L = \ln 2 \cdot R_2 C
rc_filter_cutoff f_c = 1 / (2 \pi R C) と、時定数・10→90% 応答時間

戻り値は「数字だけ」にしない

計算結果を float で返すと、それは正しいのですが、もったいないです。AI は返ってきた 300.0 を見て、単位を推測し、丸め方を決め、注意点を自分で考えることになります。せっかくこちらが知っていることを、捨てて渡すことになります。

そこで戻り値を構造体にして、数値と一緒に「結論の1文」と「注意点」を返します

from dataclasses import dataclass


@dataclass
class LedResistorResult:
    resistance_ohm: float
    """理論値。R = (Vcc - Vf) / If"""

    e_series_ohm: float
    """E 系列で実際に買える値(直近上位)"""

    actual_current_ma: float
    """e_series_ohm を使ったときに実際に流れる電流"""

    power_w: float
    """抵抗が消費する電力"""

    recommended_rating: str
    """推奨する抵抗の定格(消費電力の 2 倍以上)"""

    summary: str
    """結論を1文にまとめたもの"""

    warning: str | None = None
    """設計上の注意点。無ければ None"""

返り値の型注釈に dataclass を書いておくと、SDK が自動で JSON スキーマを作り、structured_content として返してくれます。こちらが変換コードを書く必要はありません。

summarywarning が肝です。たとえば LED に 25mA 流そうとして電源が 5V 以下なら、こう返します。

マイコンのピンから直接駆動する場合、1 ピンあたりの出力電流の上限にも注意してください。上限は機種によって数 mA〜数十 mA と大きく違い、ポート全体の合計にも制限があります。

設計上の危なさは、計算した側がいちばんよく知っています。 それを数値と一緒に渡せば、AI は「300Ω です」で終わらず、「ただしピンの定格に注意」まで言えるようになります。ツールの価値は、計算そのものよりこの付帯情報にあると言っていいくらいです。

ツール本体

led_resistor の全文です。残り2つも同じ形なので、1つ読めば十分です(3ツール分の server.py と検証スクリプトをまとめたものを、GitHub の elec-calc-mcp に MIT ライセンスで置いてあります)。

@server.tool()
def led_resistor(
    vcc_v: float,
    vf_v: float,
    if_ma: float,
    series: str = "E24",
) -> LedResistorResult:
    """LED を定電流で光らせるための電流制限抵抗を計算する。

    R = (Vcc - Vf) / If でまず理論値を出し、そのうえで E 系列(実際に売られて
    いる抵抗値)の直近上位に丸めた値と、その抵抗を使ったときに本当に流れる
    電流・抵抗の消費電力・推奨定格まで返す。

    Args:
        vcc_v: 電源電圧 [V]。マイコンのピンで駆動するならその出力電圧(5 や 3.3)。
        vf_v: LED の順方向電圧 Vf [V]。赤なら約 2.0、青・白なら約 3.0 が目安。
        if_ma: 流したい電流 [mA]。5mm 砲弾型なら 10〜20 が一般的。
        series: 抵抗の E 系列。"E12" か "E24" を指定する。既定は "E24"。

    Returns:
        理論抵抗値・E 系列値・実電流・消費電力・推奨定格と、結論の1文。

    Raises:
        ToolError: Vcc が Vf 以下(LED が点灯しない)か、値が負のとき。
    """
    if vcc_v < 0 or vf_v < 0 or if_ma < 0:
        raise ToolError("マイナスの値は指定できません。")
    if if_ma <= 0:
        raise ToolError("流したい電流 if_ma は 0 より大きい値を指定してください。")
    if series not in ("E12", "E24"):
        raise ToolError('series は "E12" か "E24" を指定してください。')

    drop = vcc_v - vf_v
    if drop <= 0:
        raise ToolError(
            f"電源電圧 {vcc_v} V が LED の順方向電圧 {vf_v} V 以下です。"
            "この組み合わせでは LED は点灯しません(Vcc > Vf が必要)。"
        )

    if_a = if_ma / 1000
    resistance = drop / if_a
    e_value = _ceil_e(resistance, E24 if series == "E24" else E12)
    actual_a = drop / e_value
    power = drop * actual_a

    warning = None
    if if_ma > 30:
        warning = (
            "一般的な 5mm 砲弾型 LED の連続定格は 20〜30 mA 程度です。"
            "それを超える電流を流すときは必ずデータシートを確認してください。"
        )
    elif if_ma > 20 and vcc_v <= 5.5:
        warning = (
            "マイコンのピンから直接駆動する場合、1 ピンあたりの出力電流の上限にも"
            "注意してください。上限は機種によって数 mA〜数十 mA と大きく違い、"
            "ポート全体の合計にも制限があります。"
        )
    elif drop < 0.5:
        warning = (
            "Vcc と Vf の差が 0.5 V 未満です。Vf のばらつきだけで電流が大きく"
            "変わるため、定電流駆動を検討してください。"
        )

    summary = (
        f"理論値は {_eng(resistance, 'Ω')} なので、{series} 系列の直近上位である "
        f"{_eng(e_value, 'Ω')} を使います。このとき実際に流れる電流は "
        f"{actual_a * 1000:.4g} mA、抵抗の消費電力は {_eng(power, 'W')} で、"
        f"定格は {_rating_for(power)} を選んでください。"
    )

    return LedResistorResult(
        resistance_ohm=resistance,
        e_series_ohm=e_value,
        actual_current_ma=actual_a * 1000,
        power_w=power,
        recommended_rating=_rating_for(power),
        summary=summary,
        warning=warning,
    )

_ceil_e は E 系列の直近上位を返すヘルパです。上位に丸めるのは、抵抗を大きくする側なら電流が設計値より少なくなる=安全側に外れるからです。

def _ceil_e(x: float, series: list[float]) -> float:
    decade = 10.0 ** floor(log10(x) + 1e-9)
    mantissa = x / decade
    for value in series:
        if value >= mantissa - 1e-9:
            return float(f"{value * decade:.12g}")
    return float(f"{10 * decade:.12g}")

アンダースコアで始まる関数には @server.tool() を付けていないので、AI からは見えません。公開するのは3つのツールだけで、内部のヘルパは隠れたままです。ツールの一覧は短いほど AI が迷わないので、公開範囲は絞るほうが得です。


🔗 Claude Code に登録する

サーバが単体で動くことは確認済みなので、あとは繋ぐだけです。方法は2つあり、用途が違います

方法1:コマンド1行

claude mcp add --transport stdio elec-calc -- E:\prog\elec-calc-mcp\.venv\Scripts\python.exe E:\prog\elec-calc-mcp\server.py

-- の後ろが、サーバを起動するコマンドそのものです。仮想環境の python.exe をフルパスで指定しているのが要点で、ここを python と書くと、Claude Code が起動するプロセスがどの Python を拾うか分からなくなります。

⚠️ AI クライアントはシェルの環境変数を引き継がない

ターミナルで .venv を有効化していても、Claude Code が起動する子プロセスにはその状態が伝わりません。「手元では動くのにクライアントからだと動かない」の大半がこれです。

対策はシンプルで、インタプリタもスクリプトもフルパスで書くこと。.venv\Scripts\python.exe を直に指定すれば、有効化の有無に関係なく同じ環境で動きます。

方法2:.mcp.json に書く

リポジトリに置いて共有したいなら、プロジェクト直下に .mcp.json を作ります。

{
  "mcpServers": {
    "elec-calc": {
      "command": "E:\\prog\\elec-calc-mcp\\.venv\\Scripts\\python.exe",
      "args": ["E:\\prog\\elec-calc-mcp\\server.py"]
    }
  }
}

こちらは ファイルなのでコミットできます。チームで同じサーバを使いたい、あるいは別のマシンに環境を持っていきたいときは、コマンドで登録するよりこちらが確実です。Windows のパスを書くときはバックスラッシュを2つ重ねる(\\)のを忘れないでください。

スコープの違い

登録先には3種類あり、見える範囲が違います

スコープ 指定 有効な範囲 向いている用途
local 既定 コマンドを実行したフォルダだけ 試しに入れてみるとき
project .mcp.json そのリポジトリを開いた全員 チームで共有する道具
user -s user そのマシンの全プロジェクト どこでも使いたい汎用ツール

既定が local である点に注意してください。別のフォルダで Claude Code を起動すると、さっき登録したサーバは見えません。計算ツールのようにどこでも使いたいものは -s user で入れておくほうが快適です。

繋がったか確かめる

claude mcp list
elec-calc: E:\prog\elec-calc-mcp\.venv\Scripts\python.exe E:\prog\elec-calc-mcp\server.py - ✔ Connected

Claude Code の中からは /mcp で、接続状態と公開しているツール数が見えます。

Claude Code の /mcp 画面で elec-calc が connected・3 tools と表示されている

/mcp の Manage MCP servers 画面。Local MCPs に elec-calc · connected · 3 tools と出れば登録成功


🧪 実際に使ってみる

登録できたら、あとは日本語で頼むだけです。

Claude Code に日本語で LED の抵抗を聞くと elec-calc が呼ばれ、330Ω・1/4W という答えが返っている画面

日本語1文からツールが呼ばれ、抵抗値・実電流・消費電力・定格までまとめて返ってくる

たとえば「5V で赤色 LED を 10mA 光らせたい。抵抗は何を買えばいい?」と頼むと、led_resistorvcc_v=5, vf_v=2, if_ma=10 で呼ばれ、こういう結果が返ります。

{
  "resistance_ohm": 300.0,
  "e_series_ohm": 300.0,
  "actual_current_ma": 10.0,
  "power_w": 0.03,
  "recommended_rating": "1/16 W 以上",
  "summary": "理論値は 300 Ω なので、E24 系列の直近上位である 300 Ω を使います。このとき実際に流れる電流は 10 mA、抵抗の消費電力は 30 mW で、定格は 1/16 W 以上 を選んでください。",
  "warning": null
}

面白いのは、AI がここから先を自分で組み立てられることです。「赤色 LED」という言葉から vf_v=2.0 を埋め、返ってきた summary を読んで人間向けに言い直し、warning があればそれも添える。こちらが書いたのは計算関数だけで、その前後の翻訳は AI がやります。

上のスクリーンショットをよく見ると、回答の上に Called elec-calc 2 times と出ています。質問は1つなのに、ツールは2回呼ばれています。回答の中身も1つの数字では終わっていません。E24 系列なら 300 Ω がぴったり存在して実電流 10.0 mAE12 系列は 300 Ω が無いので直近上位の 330 Ω になって実電流 9.1 mA ——この2通りを並べたうえで、「9.1 mA と 10 mA の差は明るさとして見分けられないので、入手性で 330 Ω を選ぶのが定番です」と結論しています。led_resistor には series 引数("E12" / "E24")を持たせてあるので、画面に出ている2回の呼び出しと2系列の数値は、この引数を振り替えて比べた結果として辻褄が合います。

こちらが書いたのは「E12 と E24 を比べて、店で買いやすいほうを勧めろ」という指示ではありません。docstring に series: 抵抗の E 系列。"E12" か "E24" を指定する。既定は "E24"。 と1行書いただけです。引数の意味が説明文から読めれば、それをどう使い分けるかは向こうが決める。 「ツールの説明は AI 向けに書く」の効きかたが、いちばん分かりやすく出ているところです。

555 の計算も同じです。「10kΩ と 47kΩ と 1µF で 555 を発振させたら何 Hz?」と聞けば、ne555_astable が呼ばれて返ります。

{
  "frequency_hz": 13.87206770085542,
  "period_s": 0.0720873067782343,
  "duty_percent": 54.807692307692314,
  "t_high_s": 0.03950938929191688,
  "t_low_s": 0.03257791748631742
}

デューティ比が 54.8% と 50% を超えているのは間違いではありません。充電は R1 と R2 の両方を通り、放電は R2 だけを通るので、High のほうが必ず長くなります。この非対称は 555 の回路構成そのものから来るもので、summary にもその一文を入れてあります(そもそも 1.44 という係数がどこから来たのかは、555 の実験記事計算ツールを作った話で扱っています)。


⚠️ 詰まったところ

FastMCP が無い — 1.x と 2.x の断絶

ネットのサンプルをそのまま写すと、まずここで止まります。

from mcp.server.fastmcp import FastMCP   # 1.x の書き方
ModuleNotFoundError: No module named 'mcp.server.fastmcp'. This is mcp 2.x,
where FastMCP was renamed to MCPServer (from mcp.server.mcpserver import
MCPServer) and other APIs changed; ...

FastMCP は 2.x で MCPServer にリネームされました。 幸い SDK のエラーメッセージが移行先まで教えてくれるので、迷うことはありません。from mcp.server import MCPServer と書き換えれば通ります。

ここで、前に書いた ESP-IDF の記事と食い違って見えるかもしれないので補っておきます。あちらでは同じ ImportError に対して pip install "mcp[cli]<2" で 1.x に落とすという対処を書きました。理由は、あの場面で動かしていたのが自分のコードではなく ESP-IDF に同梱された 1.x 前提のサーバだったからです。他人のコードを動かすなら SDK を合わせるしかありませんが、これから自分で書くなら 2.x で書けばいい。同じエラーでも、立場によって正解が逆になります。

② エラーの文言がモデルに届かない

これがいちばん厄介でした。丁寧な日本語のエラーを書いたのに、クライアント側にはこう届きます。

Error executing tool led_resistor

肝心のメッセージが消えています。原因は例外の種類でした。SDK は投げられた例外の型で扱いを変えます

投げるもの モデルに届くもの
ToolError こちらが書いたメッセージがそのまま届く
その他の例外(ValueError など) Error executing tool <名前> だけ。本文はサーバ側に残る

SDK のドキュメント文字列にも ToolError は「A tool failure you anticipated(想定していた失敗)」と書かれています。想定内の失敗は ToolError、想定外のクラッシュはそれ以外、という切り分けです。後者の本文が伏せられるのは、予期しない例外にはパスやスタックなど漏らしたくない情報が混ざりうるためで、設計としては妥当です。

ValueErrorToolError に置き換えたら、ちゃんと届くようになりました。

Error executing tool led_resistor: 電源電圧 3.3 V が LED の順方向電圧 3.3 V
以下です。この組み合わせでは LED は点灯しません(Vcc > Vf が必要)。

これは単なる作法の問題ではありません。 前者だと AI は「何かが失敗した」としか分からず、同じ引数でもう一度呼ぶくらいしかできません。後者なら「Vcc を上げるか、Vf の低い LED を選ぶ」と次の手を提案できます。エラーの文言も、戻り値と同じく AI 向けの出力です。

③ サーバが黙っていて不安になる

前述のとおり、stdio サーバを単体で起動すると何も表示されません。これは正常です。 心配なら、さきほどのクライアント側スクリプトで叩いてみてください。ツール一覧が返ってくれば動いています。

なお、サーバ側で print() を使ってはいけません。標準出力は MCP の通信路そのものなので、デバッグ出力を混ぜると JSON が壊れて接続が落ちます。ログを出したいときは stderr に出してください。


🧭 どこまでを MCP にするか

作ってみて実感したのは、MCP が向いているのは、短くて答えがはっきりしている仕事だということです。計算ツールはその典型で、引数を渡せば決まった答えが即座に返ります。

一方で、前の記事で書いたように、ビルドや書き込みのような重い処理は、まだスキルで直接叩くほうが安定する場面があります。長時間かかる処理は、完了応答の扱いという別の難しさを抱え込むためです。

読み取りや計算のような軽い仕事から MCP にしていく、という順番が現実的です。そして計算ツールを作ってみると分かるのですが、この「軽い仕事」の範囲がかなり広い。単位換算、部品表の集計、設定ファイルの検証——毎回 AI に説明していた小さな決まりごとは、だいたいツールにできます。


✅ まとめ

  • MCP サーバの正体は、飾りを付けた普通の関数です。MCPServer を作って @server.tool() を付け、server.run() するだけで、Claude Code から呼べる道具になります
  • docstring と引数名は、AI への仕様書です。 単位は if_ma のように名前へ埋め込み、「何ができないか」まで書いておくと、誤った呼び出しが減ります
  • 戻り値は数字だけにしない。 結論の1文と設計上の注意点を添えると、AI の回答の質が変わります
  • 想定内の失敗は ToolError で投げる。 ほかの例外だと、書いたメッセージがモデルに届きません
  • 登録は claude mcp add の1行か、.mcp.json 共有したいなら後者、どこでも使いたいなら -s user です
  • SDK は 1.x と 2.x で名前が変わっていますFastMCPMCPServerisErroris_error)。サンプルを写すときはバージョンを確認してください

作ったサーバは PC の中だけで完結していますが、この形のまま外の世界にも手を伸ばせます。次はシリアルポートを読ませて、マイコンのログを AI に直接見てもらうつもりです。そうなると、「ログを貼ってください」という往復がまるごと消えます。


関連記事


参考