Decided and built (HISTORY.md decisions 65 to 68); the tools
below are as built, and the maintainer's answers are under "Decided". Written
2026-10-15 on
branch agent-tools, from the maintainer's direction: "take the "trunk"
lessons learned along with other lessons learned and produce a set of easy to
use agent tools available over the remote MCP that even local agents could
use to perform editing." "Trunk" there is OCX's earlier design, on the
branch trunk of this repository. This proposal settled
PLAN.md's "An agent's MCP surface"; the names are the
maintainer's first answer.
The problem
An agent reached through OCX's MCP can talk, post and sync, but cannot read
or change a file. A remote agent, one reached through its provider's tunnel
(docs/remote-agents.md), edits its own clone and brings
its work in through sync, as a Fossil bundle: five shell steps that a weak
model gets wrong. A local model, one OCX runs itself on a node (PLAN.md's
"native agent"), has no clone of its own at all. Both need the same thing: a
few tools that read and change the run's own checkout, test the change, and
save it.
What the tools act on
The run's checkout, $$tree$$, the one its workspace steps made, where its
MCP server runs. The agent needs no clone, no Fossil and no password. The
run's sandbox and the capabilities of the run's user, the Fossil user its
process acts as (process.user), bound everything, as for every run. Edits
stay in the run's workspace until saved; a run that ends drops what it did
not save. A remote agent that keeps its own clone may still use sync.
The tools
Six, the fewest that cover one edit from start to saved; the wiki and
unversioned files' tools came with the maintainer's fifth answer, and one
more is added only when a task shows the need. Every answer starts with
ok, true or false; a refusal's next_step says in one line what to do
instead.
| Tool | Takes | Gives |
|---|---|---|
ocx_context |
nothing | the run's objective, its working branch, the files changed and not saved, and the tests it may run |
ocx_find |
text, matched exactly, case and all; optionally path, to search under |
each line holding it, as its path, its line number and its text, at most 50; and how many more there are |
ocx_read |
path; from, a line number, default 1 |
text: up to 200 lines from from, exactly as in the file, with no numbers in it; first_line, last_line and lines, how many the file has |
ocx_edit |
path; old, the exact text to replace, copied from ocx_read's text; new, its replacement |
the changed lines, as ocx_read gives them |
ocx_test |
optionally name, one of the tests ocx_context lists; without it, every test |
whether each passed, and the last 100 lines it wrote |
ocx_save |
title, intent, expression, demonstrates: one line each, a part that runs over lines read as one |
the check-in, by its hash, its link on the server, and the user the server's receipt names |
ocx_edit replaces old only where it appears exactly once. With old
empty it adds new at the file's end, making the file if there is none,
since the battery's first live trial showed models adding to a file that way
(decision 66). It neither deletes nor renames a file.
A test is a command the checkout's ocx-tests.json names, run in the
checkout within the run's limits.
ocx_save commits every change on the run's working branch, never the
branch trunk, and pushes it as the run's user. It refuses unless each test
the run lists has passed since the last edit. Its four lines are the
check-in's comment, which the server requires of every check-in:
- title: what the check-in does;
- intent: why;
- expression: what changed, and where;
- demonstrates: the test that shows it works.
When the push fails, the check-in stays in the run's clone, and next_step
says to save again; with nothing changed, ocx_save pushes what is saved but
not yet sent. It writes no verdict; a review is a person's or
another agent's, as now. Answering on the forum stays post.
Lessons it follows
Each from trunk's code and history; from the battery, OCX's trials of fresh readers, small local models among them; or from agents' use since, such as Slowpen's, a ChatGPT agent's report on mechanical-turk.
- Copy, never guess, the text to replace.
ocx_read'stextis exactly whatocx_edit'soldtakes, its line numbers kept apart so none is pasted in. Found nowhere, the refusal says "read the lines again with read and copy old exactly from its text"; found more than once, "add a line before or after to old to make it unique". Trunk's prompt: "read the exact surrounding range and copy the literal text; do not guess anchors from memory." - One field per meaning, and no example to copy as a value. The battery's smallest model filled a field with the caller in 19 of 30 runs, until it was renamed for whose it is, and copied example values into real calls.
- A few tools, each named for what it does. Trunk's MCP grew to about 90 tools, and two names drifted from its list, so "every real client saw 'unknown tool'".
- Every answer says first whether it did what was asked. The maintainer, 2026-10-14: "I read the "stopped" as an error. These tools should return a pass/fail condition somewhere."
- Bounded output. 200 lines read, 50 matches, 100 lines of a test: a small model given 7,500 tokens missed most of them.
- A refusal steers. It names what was wrong and the tool or field that
would work, so a weak model corrects itself (trunk); never a value with a
hidden meaning, such as trunk's
__all__, which matched nothing and sent a model round to its step cap. - Test before saving, and trust the record over the model. What was run is what the log says, not what the model claims.
- Names a client can call. ChatGPT's client called
ocx.statusasocx_status; MCP names are safest of letters, digits,_and-.
Decided
The maintainer's answers, 2026-10-15, each as built:
- The names: "don't care, just be consistent." Every tool is
ocx_and its name over MCP; these arecontext,find,read,edit,testandsave. - Where a run's tests come from: "our workspace clone is what i'd
expect." The project names them in its own checkout,
ocx-tests.json, each a name and its command; no key is added to a run. - The working branch: "make it unique like the runner name."
agent/, the run's user, and eight hex digits from its checkout, as a run user's name has eight. okandnext: "what does the data show works best?" Every tool's answer begins withok, and a refusal givesnext_step(nextis a word Crystal keeps). The battery measured no effect either way, so both stay (decision 68).- Slowpen's other asks: "yes, include wiki and unversioned files (for
binary file sharing). gain parity with trunk agent tools that justify
themselves."
wiki_read,wiki_edit(firstwiki_write, a page whole against its version, until the battery showed models edit a page as a file),unversioned_list,unversioned_getandunversioned_put; trunk's other tools come when the battery shows a model needs one.