fullseye

Fullseye を MCP(Model Context Protocol)から使う

Claude Code / Claude Desktop などの MCP クライアントから、fullseye の op を探し、 使い方を読み、実際に走らせるための stdio サーバ。0.1.11 で PoC として入った。

py -3.11 -m fullseye.mcp             # stdio サーバ(クライアントが起動する)
py -3.11 -m fullseye.mcp --demo      # 自分を起動して一通り叩く(下の出力が出れば動いている)
py -3.11 -m fullseye.mcp --coverage  # カタログ 5 層の被覆を JSON で

wheel から動く(0.2.0〜)。0.1.11 はカタログの正本 docs/OP_INDEX.json と知識層 docs/ops/**/*.md をリポジトリ相対で読んでいたので、pip install fullseye した環境からは CatalogError で止まっていた(理由つきで止まるのは正しいが、動かない)。いまは tools/gen_mcp_data.py索引の複製ノートの frontmatter だけ(op / dim / category / in / out / halcon + 相対パス、本文は入れない)を fullseye/data/OP_INDEX.json / fullseye/data/OP_NOTES.json に書き、package-data として wheel に入る(約 0.8 MB)。 読む順は 索引 = パッケージ内 → リポジトリ docs/無ければ CatalogError、 ノート = リポジトリ docs/ops(正本、本文も読める)→ パッケージ内の frontmatter → CatalogError。 どこから読んだかは fullseye_catalog_coverageindex_source / notes_source に出る。 wheel ではノート本文の代わりに同梱の Studio help HTML(studio_assets/op_help/)を返し、 返り値の note_body_unavailable にそう書く(黙って代替に落ちない)。図は「入力 → 出力」の 1 枚(op_help/fig/)だけになり、つまみの掃引図(docs/ops/_fig/)は checkout でだけ付く。

複製なのでずれるpy -3.11 tools/regen_all.py --check(CI で回る)と tests/test_mcp_server.py が「複製 = 正本」を件数ごと数え、tests/test_mcp_wheel.py (FULLSEYE_WHEEL_GATE=1 で有効)が wheel を建てて別 venv に入れ、リポジトリの外の cwd から 起動して索引 1,942 / ノート 1,942 が返ることを配布物の側で数える。

Claude Code への登録

claude mcp add fullseye -- py -3.11 -m fullseye.mcp

pip install fullseye した環境でも checkout でも同じ。checkout なら docs/ops の ノート本文と掃引図まで届く(cwd は問わない —— パッケージの位置から解決する)。 読み込める画像は既定で同梱サンプルだけ。自分の画像を読ませるには根を足す:

set FULLSEYE_MCP_ROOT=C:\path\to\images;D:\more   (PowerShell: $env:FULLSEYE_MCP_ROOT = "...")

tool は 8 つ(op は 1,942 あるが tool にはしない)

op はデータで、tool は「探す・読む・読み込む・走らせる・観察する」の数個だけ。 tool を 1,942 個並べると LLM の文脈を食い潰す(TheMCPCompany の実測: 18,000 tool は retrieval 無しでは使えない)。

tool 何をするか
fullseye_search_ops 名前・HALCON 名・カテゴリ・次元の部分一致。返り値の sources が出どころ(index / registry / ledger / note / facade)、by_sources が層別の内訳
fullseye_op_help op の知識層ノート(使い方・つまみ a/b の実効・HALCON 相当)+ 生成済みで実行が検証された図を resource_link
fullseye_catalog_coverage カタログ 5 層の交差。検索がどれだけの機能を見えているか
fullseye_list_samples 同梱サンプル(来歴・ライセンスつき)
fullseye_load_image 画像 → ハンドル。画素は返さず、数値統計 + 判定
fullseye_apply ハンドルに op を 1 つ。出力ハンドル + 数値統計 + 判定 + 劣化台帳
fullseye_pipeline op を順に。段ごとに記録、型連鎖は走らせる前に検査、strict は失敗段で停止
fullseye_inspect ハンドルの数値統計 + 判定(+ 小図)

画像は「在らず、必要なときだけ在る」

画像は fullseye://img/<sha16>ハンドルでやり取りし、LLM はバイト列を見ない (画像を本文に載せると 1 枚で文脈が破裂する)。代わりに返り値が自分の妥当性を名乗る:

otsu(a=0.50, b=0.50) → fullseye://img/2c1e…
判定=ok(std=0.499 range=1)  shape=[512, 512] min=0 max=1 mean=0.46 std=0.499 nonfinite=0

判定は「標準偏差 0 / 値域 0.1 未満 / 空 / 非有限 / [0,1] の外」を機械的に見て ok / constant / flat / saturated / nonfinite / out_of_range / empty を返す。 判定は必ず生の数値と併記する —— 言葉だけだと、判定器が中身を見ていなくても「健全」と 言えてしまう。免除は ops.NONFINITE_IS_MEANINGFUL(inf が答えの op)と ops.UNIT_RANGE_IS_NOT_THE_CONTRACT(image を名乗るが物理量を運ぶ op)を正本として引く。

判定が ok でないときだけ、入出力を左右に並べた 96 px の小図が resource_link で 自動で付く(vision="thumb" で強制、"none" で抑止)。パイプラインでは最初に割れた段の 1 枚だけ —— 知りたいのは「どの段で壊れたか」だから。

黙って劣化しない(strict 既定)

fullseye には fail-soft 層があり、op が失敗しても型の合う値が返る。MCP では既定を strict (on_error="raise")にし、劣化したら拒否して理由を返すallow_degraded=true を明示した ときだけ fail-soft を許し、そのときは degraded に「どの op が・どの層で・なぜ」を必ず載せる (空でも [])。

fail-closed の一覧

境界 規約
ファイルパス 根の下だけ。.. もシンボリックリンクも realpath で潰してから比べる
op 名 registry の完全一致のみ。近い名前は提示するだけで、勝手に倒さない
ハンドルの sort ≠ op の in_sort なら拒否。パイプラインは走らせる前に全段を検査
つまみ a, b ∈ [0,1]。外は拒否(丸めない)
引数 スキーマは additionalProperties: false。知らないキーは拒否
結果サイズ structuredContent が 512 KB を超えたら落とし、落としたと本文と _meta に書く
stdout プロトコル専用。ログ・監査は stderr

検証(どう確かめてあるか)

tests/test_mcp_server.py / tests/test_mcp_images.py(66 件): subprocess で本物の stdio を往復させ、stdout にプロトコル以外の 1 バイトも無いことまで見る。 拒否はそれぞれ狙った文言で落ちることを本文で判定する。小図は実際に開いて高さと非黒を見る。 --demo は同じ経路を人が目で見る用。

設計の出どころ

RAD(agents コーパス)の 4 本 —— TheMCPCompany(18,000 tool は retrieval が要る)、 MCP-Universe(GPT-5 でも 43.7 %、ボトルネックは long-context と unknown tools)、 Self-Healing Router(失敗は必ず記録か昇格、沈黙のスキップにしない)、 Function Hijacking(tool 選択の乗っ取り ASR 70〜100 %)—— と、TRIZ の分離原理 (画像は「在らねばならない」かつ「在ってはならない」→ 時間で分離 + #25 セルフサービス)。 画像処理ライブラリを MCP で公開した先行例は、8 コーパスを引いて 0 件だった(2026-09-15)。