◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Cowork
◈ Cowork/2026-07-12Advanced

The Night a Connector Grew Write Tools, Your Unattended Job Could Quietly Send Something

The moment a connector update quietly adds write tools, a job you run unattended can suddenly send email or delete files. Here is how to build a gate that snapshots a connector's tool surface, diffs it, and stops unapproved writes before they run — drawn from indie development.

cowork16mcp20connectorunattended4security19

✦ Premium Article

I was reviewing the logs of a nightly job the next morning, as usual. The run itself had succeeded. But the tool list of the connector I use held a few names that were not there the week before: create_message, send_message, delete_file. The upstream service had updated, and a connector that used to only read could now compose and send email, and manipulate files.

That progress is welcome in itself. And yet a small chill ran down my back. My nightly job picks the tools a connector offers on its own, according to the situation. Until yesterday, the worst outcome of a bad decision was a misread. From tonight, the same misjudgment could turn into a send, or a delete. The danger was not born the moment my code changed. It was born the moment the connector's tool surface quietly widened.

In July 2026 the Microsoft 365 connector gained write tools — composing, sending, and organizing email, calendar operations, and creating or updating files on OneDrive and SharePoint. A human sitting beside the machine can pause a breath before the Send button. In unattended operation, unless you build that breath into the design yourself, no one stops you. Drawing on running four sites unattended as an indie developer, we build, as working code, a gate that detects when tools have grown and dams up unapproved writes before they execute.

The danger is born when a new tool appears

When we think about the security of an unattended job, we tend to look at our own prompt and permission settings. But the range of operations an agent can actually call is, in the end, decided by the tool list the connector exposes. That was my blind spot. Without changing a single character of my code or my permission scope, the upstream service bumping a version can widen the set of possible operations.

With a read-only connector, the worst failure stops at "read the wrong thing." Add one write tool and the nature of failure changes. A misread can be rolled back; a sent email or a deleted file does not come back easily. So what deserves watching is not "what tools exist now" but the diff: "what grew since last week."

I settled on a policy. Detect the change in tools every time — above all, the appearance of a new write or destructive tool. Do not let an unapproved one proceed to execution. Approval exists only when I explicitly write it into a policy file. This direction — a newly grown write tool is denied by default — sits at the very outer edge of the unattended job.

Sort tools into read / write / destructive automatically

The first part is a classifier that sorts the tool list by risk. MCP tools carry a name and a description. Perfect semantic analysis is not needed. Sorting into three tiers from the verb in the name is enough in practice. Anything ambiguous is pushed to the safe side and collected as "needs review."

# classify_tools.py
import re
from enum import IntEnum
 
class Risk(IntEnum):
    READ = 0         # reference only; nothing to roll back
    WRITE = 1        # changes state; usually reversible
    DESTRUCTIVE = 2  # send / delete / payment; hard to reverse
 
# verbs treated as destructive (a send, a delete)
DESTRUCTIVE = r"(send|delete|remove|purge|archive|pay|charge|transfer|deploy|publish|revoke)"
# common write verbs
WRITE = r"(create|update|write|edit|insert|upsert|move|rename|set|add|append|patch|put|post)"
# read verbs
READ = r"(get|list|search|read|fetch|find|query|describe|lookup|view)"
 
def classify(tool_name: str) -> Risk:
    name = tool_name.lower()
    verb = re.split(r"[_\-.]", name)[0]  # treat the leading token as the verb
    if re.fullmatch(DESTRUCTIVE, verb):
        return Risk.DESTRUCTIVE
    if re.fullmatch(WRITE, verb):
        return Risk.WRITE
    if re.fullmatch(READ, verb):
        return Risk.READ
    # if the verb is unknown, scan the whole name for a destructive word
    if re.search(DESTRUCTIVE, name):
        return Risk.DESTRUCTIVE
    if re.search(WRITE, name):
        return Risk.WRITE
    # still unknown: do not assume READ; pick it up as WRITE
    return Risk.WRITE

I include pay and charge among the destructive verbs because I have payment connectors in mind. If a billing connector like Stripe starts offering create_charge or refund, an unattended job could actually move money — exactly the kind of irreversible operation I want stopped by default to avoid an accident.

The line I cared about is the last one. Treating an unknown verb optimistically as READ would turn the classifier itself into a hole. Leaning "unknown means write or worse" pushes the errors onto the side of over-detection. Over-detection I can clear with a single approval; a destructive tool I miss goes unnoticed until morning. You decide, in advance, which error you are willing to carry.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A way to snapshot the tools a connector exposes and, by diffing against last week, surface only the write tools that newly appeared
✦A classifier that sorts tool names into read / write / destructive by verb, plus a preflight gate that halts unapproved destructive tools before they run
✦Measured results from a two-stage guard that logs every intent to send or delete into a dry-run ledger for a next-morning review
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ Cowork2026-09-08
Until I planted a failing sample, my unattended checks had never once failed
A check running on an unattended schedule lost its exit code to a single pipe added for readable logs. I measured where the status disappears and built a small harness that checks the checker.
◈ Cowork2026-08-23
Not Every Unreachable Connector Means the Same Thing in an Unattended Run
When an unattended task cannot reach a connector, the cause splits into three states: still connecting, unauthenticated, or genuinely absent. Each demands the opposite response. Here is the resolver that tells them apart.
◈ Cowork2026-10-08
The Night I Halved the Instructions I Load Every Time: Three Drawers Before Any Cutting
The longer an instruction file grows, the more rules slip through. Here is how I sorted what gets loaded every time into keep, move, and delete, plus a small script that measures each heading.