Skip to content

[Decision] connector-mcp tool-snapshot pin (#26709 item 3(c)): one aggregate toolsSnapshotSha256, or a per-tool pin record that can name the tool a server rewrote #27401

Open

Description

@objectstack-fleet

epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U

carries item 3(c) of #26709; #26709 keeps item 1 (PR #27387). Filing gate: ② a contract-shape decision the maintainer takes. Raised by the os-dev on #26709 as needs_decision (report comment 6488553467); the seat's own readings below. Child of #27293 (E1). ⛔ Not a claim; item 3(c) is not dispatched while this card is open.

维护者速读

Background

#26709 item 3(c), verbatim: "providerConfig.toolsSnapshotSha256 (optional): when set, the hash of the selected tools' name + description + inputSchema (canonical JSON) is compared on every materialization; a mismatch marks the instance degraded with a message naming the changed tool, so a server-side rewrite of a tool's description or schema (the "rug-pull" class) is visible instead of silently accepted." Triage (6487372008) graded it a protocol addition, p1, domain:server.

Governing text: the card's own item text (above) and its acceptance line; AGENTS.md Prime Directive #12 (contract-first: refuse loudly at authoring or load, never tolerate silently); triage 6487372008 「include keeps one meaning across openapi and mcp」 (the pin should compose with include, keyed the same way).

Protocol statement: Metadata-protocol: added either way (a new author-written providerConfig key on mcp); Clause-②: yes (widening).

Premises (each with its re-check)

  1. One aggregate hash cannot attribute a mismatch to a tool. (Arithmetic; no re-check.)
  2. An unchanged instance is never re-materialized in a running process, so there is no earlier good snapshot in memory: git show origin/main:packages/services/service-automation/src/plugin.ts | sed -n 1844,1847p ("unchanged entries are left alone (an unchanged MCP instance is NOT reconnected)"), and the signature check at ~:1970. Read 2026-10-29T05:10Z at origin/main.
  3. A degraded channel exists to report a mismatch: the provider throws ConnectorUpstreamUnavailableError and the materializer registers a degraded husk retried with backoff (degradeConnectorInstance, plugin.ts:2172). Its log wording is "upstream unavailable … retrying with backoff", which misdescribes a pin mismatch.
  4. include on mcp is a name allowlist (mcp-provider.ts:223-226).

The question

When a remote MCP server silently rewrites one of the tools an app depends on, what should the administrator see: "something changed", or "tool X changed"?

Options

What it does What the administrator sees Effort
A Keep the ruled single toolsSnapshotSha256; on mismatch degrade the instance with expected and observed digests "The tool set changed" — no tool named; they diff the server by hand domain:server S, 1 PR; no precedent
B Replace it with providerConfig.toolsSnapshot: a record keyed by tool name, each value the sha256 of that tool's canonical name + description + inputSchema "Tool create_issue changed" (and "pinned tool x is gone"); meets the card's acceptance line domain:server S–M, 1 PR; #27373's offline tool can emit the record
C Single hash plus in-process last-good memory Names the tool only if the same process saw a good snapshot first — at cold boot (the common detection point) falls back to A S + hidden state; not recommended
D Drop item 3(c) for now (close not planned); include already keeps new tools out No rewrite detection zero

Business meaning: A is a seal on the whole box; B is a seal on each item in the box; D is no seal yet.

四维分析

os-decision-facets

  • ① 项目长远合理性:两年后的样子是「每个被依赖的远程工具都能被钉住,变了能精确报出是谁」(参照 npm/pnpm 锁文件按包逐条记 integrity,而不是给整棵依赖树一个总 hash);B 与 include 同样按工具名为键,两把钥匙能叠用;A 只能报「变了」,逐工具的数据算出来又扔掉;C 引入依赖进程历史的隐藏状态;D 不增生但把这类攻击面留着。
  • ② 实际业务拉动:板上没有客户点名要这个功能;拉动来自你 2026-10-27 对目录调研的指示(「建议 1+2立卡」),3(c) 在那张卡的范围内。零客户拉动按规则会荐 D,但该项已在你指示的范围内,所以本卡只问形状,D 留作你收回的出口。
  • ③ 防 AI 犯错:B 是结构化契约,一条钉错的 pin 会指向它那个工具,响亮且可定位;A 把「钉的值过时」与「工具被改写」压成同一条模糊报错,AI 和管理员都只能猜。
  • ④ 创业阶段不扩散:A 与 B 工作量相当(都是一个 helper + 测试),都不新增门禁;D 最省。
    Prior rulings read: toolsSnapshot, tool snapshot hash, rug-pull → the card's own item text and triage 6487372008 (grade and lane only, not the shape); thread: connectors: auto-shelf readiness — declarative include and a loud Swagger 2.0 refusal for connector-openapi; tools/list pagination, fatal 401 and a tool-snapshot hash for connector-mcp; an offline spec-normalization tool #26709; repo: objectstack-ai/objectstack

Recommendation

B. The card's purpose is to make a server-side rewrite visible and attributable, and only per-tool data can name the tool; B keys the pin by tool name like include, so the two compose. Not A: it misses the card's own acceptance line. Not C: hidden, history-dependent behaviour. Not D: the item is inside the scope you directed; D stays the exit if you no longer want it. Confidence gap: whether the degraded channel's "upstream unavailable … retrying with backoff" wording and retry loop should be specialised for a pin mismatch (it reconnects on every backoff until the pin or the server changes); under A or B the dev should name that in the PR.

Self-check: 只看①选 B;②③④ 是否翻转:否(② 零客户拉动指向 D,但该项在你已指示的范围内,只影响是否保留出口,不翻字母)。

After the ruling

Related

#26709 (parent item) · PR #27387 (item 1) · #27373 (offline tool that could emit pins) · #27293.

Dedupe

Split by the dev's needs_decision; the parent item is #26709. The 500 most recent issues and PRs, titles grepped for snapshot|rug-pull|toolsSnapshot (read 2026-10-29T04:10Z): 0 hits; control connector on the same set: 10 hits.

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 可对外的 API | none | P②

    Triage: p1 · domain:server · decision stays with the maintainer · 2026-10-29T06:00Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-bill · session_01Utp6CbQLcy8yg6vhJVXXi8

    Brief-claimed-by: session_01Utp6CbQLcy8yg6vhJVXXi8 · batch #428 · 2026-10-29T10:17Z

    Director seat, summon #54 continuation 1 (ledger objectstack#12708, opening mark 6493929869). Presented to the maintainer in batch #428 as item 3 of 4 — #26709 item 3(c), split by the dev's needs_decision (6488553467), epic #27293 E1.

    Seat increment (re-read at origin/main ded67bfb, 2026-10-29T10:17Z):

    • Premises re-run, all hold: an unchanged MCP instance is not reconnected on reconcile (plugin.ts L1844–1847), so a running process holds no earlier good snapshot; the degraded channel is degradeConnectorInstance (plugin.ts L2172, called L2077) and its wording describes an unavailable upstream; toolsSnapshot has 0 hits under packages/ (nothing shipped yet); item 1 (openapi include, PR feat(connector-openapi): declarative providerConfig.include operation allowlist #27387) is merged and not held by this card; connectors: an offline OpenAPI normalization script for connector-openapi — Swagger 2.0 → 3.x, YAML → JSON, $ref bundled and dereferenced, absolute servers[0].url, sha256, and a generated include list #27373 (the offline normalisation tool the card names as a pin emitter) is closed.
    • Prior rulings: triage 6487372008 graded the item (protocol addition, p1, domain:server) and ruled nothing on the shape; check-prior-rulings.mjs --card 27401 names ADR-0129 D3, ADR-0097 §6, ADR-0109 §4, ADR-0032 — term matches, none decides a snapshot pin's granularity. The item's own acceptance line ("a message naming the changed tool") is the governing text, and it contradicts the item's key shape (one aggregate hash); the maintainer picks which half gives way.
    • Facets: agree with the card on all four. ① per-tool integrity, keyed by tool name like include, is the lockfile shape (npm / pnpm record integrity per package, not one hash per tree); the two keys compose. ② no customer named it; the item is inside the scope the maintainer directed on 2026-10-27 (「建议 1+2立卡」), so D is the exit if that scope is withdrawn, not the default. ③ B names the tool and distinguishes "pin stale" from "tool rewritten"; A compresses both into one undiagnosable message; C hides state in process history. ④ A and B cost the same (one helper + tests); neither adds a gate.
    • Seat's letter: B — providerConfig.toolsSnapshot, a record keyed by tool name whose value is the sha256 of that tool's canonical name + description + inputSchema; a mismatch degrades the instance with a message naming the tool (and a pinned tool that is gone); the aggregate toolsSnapshotSha256 spelling is retired before it ever ships. Carry the card's confidence gap into the dispatch: the degraded channel's "upstream unavailable … retrying with backoff" wording and retry loop should be specialised for a pin mismatch (no reconnect loop fixes a pin).
    • Workload of B: domain:server · 1 PR · S–M · Metadata-protocol: added, Clause-②: yes (widening) ⇒ contract review before landing · no precedent.
    • After the ruling: the [Epic] Marketplace and connector auto-shelf — E1 platform prerequisites (openapi/mcp hardening, outbound OAuth, request signing), E2 administrator credential hand-off, E3 author recipes and handbook, E4 the first shelf of official connector apps #27293 epic PM dispatches it to domain:server with Ruling-ref:; the card leaves the box under pm:epic (its stroke).

    Prior rulings read: toolsSnapshot, tool snapshot, rug-pull, include → triage 6487372008 (grade only); thread: 6489246137; repo: objectstack-ai/objectstack

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions