Inspiration

Every agent client I use has a switch, not a latch. The controls are set before the run: ask me before every write, or let it go. When one decision worries me mid-task, my only move is to stop everything — and lose the ten good edits the agent was about to make. That is not collaboration; it is excessive agency.

A house has many doors. When one of them worries you, you do not weld the building shut. You throw a latch on that door and leave the rest of the house open. That is the whole idea of LATCH.

The idea

Revocable authority, one object at a time. The agent may act on every open object. The human can, at any moment, take one object out of its hands — and only that one. Everything not latched stays open, so the agent keeps working instead of waiting for approval on every small step.

Three human moves cover it: HOLD protects one object, Object rejects or cuts one object, and Lock freezes the whole board once. Unlike per-step approvals, a latch does not stop the agent. Unlike after-the-fact review, it is set before the next write. A refusal tells the agent that the object is held and points it to the release path; it must ask, not override.

This is not a contract feature. Six cards on a board could be six files in a repo, six keys in a config, six hunks in a PR, or six lines in a budget. The deal is only the sample data, because everyone understands: “tighten the deal, but don't touch Indemnity.”

Why WebMCP

A latch needs a structured channel through which the page can tell the agent: this object is off-limits, here is why, here is how to ask. WebMCP is that channel. The page registers tools; each tool's execute checks current authority before it writes and can refuse with a reason. A release asks the human, and a high-risk write requires an on-page confirmation.

The pattern does not have to wait for agent clients to add a new global permission system. A page can ship it today: the human watches the same live state on which the agent acts, with no chat transcript acting as the permission boundary.

What people and agents do together

LATCH is a reference board with seven WebMCP tools. An agent can read the board, inspect one object, propose and apply structured changes, ask to release a held object, load a fixture, and request a final commit. The human can add a binding note, HOLD one object, Object to it or cut it, confirm high-risk writes, reject a release request, or refuse to lock.

What neither side gets from a normal agent workflow: a DOM-only state is not a reliable, structured authority boundary for an agent, and a human cannot keep up with an agent that rewrites six objects at once. With a latch, one sensitive decision stays with the person while the rest of the work keeps moving. It is for anyone who lets an agent write on their behalf — the person negotiating a contract today, the developer running Codex tomorrow.

  • Scenario A opens with six writable objects.
  • apply_card_change rewrites one without the agent touching the UI.
  • Hold this latches one; the other five stay writable.
  • A write against the held object returns isError: true with a reason; its text does not change.
  • request_release asks the human; only an on-page Confirm lifts HOLD.
  • commit_deal is a separate final freeze; Not now keeps the board live.

A / B / C are authority fixtures — all open, one held, one cut. List, Side by side, and One at a time are three attention modes over the same state, not three products.

How I built it

Vite, React, and TypeScript; static assets on a Cloudflare Worker. Source: github.com/MyTelexGlobal/latch (MIT).

The page late-binds document.modelContext or navigator.modelContext and registers seven tools once. Reads use readOnlyHint; agent-proposed content is marked untrusted. HOLD is a human control, not an agent tool. Tools stay registered while execute enforces current authority, so the agent's tool list never flickers and every refusal remains actionable:

document.modelContext.registerTool({
  name: "apply_card_change",
  description: "Replace one card. Fails if that card is on HOLD or cut.",
  inputSchema: {
    type: "object",
    properties: { card_id: { type: "string" }, text: { type: "string" } },
    required: ["card_id", "text"],
    additionalProperties: false,
  },
  execute: async ({ card_id, text }, extra) => {
    const card = boardRef.current.cards.find((c) => c.id === card_id);
    if (card?.held) {
      return { content: [{ type: "text", text: "on HOLD" }], isError: true };
    }
    // Confirm high-risk writes on the page, then replace card.text.
  },
});

High-risk writes, release requests, and the final commit go through an on-page confirmation. Where the client exposes requestUserInteraction, LATCH uses it to signal that a human decision is needed. Cancellation is handled without partially applying a write.

Possible future

LATCH is an authority layer for agentic work. A person can place a reversible HOLD on one sensitive object—code, a record, a contract clause, or a budget line—while the agent continues working on everything still open.

The live deal board is the first working WebMCP expression of that pattern: one visible object, one precise human boundary, and no need to stop the rest of the agent’s work.

Challenges

Four things must agree at every moment: the board, the agent's tool results, the per-object HOLD control, and the confirmation dialog. Keeping tools registered — rather than unregistering them on HOLD — was the key trade-off: a stable tool list plus a refusal with a reason gives the agent a safe next step instead of making the capability disappear.

Where this goes: the same latch in Codex

Picture the board as a repository:

  • HOLD auth/ while the agent refactors the rest: a guarded write path refuses auth/* with a reason and allows work to continue elsewhere.
  • Object with a note — “keep the public API stable” — that the agent must follow for that object.
  • request_release becomes a one-line question to the developer, not a stalled session.
  • Lock is the merge: a separate final freeze.

The developer keeps ownership of architecture, security boundaries, and product intent; the agent keeps its speed on everything that remains open. The primitives are the ones LATCH already runs over WebMCP: a refusal with a reason, a release that asks, and a confirmation that stays with the person. The board is the smallest thing that proves the interaction works; a Codex integration is a future application, not a claim about the current client.

LATCH is an independent WebMCP project and is not affiliated with latch.tools.

Built With

Share this project:

Updates

Submission history