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_changerewrites one without the agent touching the UI.- Hold this latches one; the other five stay writable.
- A write against the held object returns
isError: truewith a reason; its text does not change. request_releaseasks the human; only an on-page Confirm lifts HOLD.commit_dealis 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 refusesauth/*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
- chatgpt
- chrome
- cloudflare
- codex
- css
- html
- javascript
- mcp
- openai
- react
- typescript
- vite
- vitest
- webmcp
Log in or sign up for Devpost to join the conversation.