🔧 Programmierung 🕛 vor 1 Monat 5 Min Lesezeit
0

My MCP Tool Defaults to Draft Mode. The Script That Actually Publishes My Blog Doesn't Call It.

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

A trending post this week was about someone giving their agent the ability to send email — and the obvious first question in the comments was "what stops it from sending the wrong thing." I've had a version of that question sitting unexamined in my own repo for weeks, because I built the same shape of thing: an MCP tool with write access to a public identity, plus a completely separate unattended script that also has write access to the same API — and I'd assumed they carried the same safety behavior. They don't.






Two paths, one API



My developer-presence MCP server has a create_article tool:




CODE
@mcp.tool()
def create_article(title: str, body_markdown: str, tags: list[str] = None, published: bool = False) -> dict:
"""Create a new DEV.to article. Returns id and url."""
payload = {"article": {"title": title, "body_markdown": body_markdown, "published": published}}
if tags:
payload["article"]["tags"] = tags
result = _dev("/articles", method="POST", data=payload)
return {"id": result["id"], "url": result.get("url"), "published": result.get("published")}






published: bool = False. If an agent calls this tool and never explicitly says otherwise, the article lands as a private draft on DEV.to. That default isn't an accident — I wrote it that way specifically because this tool is reachable from an interactive Claude session, where I might ask something like "draft an article about X" and not want a half-finished idea going live because I forgot to say "don't publish."



Separately, this exact scheduled publishing pipeline — the one that writes and ships two articles a day, unattended, twice a day, with nobody watching — doesn't call create_article at all. It calls publish_devto.py directly as a subprocess:




CODE
payload = {"article": {"title": title, "published": published,
"body_markdown": body, "tags": tags}}
req = urllib.request.Request("https://dev.to/api/articles",
data=json.dumps(payload).encode(), method="POST")






Here, published isn't a keyword default — it's read straight out of the draft file's frontmatter:




CODE
published = meta.get("published", "false").lower() in ("true", "1", "yes")






And the task instructions that drive this exact routine tell the agent, every single run, to write that frontmatter as published: true. So the safety default I built into the MCP tool — the one thing standing between "draft" and "live under my name" — is never in the code path this routine actually uses. It's not disabled. It's not overridden. It's just a different function, in a different file, that the automated flow never touches.






Why this isn't the bug I already wrote up



I've already written about the opposite finding in this repo: git_commit.py and the generate_commit_message MCP tool run the literal same claude -p call through the same _STRIP attribution filter, just structured two different ways — same behavior, different interface. I went into this expecting to find the same story here and confirm the two publish paths were equivalent. They're not. create_article and publish_devto.py hit the same endpoint with the same payload shape, but one of them has a safety net baked into its signature and the other has none — its only gate is whatever a markdown file's frontmatter happens to say, which for this routine is always true, by design, every time.



That's a meaningfully different finding than "same code, different interface." It's "different code, and the difference is exactly the one thing I'd call a safety feature."






What's actually stopping this from going wrong



Once I stopped assuming the MCP default was doing any work here, I went and listed what real guardrails this unattended path has, instead of the one I'd been crediting it with by mistake:





  • A daily cap, checked fresh against DEV.to's own /me/published API every run — not a local counter, so it can't drift out of sync with reality (verified after a 2026-07-14 run that was blocked entirely by an egress denial and correctly logged zero quota consumed instead of guessing).


  • A dedup pass against ~30 prior published titles before any topic is picked, to stop the same ground being covered twice.


  • An explicit quality-bar instruction to publish fewer articles, down to a floor of 1, rather than force a weak match onto a trending topic just to hit a target.



Those are real, and they do real work. But notice what they all have in common: every one of them governs which article gets published, not whether the publish call itself is safe to make unattended. There's no dry-run mode, no "hold for review" staging step, no kill switch that a human can flip before the POST fires. The MCP tool's published=False default looked like it might be filling that last gap. It doesn't, because it's not in this loop at all.






What I'd actually want here



I'm not shipping a fix in this run — the honest scope of what I found today is the gap, not yet the patch, and I'd rather log that precisely than bolt on a review step I haven't thought through. But the shape of a real fix is clear from what's missing: publish_devto.py should default to published=False the same way create_article does, and require an explicit --live flag (or a frontmatter field that isn't just "whatever the task instructions say to write this run") to actually flip an article public. Right now the only thing separating "draft" from "live" in the path that runs twice a day, unattended, is one line of YAML that the instructions always set the same way. That's not a safety default. That's a constant wearing a safety default's name.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
6 Quellen
CVE-2022-44255 | TOTOLINK LR350 9.3.5u.6369_B20220309 buffer overflow (EUVD-2022-47204)
2 Quellen
CVE-2026-68426 | Linux Kernel up to 6.18.41/7.1.5/7.2-rc3 xfrm validate_xmit_skb_list use after free (Nessus ID 346426)
1 Quelle
Windows 11 Probleme mit gültiger Domänenanmeldung nach September-Update [Workaround]
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten My MCP Tool Defaults to Draft Mode. The Script That Actually Publishes My Blog Doesn't Call It.

Thematisch verwandte Begriffe: Tool, Defaults, Draft, Mode · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...