MCP サーバーを一つ外したのは、つなぎ始めてから初めてのことでした。
きっかけは、Lab のサイト運用を Gemini CLI に手伝ってもらっていた夜です。ファイルの整理を頼んだはずが、返ってきた関数呼び出しには身に覚えのない引数が混ざっておりました。別のサーバーが持っているツールのパラメータです。API からは 400 が返り、頼んだ処理とは何の関係もない「そのプロパティは文字列でなければならない」という文言だけが残りました。
設定ファイルを開いて、私は少し手が止まりました。便利そうなサーバーを見つけるたびに mcpServers へ足してきたのですが、外した記録が一件もありません。足す手順は自分の中に出来上がっていて、減らす基準だけが無かったのです。
最初にお伝えしたいのは、気にすべき数字は公式が定める上限そのものではない、ということです。上限に当たるより先に、応答の質のほうが落ちます。
512 という上限は、壊れ始める場所ではありません
Gemini API には、1 リクエストに載せられる関数宣言の数にはっきりした上限があります。超えると、モデルが動き出す前にリクエストごと弾かれます。
[API Error: [{
"error": {
"code": 400,
"message": "The GenerateContentRequest proto is invalid:\n * tools[0].function_declarations: [FIELD_INVALID] At most 512 function declarations can be specified.",
"status": "INVALID_ARGUMENT"
}
}]]これは gemini-cli の Issue #19083 として報告されている挙動で、報告者は MCP サーバーを 20 台以上つないだ状態で踏んでいます。エラーは即座に返り、途中まで進んで失敗するわけではありません。その意味では、まだ親切な壊れ方です。
やっかいなのは、その手前で起きるほうです。188 個のツールを持つサーバーを 1 台つないだ事例では、ログイン用のツールを明示的に呼んだにもかかわらず、まったく別のツールのパラメータが引数へ紛れ込み、やはり 400 になりました。上限の 512 には遠く届いていません。私が踏んだのも、おそらくこちら側です。
ツールの説明文は、モデルにとってはどれも似た形の候補です。候補が増えるほど、どれを呼ぶか・どの引数を渡すかの判断は曖昧になります。上限は API が決めますが、使ってよい数は自分で決めます。 公式の MCP サーバー設定ドキュメントには接続方法とフィルタの書式が丁寧に載っていますが、何個までにすべきかという目安は書かれておりません。そこは自分で埋める部分なのだと、私は受け取りました。
つないだツールを、まず数えます
減らす前に、いま何個あるのかを知る必要がありました。/mcp はサーバーごとのツール名を並べてくれますが、合計を教えてはくれません。そこで、設定ファイルに並んだ stdio 版のサーバーへ順につないで数えるだけの小さなスクリプトを書きました。
#!/usr/bin/env python3
"""~/.gemini/settings.json の stdio 版 MCP サーバーへ順に接続し、公開ツール数を数えます。
使い方: python3 count_mcp_tools.py
出力: サーバー名 / ツール数 / 名前が長すぎるツールの件数 / 合計
"""
import json
import os
import subprocess
from pathlib import Path
SETTINGS_PATH = Path.home() / ".gemini" / "settings.json"
PROTOCOL_VERSION = "2025-06-18" # サーバーは自分が話せる版を返してきます
def rpc(proc, payload):
"""JSON-RPC を1行書き、id の一致する応答が来るまで読みます。"""
proc.stdin.write(json.dumps(payload) + "\n")
proc.stdin.flush()
if "id" not in payload:
return None
while True:
line = proc.stdout.readline()
if not line:
raise RuntimeError("応答を返す前にサーバーが終了しました")
try:
msg = json.loads(line)
except json.JSONDecodeError:
continue # 標準出力へログを吐くサーバーがあるため読み飛ばします
if msg.get("id") == payload["id"]:
return msg
def list_tools(conf):
env = dict(os.environ)
env.update({k: os.path.expandvars(v) for k, v in conf.get("env", {}).items()})
proc = subprocess.Popen(
[conf["command"]] + conf.get("args", []),
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
cwd=conf.get("cwd"), env=env, text=True, bufsize=1,
)
try:
rpc(proc, {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {
"protocolVersion": PROTOCOL_VERSION,
"capabilities": {},
"clientInfo": {"name": "tool-counter", "version": "0.1.0"},
}})
rpc(proc, {"jsonrpc": "2.0", "method": "notifications/initialized"})
res = rpc(proc, {"jsonrpc": "2.0", "id": 2, "method": "tools/list"})
return [t["name"] for t in res.get("result", {}).get("tools", [])]
finally:
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
def main():
servers = json.loads(SETTINGS_PATH.read_text()).get("mcpServers", {})
total = 0
for name, conf in sorted(servers.items()):
if "command" not in conf:
print(f"{name:22} url 接続のため数えません")
continue
try:
tools = list_tools(conf)
except Exception as err:
print(f"{name:22} 取得できませんでした: {err}")
continue
total += len(tools)
# 実際に渡る名前は mcp_{サーバー名}_{ツール名} で、63 文字を超えると途中が詰められます
long_names = [t for t in tools if len(f"mcp_{name}_{t}") > 63]
note = f" 名前が 63 文字超: {len(long_names)} 件" if long_names else ""
print(f"{name:22} {len(tools):4} 個{note}")
print(f"{'合計':22} {total:4} 個 (API 上限 512 個)")
if __name__ == "__main__":
main()initialize を送ってから notifications/initialized を挟み、そのうえで tools/list を呼ぶ——MCP の stdio 接続はこの順番が決まっております。標準出力へ起動ログを流すサーバーが混ざっていたため、JSON として読めない行は黙って飛ばすようにしました。ここを厳密にすると、数えたいだけなのに例外で止まります。
63 文字の判定を入れたのは、ツール名がそのまま渡るわけではないからです。Gemini CLI は衝突を避けるために mcp_{サーバー名}_{ツール名} という完全修飾名を割り当て、63 文字を超えると中ほどを詰めて短くします。名前が似ているツールが並んでいると、詰められた結果さらに見分けがつきにくくなります。
数えてみると、私の手元は上限の 512 には遠く及びませんでした。それでも、自分が把握している数よりはるかに多い、というのが正直なところでした。
includeTools は「残すもの」を書く場所です
Gemini CLI には、サーバー単位でツールを絞る設定が二つ用意されています。includeTools は許可リストで、書いたツールだけが有効になります。excludeTools は拒否リストで、書いたツールが消えます。何も書かなければ、そのサーバーのツールは全部が有効です。
{
"mcpServers": {
"site-ops": {
"command": "npx",
"args": ["-y", "@example/site-ops-mcp"],
"includeTools": ["read_file", "write_file", "list_dir"],
"timeout": 30000
}
}
}両方を書いた場合は excludeTools が勝ちます。どちらのリストにも載っているツールは消えます。拡張機能が持ち込んだサーバーを上書きするときも同じ考え方で、拒否リストは足し合わされ、許可リストは重なった部分だけが残ります。つまり、拡張機能の側から勝手にツールを復活させられることはありません。手元の設定に拒否権があるという作りです。
サーバーをまるごと止めたいときは、mcpServers を消さずに上位の mcp で扱えます。
{
"mcp": {
"excluded": ["experimental-server"]
}
}一つだけ、名前の付け方に落とし穴があります。サーバー名にアンダースコアを使わないでください。完全修飾名を解釈する側は mcp_ の次に現れる最初のアンダースコアでサーバー名とツール名を切り分けるため、my_server のような名前だと境目を取り違えます。しかもエラーにはならず、権限のルールだけが当たらなくなります。my-server のようにハイフンで書くのが安全です。
常時有効・その場だけ・外す、に仕分けます
数えたツールを、私は三つに分けました。判断の軸は「便利かどうか」ではなく、「今週これを呼ばなかったら困るか」です。
| 扱い | 条件 | 設定の書き方 |
|---|---|---|
| 常時有効 | ほぼ毎日呼ぶ。呼ばれないと作業が止まる | includeTools に明記する |
| その場だけ | 月に数回。呼ぶ場面を自分で言える | 普段は無効。使う回だけセッション単位で戻す |
| 外す | 入れた理由を思い出せない。代わりに手でやれる | mcp.excluded へ移す |
三つ目の条件を入れたことで、判断がずいぶん速くなりました。「入れた理由を思い出せない」は、私の設定ファイルではかなりの数に当てはまったのです。試したときは面白かったのですが、そのあと一度も呼んでおりません。
同じサーバーの中でも仕分けは効きます。読み取り系のツールだけを includeTools に残し、書き込み系を落とすと、候補の数が減るうえに、誤って呼ばれたときの被害も小さくなります。個人開発で使う道具は、権限の広いものほど普段は畳んでおきたいところです。
一方で、絞りすぎると「あのツールが無い」と気づくまでに時間がかかります。ですので、外したものを一覧にして設定ファイルの隣へ置いております。消すのではなく、片付ける——その区別を自分の中で保つための一手間です。
外したあとに、戻る道を残します
仕分けを決めても、必要になったその場で戻せなければ、結局また全部を常時有効へ引き戻してしまいます。Gemini CLI はサーバー単位の出し入れをコマンドで扱えます。
# いま何がつながっているかを確認します
gemini mcp list
# 普段は落としておきます
gemini mcp disable experimental-server
# その回のセッションだけ戻します
gemini mcp enable experimental-server --session対話中であれば /mcp で状態を見て、/mcp enable <名前> でそのまま戻せます。無効にしたサーバーは一覧に残り、接続だけがされません。存在を忘れずに済むという意味で、設定ファイルから削除するより私はこちらを好みます。
なお、接続そのものが失敗しているときは数の問題ではありません。認証やパスの側を先に疑ってください。そのあたりはGemini CLI が起動しない・認証が通らないときに見る場所に書き残しております。接続のしかた自体をこれから整える方は、Gemini CLI で MCP サーバーを使うから読んでいただくと順序が合うかと思います。
自分でサーバーを書く場合は、公開するツールの数そのものを設計の対象にできます。用途の違う機能を 1 台へ詰め込むより、小さく分けて必要な側だけをつなぐほうが、結果として呼び間違いが減りました。作り方はTypeScript でカスタム MCP サーバーを構築するにまとめてあります。
まず1つやるなら
今夜のうちに、上のスクリプトを一度走らせて合計だけ控えておいてください。512 という上限と比べるためではなく、自分が把握していた数とのずれを見るためです。私の場合、そのずれの大きさが、減らす基準を決める気にさせてくれました。
読んでくださってありがとうございました。道具を増やす話は楽しいのですが、減らす話も同じくらい自分の役に立っております。