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_coverage の index_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 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 = "...")
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_find_ops |
やりたいことの言葉(日本語・英語・中文 簡/繁・한국어・Deutsch・हिन्दी)で探す。op の要約 7 言語・名前・HALCON 名・型を BM25 で照合し、順位つきで要約(問い合わせの言語)とノートの場所を返す。同義語は拾わない |
fullseye_plan |
have・want・goalから型付きDAGを返す。後ろ向きも同じ超グラフ探索器を使う |
fullseye_describe |
sort・op・族を1件だけ説明する |
fullseye_op_cards |
候補 op の短いカードを token 予算内で返す。カードの途中では切らない |
fullseye_validate_pipeline |
op 名、型連鎖、a/b の範囲、段数を実行せず検査し、段番号つきの理由と候補を返す |
fullseye_op_help |
op の知識層ノート(使い方・つまみ a/b の実効・HALCON 相当)+ 生成済みで実行が検証された図を resource_link で |
fullseye_catalog_coverage |
カタログ 5 層の交差。検索がどれだけの機能を見えているか |
fullseye_list_samples |
同梱サンプル(来歴・ライセンスつき) |
fullseye_load_image |
画像 → ハンドル。画素は返さず、数値統計 + 判定 |
fullseye_load_stack |
複数画像 → images / seq<image> / polsweep の束ハンドル |
fullseye_apply |
ハンドルに op を 1 つ。出力ハンドル + 数値統計 + 判定 + 劣化台帳 |
fullseye_pipeline |
op を順に。段ごとに記録、型連鎖は走らせる前に検査、strict は失敗段で停止 |
fullseye_inspect |
ハンドルの数値統計 + 判定(+ 小図) |
fullseye_fix_text |
画像の中の文字を「本当はこう書いてあるべき文字列」に合わせて直す。行ごとに {text, bbox}、mode=repair_flagged(床を超えた字だけ置換、正しい字に触らない)/ rewrite_line(行を丸ごと同じ書体で描き直す)。直した画像はハンドル + 全解像度 PNG、報告に status / reason_code / mismatch(typo か unrelated = 元の字が指示と無関係な疑い)。検証を通らない置換は元に戻す |
fullseye_import_json |
型付きの値を JSON 封筒で引数に受け取り、以後の op が使えるハンドルにする。json(文字列)か envelope(オブジェクト)のどちらか一方。配列 sort(image / color / region / points / matrix …)はハンドルに、feature / contour / table は値を返す。壊れた封筒・未知 sort は -32602 |
fullseye_export_json |
ハンドルの中身を JSON 封筒で持ち出す。structuredContent に fullseye.from_jsonable で bit そのまま戻せる封筒、本文に fullseye.to_markdown の読める描画。readable=true で数をリストに。上限(512 KB)超の大きな画像は isError で断る(画像はハンドル/小図で扱う) |
fullseye_write_viewer |
PoC の結果を自己完結した 1 枚の HTML ビューアにして書く(タブ・正直なバッジ passed / limit / null・数字・3-D の点群/メッシュ・2-D 動画と時系列・前後の分割・ja/en/zh/tw/ko/de/hi/fr の切り替え)。spec(JSON)・lang(言語の配列、先頭が既定)・name。spec は信頼境界で再検証し、形違い・NaN・未知のバッジ/種類・欠けた訳は -32602。書き先はサンドボックスだけで、resource_link で返す |
fullseye_estimate_distortion |
本来まっすぐな線群の点列(lines=[[x,y],…] を並べた配列、2 本以上・各線 3 点以上)と 3x3 内部行列 K から Brown–Conrady 歪み係数 dist=[k1,k2,p1,p2,k3] を推定(plumb-line 法、チェッカー不要)。radial=1/2/3、tangential 真偽。返す dist は undistort_image にそのまま渡せる。lines/K は信頼境界で再検証し、形が違えば -32602、推定不能は isError |
小さな LLM は、候補を絞ってから短い説明だけを渡す。推奨順は
find_ops / plan → op_cards → validate_pipeline → pipeline。
{"name":"fullseye_plan","arguments":{"have":"image","want":"feature","goal":"count connected defects"}}
{"name":"fullseye_op_cards","arguments":{"names":["otsu","count_obj"]}}
{"name":"fullseye_validate_pipeline","arguments":{"start_sort":"image","stages":[{"op":"otsu"},{"op":"count_obj"}]}}
find_ops の probe は既定入力での試し実行結果である。
失敗は「その入力では動かなかった」という選択材料であり、op 自体の故障とは断定しない。
fullseye_write_viewer)PoC の結果を、自己完結した 1 枚の HTML(タブ・正直なバッジ・3-D の点群/メッシュ・2-D 動画・前後の分割・言語切り替え)にして書く。Python の fullseye.write_viewer と同じ門を通る(能力ノート interactive-poc-viewer、例 examples/interactive_poc_viewer.py)。
{"method": "tools/call", "params": {"name": "fullseye_write_viewer", "arguments": {
"lang": ["ja", "en"],
"spec": {"id": "demo", "title": {"ja": "見本", "en": "Demo"}, "subtitle": {"ja": "副題", "en": "Subtitle"},
"hint": {"ja": "操作", "en": "Hint"},
"tabs": [{"id": "a", "kind": "points", "badge": "null",
"label": {"ja": "点群", "en": "Points"}, "text": {"ja": "説明", "en": "Text"},
"pos": [[0, 0, 0], [1, 0, 0], [0, 1, 1]], "val": [0.0, 0.5, 1.0],
"cbar": {"lo": 0, "hi": 1, "unit": "mm"}}]}}}}
spec は untrusted として信頼境界で再検証する。形が違う・NaN・未知のバッジ(passed / limit / null 以外)・未知の kind・空の tabs・要求した言語の訳が 1 つでも欠ける、は -32602(欠けた鍵を全部並べる。他の言語で補わない)。name は英数字と _ -、.. や / は拒否)。返り値の resource_link(text/html)でファイルを受け取る。?lang=xx と ?tab=<id> で開く言語とタブを選べる。fullseye_fix_text)の呼び出し例画像は fullseye_load_image を color=true で読んでハンドルにし、行ごとに「本当はこう
書いてあるべき文字列」と行に密着した bbox を渡す。Python の fullseye.glyph_correct_spec
と同じ items(能力ノート fix-text-in-images、
例 examples/fix_text_in_image.py)。
{"method": "tools/call", "params": {"name": "fullseye_fix_text", "arguments": {
"handle": "fullseye://img/2c1e7a90b3d4f5e6",
"items": [{"text": "電気設備", "bbox": [24, 40, 384, 96]}],
"mode": "repair_flagged"}}}
返り値(本文の抜粋。structuredContent.report に行ごと・マスごとの全記録、fixed_png に
全解像度の PNG のパス、resource_link にも同じファイル):
fix_text(mode=repair_flagged, 床=0.0505 from typeface, 書体 3 本) → fullseye://img/9f0c…
- replaced 電気設備 ・・◆・ mismatch=typo(壊れ 25 %, 距離 0.099)
記号: ・無事 ◆直した ×検証不通過(元に戻した) ?直せない。unrelated は「元の字が指示と無関係」の疑い
mode="rewrite_line" は行を丸ごと同じ書体で描き直す(見逃し・字数違いも直るが書体は変わる)。bbox は省ける(全行そろえて)。暗い字の行を上から検出して items の順に当て、使った bbox と
layout を返す。版面が取れなければ isError で断る(黙って外れた箱で直さない)。
Python では fullseye.glyph_make_spec(rgb, texts) が同じ指示書を返す。skipped + reason_code(missing_text_or_bbox / bbox_too_small / empty_text /
no_font / no_ink / empty_cell / multimodal_colour / cannot_replace / no_lines / layout_implausible)。縁取り・影の文字は
multimodal_colour で断り、画像は触らない。mismatch=unrelated(壊れたマスが 2/3 以上で、距離の中央値も 0.10 以上)のときは、描き直しが
成功していても指示か画像のどちらかが違う疑いなので、前後対比の小図を自動で付ける(vision=auto)。mode は -32602 で入口で拒む。fullseye_import_json / fullseye_export_json)の呼び出し例型付きの値(点群・行列・小さな画像・region …)を JSON 封筒で MCP に注入したり、ハンドルの中身を
JSON で持ち出す。封筒は fullseye.to_json / fullseye.to_jsonable が作る自己記述の形
(能力ノート typed-results-as-json、例
examples/typed_results_json.py)。
{"method": "tools/call", "params": {"name": "fullseye_import_json", "arguments": {
"envelope": {"fullseye_sort": "points", "version": 1,
"payload": {"encoding": "list", "dtype": "float64",
"shape": [2, 2], "data": [[1.5, 2.0], [3.25, 4.0]]}}}}}
返り値の structuredContent に handle / sort / shape(配列 sort のとき)。以後は fullseye_apply
などにそのハンドルを渡せる。取り出しは:
{"method": "tools/call", "params": {"name": "fullseye_export_json",
"arguments": {"handle": "fullseye://img/1524e3d5c6e2dc59", "readable": true}}}
from_jsonable が fail-closed。壊れた封筒・版違い・
形の不一致・未知 sort は -32602)。import_json は json と envelope の両方または両方無しを断る。export_json は封筒が上限(512 KB)を超えると isError で断る —— 大きな画像は JSON で持ち出さず、
ハンドルのまま fullseye_apply で回すか小図で見る。sort が JSON にできないハンドルも断る。fullseye.apply_json(image, op, a, b)(JSON 入力 → op → JSON 出力)や
fullseye.as_value(x)(値でも封筒でも受ける引数入口)で使える。fullseye_estimate_distortion)の呼び出し例本来まっすぐな線(印刷線・建物のエッジ・定規)がレンズで曲がった点列だけから、Brown–Conrady
歪み係数を推定する(チェッカーボードの対応点は不要。能力ノート
estimate-lens-distortion、例
examples/estimate_lens_distortion.py)。
{"method": "tools/call", "params": {"name": "fullseye_estimate_distortion", "arguments": {
"lines": [[[12.0, 41.3], [40.0, 40.2], [100.0, 40.0], [188.0, 41.8]],
[[41.0, 12.4], [40.2, 100.0], [41.9, 188.0]]],
"K": [[190.0, 0.0, 99.5], [0.0, 190.0, 99.5], [0.0, 0.0, 1.0]],
"radial": 2, "tangential": false}}}
返り値の structuredContent.dist が [k1, k2, p1, p2, k3]。そのまま undistort_image に渡せる。
lines(各線 [[x,y],…])と K は untrusted として信頼境界で再検証する —— K が 3x3 でない、
線が (N,2) でない、非有限が混ざる、線が 2 本未満は -32602。各線 3 点未満など道具側の拒否は isError。K 固定(1 枚では中心と p1,p2 が縮退)。向きの違う線を混ぜ、画面いっぱいに張るほど安定。fullseye_apply / fullseye_pipeline / fullseye_inspect)apply / pipeline / inspect の結果が小さい型付きの値(scalar / feature / table / points / matrix /
signal / vector / keypoints / counts / contour)のとき、structuredContent.json に fullseye.from_jsonable
で bit そのまま戻せる封筒が、本文に fullseye.to_markdown の読める描画(表または 1 行要約)が
自動で付く。画素・場・ボリューム系(image / volume / region / color …)や上限(512 KB)超は付けない
—— 大きい画像はハンドル/小図で扱う(fullseye_export_json と同じ判断)。追加情報なので既存の返り
(handle / value / stats …)はそのまま。例: count_obj で終わるパイプラインは structuredContent.json
に {"fullseye_sort":"scalar",…} が入り、from_jsonable で数値を厳密に回収できる。
画像は 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 枚だけ —— 知りたいのは「どの段で壊れたか」だから。
fullseye には fail-soft 層があるが、MCP pipeline はstrict固定とし、op が失敗したら 拒否して理由を返す。途中までの段階記録は返すが、劣化値を後続段へ流さない。
| 境界 | 規約 |
|---|---|
| ファイルパス | 根の下だけ。.. もシンボリックリンクも 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)。
公開する計画入口は fullseye_plan、fullseye_describe、fullseye_choose の3つだけである。
未計測の制約は推測で補わず、fail-closedの理由として返す。tools/list に
{"profile":"minimal"} を渡すか、FULLSEYE_MCP_PROFILE=minimalを設定すると実行に必要な10道具を公開する。
docs/mcp_context_benchmark.jsonを生成する実測では、全20道具が3,256トークン、最小10道具が1,458トークンである。
未検証の設計パターンと重複した計画入口はMCP一覧に出さない。