MCPサーバーの作り方|手元のフォルダをAIに触らせる最小の形
mcp サーバー 作り方で検索すると、プロトコルの全体像から順に説明する長い記事が並びます。ただし最初に決めるべきことは、どの言語で書くかではありません。AIに何を触らせるか、どこから先は触らせないか、そして返す形をどうするかです。この記事では、手元のフォルダの中身を読ませる最小のサーバーを題材に、決める順番と、いまの仕様で変わっている前提を整理します。
検索で出てくる手順は、仕様の世代が混ざっている
MCPの仕様は改訂が続いており、公開されている最新の版は2026-07-28です。ここで押さえておくべきなのは、初期化の扱いが以前の版と変わっている点です。古い版では、接続のたびに初期化のやり取りを済ませてから本題に入る形でした。いまの版は状態を持たない作りに変わり、必要な情報は毎回の要求そのものに入れて運びます。
この違いは実装に直接効きます。初期化の握手を前提に書かれた記事の手順をそのまま写すと、新しい版に合わせた相手と噛み合いません。逆に、新しい版だけを見て書いたサーバーは、古い版のままの相手から使えません。公式の仕様では、新しい側から相手の世代を判定する手順まで決められていて、まず探索の要求を投げて、返り方で相手が新旧どちらかを見分ける形になっています。
つまり「作り方」を調べるときに最初に確かめるのは、その記事がどの版を前提にしているかです。参照するなら公式の仕様の日付を見て、自分が繋ぎたい相手がその版に対応しているかを合わせて確認します。
サーバーが差し出せるものは3つに分かれている
MCPのサーバーが提供できる機能は3種類に整理されています。リソース、プロンプト、ツールです。リソースは読ませるためのデータで、プロンプトは利用者が選べる定型の指示で、ツールはモデルが呼び出せる関数です。クライアント側が差し出せる機能もあり、利用者に追加の情報を尋ねる仕組みが用意されています。
このうち、実際に作られているサーバーの中心はツールです。役割については次のように説明されています。
AIが実際に外部アクションを実行するための機能です。ファイルの作成・削除、外部APIの呼び出し、データベースへの書き込みなど、能動的な操作を実現します。MCPサーバーの中核となる機能であり、多くの実装例でツール機能が中心的な役割を担います。 出典: blastengine.jp
手元のフォルダを扱うサーバーであれば、最小の構成は2つのツールで足ります。指定したフォルダの中身を一覧で返すものと、指定したファイルの中身を返すものです。書き込みや削除は最初から入れず、読むだけのサーバーを動かして形を掴んでから追加する順番にします。読むだけであれば、想定外の呼び出しが起きても失うものがありません。
stdioで繋ぐときの決まりを、最初に頭に入れる
標準で定められている接続の方法は2つです。1つはstdioで、クライアントがサーバーを子プロセスとして起動し、標準入力と標準出力でやり取りします。もう1つはStreamable HTTPで、1つの窓口に対して要求を送り、返りが1つのJSONか要求ごとの流れとして返ってきます。
手元のフォルダを扱うサーバーは、外に出す必要がないのでstdioが素直な選択です。仕様に書かれた決まりのうち、実装で引っかかりやすいのが次の2点です。
- やり取りは1件ごとに改行で区切り、1件の中に改行を含めない
- サーバーは標準出力に、決められた形式の情報以外を書き込まない
2つ目が、最初にはまる場所です。デバッグのために状況を出力したくなりますが、標準出力に1行でも余分な文字が混ざると、その行が壊れた情報として扱われ、動かなくなります。仕様では、記録を残したいときは標準エラー出力を使ってよいことが明記されています。クライアント側は、標準エラー出力に何か出ていても、それを異常だと決めつけないよう求められています。
書き方に直すと、Pythonなら画面に出す関数をそのまま使わず標準の記録の仕組みを使う、JavaScriptならログを出す関数ではなく誤りを出す関数を使う、ということになります。この1点を最初に決めておけば、原因の分からない接続の失敗を何時間も追う場面が消えます。
終了の作法も決まっています。クライアントは入力を閉じてサーバーの終了を待ち、時間内に終わらなければ強制的に止めます。サーバー側は、入力が閉じたことを知ったら速やかに終了する作りにしておきます。この合図が唯一どの環境でも通じるものだと仕様に書かれているため、ここに独自の終了手順を加える必要はありません。
土台の用意は、言語ごとに用意されているものを使う
ゼロから通信部分を書く必要はありません。公式の手引きでは、Pythonなら環境と依存関係の管理の道具を使って始める形が示されています。
uv init weather
cd weather
uv venv
source .venv/bin/activate
uv add "mcp[cli]"
導入したら、サーバーの本体を作り、型の注記と説明文からツールの定義が自動で組み立てられる形で書いていきます。関数の上に印を付けるとツールとして公開され、引数の型と説明がそのまま相手に伝わる仕組みです。
JavaScript側も同じ考え方で、パッケージを入れてサーバーを作り、ツールを登録して、標準入出力の運び手に繋ぎます。
npm install @modelcontextprotocol/server zod
npm install -D @types/node typescript
ここで決めておくとよいのは、フォルダの場所を実行時の引数で受け取る形にすることです。コードの中に固定で書くと、別のフォルダを見せたいときにサーバーを作り直すことになります。起動時の引数で渡す形にしておけば、同じサーバーを用途ごとに別の設定として登録できます。
ツールの定義は、名前と入力の形で品質が決まる
ツールの定義に入れるのは、名前、表示用の名前、説明、入力の形、そして返す形です。入力の形はJSON Schemaで書き、版を指定しなければ2020-12として扱われます。引数を取らないツールでも、空の入れ物であることを明記した形を書くよう求められています。
名前の付け方には目安が示されています。長さは128文字以内で、大文字と小文字は別のものとして扱われ、使える文字は英数字とアンダースコア、ハイフン、ドットです。空白やカンマは避けます。1つのサーバーの中で重複させないことも求められています。複数のサーバーをまとめて使う側では、同じ名前のツールが衝突することがあるため、呼び出す側が前置きを付けて区別する運用が想定されています。
説明文は、人間向けの飾りではなく判定の材料です。モデルはこの文章を読んでそのツールを呼ぶかどうかを決めるため、いつ使うべきかと、何を返すかが書かれていないツールは呼ばれないか、誤って呼ばれます。フォルダの一覧を返すツールであれば、対象がどこの範囲に限られるのか、隠しファイルを含めるのか、並び順が何かまで書いておきます。
返す形も決められます。出力の形を宣言しておくと、サーバー側はその形に沿った結果を返す義務を負い、受け取る側はその形で検査してよくなります。呼び出す相手が自動で処理を続ける作りであれば、文章だけを返すより、構造のある形も一緒に返すほうが扱いが安定します。
失敗の返し方を2種類に分けて設計する
失敗の扱いは、仕様で2つに分けられています。ここを混ぜると、直せる失敗が直せない失敗として扱われます。
1つ目は、要求そのものが成り立っていない場合です。存在しないツールを呼ばれた、形式が崩れている、サーバー側で想定外のことが起きた。これらは通信の層の誤りとして返します。
2つ目は、ツールは正しく呼ばれたが、実行が目的を果たせなかった場合です。指定されたファイルが無い、引数の値が範囲の外にある、業務上の条件に合わない。これらは結果の中に誤りである印を立てて返します。この形で返すと、呼び出した側は内容を読んで引数を直し、もう一度呼び直せます。
手元のフォルダを扱うサーバーで具体的に言えば、「そのフォルダは見せる範囲の外にある」は2つ目です。文章として理由を返せば、相手は範囲の中の別の場所を指定し直せます。ここを通信の層の誤りとして返してしまうと、相手は打つ手がなくなります。
状態を持たせたいときは、接続ではなく引数で持たせる
作り始めてすぐ迷うのが、複数回の呼び出しをまたいで状態を覚えさせたい場合です。フォルダを開いたまま次の操作をさせたい、検索の結果を保持して続きを取らせたい、といった要求が出てきます。
いまの仕様では、接続そのものに状態を持たせる仕組みが用意されていません。そのため公式の指針は、状態を作るツールが目印の文字列を返し、後続のツールがその目印を引数として受け取る形にすることです。呼び出す側が目印を持ち回り、サーバーはその目印を鍵にして中身を引きます。
この形にするときに決めておくことが3つあります。
- 目印は中身の構造が読み取れない文字列にする。形が推測できると、他の利用者の状態を当てられる余地が生まれる
- 有効な期間を決めて、ツールの説明文に書いておく。読んだ側が、状態を作るかどうかの判断に使える
- 期限が切れた目印で呼ばれたら、そのことを文章で返す。相手が新しく作り直して復帰できる
認証を伴うサーバーであれば、目印は名前でしかないため、呼び出しごとに呼び出した相手の権限を確かめ直す必要があります。手元のフォルダを読ませるだけのサーバーなら、状態を持たせずに毎回パスを受け取る形が最も単純で、事故も起きません。状態を持たせるのは、持たせない形では明らかに回数が増えると分かってからにします。
動作の確かめ方と、権限の絞り方
書けたら、AIに繋ぐ前に専用の道具で確かめます。参照の道具が公式に用意されていて、1つのパッケージの中に画面付きのもの、コマンドで叩くもの、端末の中で操作するものの3つが入っています。必要な環境はNode 22.19.0以降です。
npx @modelcontextprotocol/inspector node path/to/server/index.js
これで画面付きのものが立ち上がり、一度きりの合図の付いたURLが表示されます。コマンドで結果だけ取り出したい場合は、方式の指定と呼び出す手続きの名前を指定します。ツールの一覧を取るだけなら、手続きの名前に一覧のものを指定します。
自動で確かめる流れに組み込むなら、コマンド側を使うのが向いています。呼び出し方は公式の手引きに一覧で並んでいます。
確認が済んだら、使う側の設定に登録します。設定の置き場所はOSごとに決まっていて、macOSであれば利用者のライブラリの中にある設定ファイルです。起動するコマンドと引数を書くと、次の起動から読み込まれます。
権限については、仕様の側からも注意が置かれています。ツールは任意の処理の実行に等しいため、呼び出しを拒否できる人間が流れの中にいるべきだとされています。また、ツールに添えられた説明や注記は、信用できる相手から来たものでない限り信用できないものとして扱うよう求められています。自分で作るサーバーでも、見せるフォルダの範囲を引数で絞り、その外側のパスを受け取ったら実行せずに理由を返す形にしておくのが順番です。詳しい手順は公式の手引きに言語別で並んでいます。
作ったあとに残る作業は、窓の行き来のほうにある
サーバーができても、日々の作業は「フォルダを見て対象を決める、コマンドで確かめる、AIに渡して続きを頼む」の繰り返しです。サーバーを1つ作っても、この3つの窓が別々に開いている状態は変わりません。設定ファイルを直す、起動の引数を変える、記録を見る、といった作業のたびに切り替えが入ります。
フォルダとターミナルとAIが同じ窓にある状態なら、設定ファイルの場所を選んだまま中身を確かめ、その場で起動のコマンドを打ち、返ってきた記録をそのまま読めます。サーバーを作る作業自体が、まさにこの3つを行き来する作業なので、往復の回数がそのまま所要時間になります。
道具の側で何ができるかはできることに整理されています。Finderや他のファイル管理との違いを並べたものは他のファイル管理との比較にあり、外出先のiPhoneから手元のMacで動いている処理の続きを確かめる使い方はiPhone・iPadから続きをにまとまっています。費用の目安は料金、判断に迷いやすい点はよくある質問にあります。
最初に決めるべきことは1つです。何を読ませるかではなく、どこから先は触らせないか。範囲を引数で受け取る形にしてから書き始めれば、後からツールを追加するときに、そのつど権限の話をやり直す必要がなくなります。
よくある質問
MCPサーバーを作るのに必要な知識はどれくらいですか?
PythonかJavaScriptで関数を書けて、JSONの構造が読めれば足ります。通信の部分は公式に用意されている土台が引き受けるため、自分で書くのはツールの中身と入力の形の宣言です。ただし標準出力に余分な文字を出さない決まりだけは、最初に理解しておく必要があります。
なぜ標準出力にログを出してはいけないのですか?
stdioで繋ぐ場合、標準出力はやり取りそのものの通り道です。1件ごとに改行で区切られているため、記録の文字が混ざるとその行が壊れた情報として扱われ、接続が続かなくなります。記録は標準エラー出力に出す決まりになっており、受け取る側もそれを異常とは扱いません。
作ったサーバーが動いているかを確かめる方法はありますか?
公式の確認用の道具を使います。画面付き、コマンド、端末の中の3つの形が1つのパッケージに入っており、起動のコマンドを引数として渡すとそのまま接続します。ツールの一覧を取るだけならコマンドの形で手続きの名前を指定すれば、結果が機械で読める形で返ります。
古い記事の手順をそのまま真似してもよいですか?
仕様の版によって前提が違うため、そのままでは噛み合わないことがあります。以前は接続のたびに初期化のやり取りをしていましたが、最新の版では状態を持たない作りに変わり、必要な情報は毎回の要求に入れて運びます。参照する記事が、どの日付の版を前提にしているかを先に確かめてください。