Build1 distinct publisher3 min readPublished Updated
A builder audited the human-in-the-loop gate on his own MCP write tool and found a keyword argument the caller sets. Optional evidence makes an optional check.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
An engineer running an MCP server for his DEV.to publishing pipeline went back and read the safety gate he had written in front of his own live-article write tool, and concluded in a post on dev.to that it was not a permission boundary [1][2]. The reason generalises to most of the human-in-the-loop checks being wired into agent tools right now: the confirmation is a keyword argument, and the agent is the caller [6].
The tool, update_article, edits a live article by id, including title, body and published flag [2]. The dangerous case is overwriting the body of something already published, because DEV.to keeps no version history and a bad write is gone the moment it lands [3]. So in July he added the check everyone adds: if the target is published and the call touches title or body_markdown, and confirm is not set, return the proposed diff with applied set to false instead of writing [4][5].
That does something. It does not do what it looks like. There is no code path that forces a caller through the preview step before it can set confirm=True [6]. An agent that never saw the docstring, or that decided on its own the article needed fixing right now, can call update_article with a new body_markdown and confirm=True as its very first move, and for that call the gate is simply not there [7]. In his words, it only stops the caller who was already going to stop and ask [8].
The second layer is the more instructive failure, because it is a better check that breaks the same way. A few weeks later he added expected_fingerprint, a hash of the article state at preview time, so the tool refuses a stale write even when confirm=True is supplied [9][10]. That catches drift the first version could not, such as someone editing the article on-site between the preview and the confirm [11]. But expected_fingerprint defaults to None, and omitting it skips the check rather than the write [12]. His own docstring says the check is opt-in, not mandatory [13].
The asymmetry is the part worth stealing. confirm defaults to a value that withholds the write, while expected_fingerprint defaults to a value that permits it [14]. A check that fires only when the caller supplies the evidence for it is a check the caller can opt out of by omission, not just by an explicit bypass flag [15]. The risky caller is not the one passing confirm=False; it is the one that never learned the parameter exists, and Python does not make that caller pass anything at all [16].
The fix he describes is one line: annotate expected_fingerprint as a required string rather than defaulting it to None, and drop the is not None from the condition, so a live content write arriving without a fingerprint is refused instead of waved through [17][18]. He has not shipped it, and his stated reason deserves to be read straight rather than as an excuse: the tool is only ever invoked by him, through Claude Desktop, on his own machine, so the realistic threat model is his own mistake or a confused agent turn, not an adversarial caller [19][20]. He also concedes what that leaves the design resting on, which is that he mostly remembers to preview first [21].
What to watch is which side of the default each destructive tool in a stack falls on, and that is testable without reading any docstrings: call the tool with only the arguments it actually requires and see whether the write lands. The broader agent-tooling conversation this post is responding to is full of signed capabilities, policy layers and pre-write human checks [22]; the ones that live in the tool's own signature inherit this hole, and the ones that sit outside the callable do not.
Ranked by verification strength, evidence, and original report placement.
The author of a dev.to post says he built something that looks like a gate in front of a dangerous tool, then read his own code closely enough to notice it is not one.
The tool in question is update_article in the MCP server the author runs for his DEV.to publishing pipeline; it edits a live article by id, covering title, body and published flag.
Overwriting a published article's content is the dangerous case because DEV.to keeps no version history, so a bad write is gone the moment it lands.
In July the author added a gate computing live_content_write as the article already being published and the call including title or body_markdown.
If live_content_write is true and confirm is not set, the tool returns applied: False with the proposed diff and a reason stating that title/body_markdown changes to a published article require confirm=True because DEV.to has no version history to undo the write.
confirm is a keyword argument the caller supplies, and there is no code path that forces an agent through the preview step before it can set confirm=True.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Strong first-party code evidence, no external corroboration
The core technical claims are shown rather than asserted: the live_content_write computation, both refusal branches, the refusal reason strings, the None default, and the quoted docstring line all appear in the post, and the argued conclusion follows directly from that code. What is missing is any independent check — the repository is not linked or reviewed, the self-test is only promised, and the platform behaviour (DEV.to keeping no version history) is stated by the author alone.
One tool, one user, hardening unshipped
Adoption evidence is limited to a single personal MCP server invoked only by its author through Claude Desktop on his own machine. Two layers are disclosed as shipped (the confirm gate and the optional fingerprint), while the fix the post argues for is explicitly not shipped. No other users, deployments, downloads, or dependent projects appear anywhere in the cluster.
Slightly understated relative to its own evidence
The headline claim — that a caller-supplied confirm flag is not a permission boundary — is fully carried by the quoted code, and the author repeatedly discounts his own finding (single-user tool, threat model is himself, fix not shipped, 'mostly remember to preview first'). If anything the generalizable point about safety checks that fire only when the caller supplies the evidence applies more widely than the modest, self-limiting framing suggests. The small negative rather than a larger one reflects that the generalization is drawn from one hobby tool and one unverified reading of the week's discourse.
Personal-brand publishing, no commercial stake disclosed
The only visible incentive is developer-audience attention: the post is published on dev.to by the author of the tooling it describes, and the tooling exists to publish to dev.to, so writing about it feeds the same pipeline. There is no vendor, sponsor, funding round, paid product, or competing framework being promoted or attacked, and the piece works against simple self-promotion by cataloguing the author's own design mistake.
Reliable on the narrow code claim, thin everywhere else
Confidence is high that the described code behaves as quoted and that the author's design conclusion follows from it, because the relevant lines are reproduced. Confidence is low on everything beyond that: one publisher, one item, one self-reporting author, no repository or third-party review, unverified platform behaviour, and an unverified claim about a wider industry pattern.
build
Before you spend quota on an agent skill, make it pass an eval harness1 distinct publisher
build
MapMcp publishes your whole tool list before you add a single auth check1 distinct publisher
build
Your agent does not need every MCP tool, and the toolbox is the liability1 distinct publisher
build
Claude Code hands a hook's exit code 2 back to the model as a refusal reason1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
2 articles · August 16, 2026