You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
crates/stella-tools/src/custom.rs hardcodes read_only: false for every .stella/tools/*.toml tool, and MCP tools get the same treatment — so a genuinely read-only third-party tool cannot say so, and the MCP spec's own readOnlyHint / destructiveHint / idempotentHint annotations are discarded entirely.
PR #3281 built the machinery that makes ingesting them safe: ToolContract::declared records a claim while grading it High, and trusted_read_only() is the one place the distinction is load-bearing. This is #2716 §6.
Definition of done
.stella/tools/*.toml gains read_only, risk, idempotent and an optional [output_schema]. The manifest is the TOML serialization of the same contract, not a second format.
MCP tool annotations map into the same contract fields.
Both arrive as Provenance::Declared. A claim is displayed and available to policy; it never admits the tool to the read-only/speculation dispatch sets on its own — that is trusted_read_only(), and crates/stella-tools/src/contracts.rs's tests already pin it.
The one route from claim to trust is the existing foundry adoption gate (crates/stella-tools/src/foundry_gate.rs): witnessed adoption + human enablement + byte-digest match. Nothing else upgrades provenance.
Document the asymmetry in the stella-tools and stella-mcp crate READMEs.
Why the untrusted posture is not paranoia
The read-only bit is not decorative: it admits a tool into concurrent dispatch alongside other reads, and it fences a verifier into a set it cannot mutate the workspace from. A false claim there is a data race and a broken verification boundary, not a cosmetic error. An MCP server's metadata is untrusted for the same reason its output is (#2689).
Witness
A manifest declaring read_only = true is displayed as read-only, is refused by a Medium risk ceiling, and does not appear in the executor's read-only dispatch set — with the built-in equivalent doing all three the other way.
crates/stella-tools/src/custom.rshardcodesread_only: falsefor every.stella/tools/*.tomltool, and MCP tools get the same treatment — so a genuinely read-only third-party tool cannot say so, and the MCP spec's ownreadOnlyHint/destructiveHint/idempotentHintannotations are discarded entirely.PR #3281 built the machinery that makes ingesting them safe:
ToolContract::declaredrecords a claim while grading itHigh, andtrusted_read_only()is the one place the distinction is load-bearing. This is #2716 §6.Definition of done
.stella/tools/*.tomlgainsread_only,risk,idempotentand an optional[output_schema]. The manifest is the TOML serialization of the same contract, not a second format.Provenance::Declared. A claim is displayed and available to policy; it never admits the tool to the read-only/speculation dispatch sets on its own — that istrusted_read_only(), andcrates/stella-tools/src/contracts.rs's tests already pin it.crates/stella-tools/src/foundry_gate.rs): witnessed adoption + human enablement + byte-digest match. Nothing else upgrades provenance.idempotentfield toToolContracthere, with this as its consumer — it was omitted from feat(stella-tools): ToolContract, AuthzGate port and the gate decorator — the governance half of tool-first #3281 rather than shipped unread.stella-toolsandstella-mcpcrate READMEs.Why the untrusted posture is not paranoia
The read-only bit is not decorative: it admits a tool into concurrent dispatch alongside other reads, and it fences a verifier into a set it cannot mutate the workspace from. A false claim there is a data race and a broken verification boundary, not a cosmetic error. An MCP server's metadata is untrusted for the same reason its output is (#2689).
Witness
A manifest declaring
read_only = trueis displayed as read-only, is refused by aMediumrisk ceiling, and does not appear in the executor's read-only dispatch set — with the built-in equivalent doing all three the other way.Refs #2716, #2689, #3281.