Otōto is an MCP server for Claude Code and OpenCode. Your agent hands it a question; a small model on a server you choose explores the code and brings back a short answer, with the lines that back it checked and quoted.
Where does a failed request move on to the next model server?
Otōto answers
Llm::request tries each endpoint in turn; one that fails is marked down for 60 s and the next is asked. When every failure was one that passes (a dropped connection, a 502), it goes round once more after 2 s.
4 turns · 6 tool calls · 20.0k tokens on the small model · 18 s
Fig. 1Real asks on public repositories and on Otōto itself, and a plugin; replies shortened. Claude Code reads a paragraph and a few quoted lines, not the files they came from.
01How it works
Your agent asks. Its little brother does the reading.
Every file an agent opens, every search result and every dead end stays in its context for the rest of the session, and each later turn carries it again. Otōto does that exploring in a context of its own, thrown away after each call.
1
Claude Code asks
A whole question to ask, or locate and callers for where something is and who uses it.
2
Otōto explores
A small model on your server reads, outlines and searches the repository with read-only tools, as many turns as it takes.
3
A checked answer comes back
A few lines, with every path:line checked against the files and quoted. A claim the code does not back is flagged.
Your agent's context, when it explores itselfEverything it read stays, and goes with every later turn.
about 2,600 lines of files and searches
Your agent's context, when it asks OtōtoThe answer stays; Otōto's reading was thrown away.
a paragraph and three lines
Fig. 2What one lookup leaves in your agent's context: an illustration, not to scale. With a paid model the smaller context is a smaller bill; with one model on both sides (OpenCode on a local model), a main agent that stays on the task.
02Tools
Some questions need a model. Most lookups don't.
Three tools hand the work to the small model. The rest answer at once, from the files and from git, for when your agent already knows what it wants to see.
Tool
What it does
For example
askmodel
A whole question, answered in a few lines with checked citations.
how is auth wired into the router?
locatemodel
Where something is defined, or where a behaviour is decided.
where are retries configured?
callersmodel
Who calls or uses a function, type or setting.
who uses DOWN_FOR?
read
Code by address, many at once: a declaration by name, a range, a config key.
src/ototo/agent/llm.rs#brief, pom.xml#spring-core
outline
A file's or a directory's declarations, with their lines.
src/agent/
search
Exact text or a regex across the repository, as path:line hits.
-F RETRY_AFTER
changes
What a branch, a commit or a range changed, by declaration.
--commit 907d144
history
The commits behind some lines or a declaration.
src/ototo/agent/llm.rs#request
digest
A build or test run cut to its failures, errors and summary.
ototo digest -- cargo test
$ ototo read src/ototo/agent/llm.rs#brief── src/ototo/agent/llm.rs:290-301 brief · fn brief(e: &anyhow::Error) -> String
290| fn brief(e: &anyhow::Error) -> String {
291| if let Some(r) = e.downcast_ref::<reqwest::Error>() {
292| if r.is_connect() {
293| return "unreachable".into();
294| }
295| if r.is_timeout() {
296| return "timed out".into();
297| }
298| }
299| let s = e.to_string();
300| if s.chars().count() > 120 { … } else { s }
301| }
Fig. 3One function by name, with its lines: no file to open, no model involved.
$ ototo changes --commit 907d144── changes in 907d144 changes: one commit, or a range, told by declaration, 3 files, +123 −20
M src/ototo/main.rs +13 −2
changed Command (69-291)
changed main (361-585)
M src/ototo/mcp.rs +19 −7
changed instructions (31-86)
changed tools (220-380)
M src/ototo/tools/git.rs +91 −11
added commit (171-181) added range (184-205)
Fig. 4A commit told by the functions it touched, instead of a diff to read through.
03Plugins
For the files your build tools understand, and your agent has to guess at.
A plugin reads one kind of file the way its tool does: read pom.xml#spring-core returns the version Maven resolves, not the line that says ${spring.version}. Plugins are WebAssembly, signed and sandboxed: a file plugin sees the repository, read-only; an archive plugin is lent only the files it opens; a forge plugin reaches only the hosts you grant it and never sees its token. Each has its own version, and ototo plugins update brings yours up to date between releases.
archive archive
Inside a JAR without unpacking it; a class as its declarations, no javap.
*.jar, *.war, *.zip, *.whl, *.class
maven file
A dependency's effective version and scope, through parents, BOMs and properties.
pom.xml
spring-config file
A property as each profile sees it, and where each value is set.
application*.yml, .properties
terraform file
A resource with its variables worked out, per environment.
*.tf, *.tfvars, *.tofu
kube file
A Kustomize resource as it builds; a Helm value and the templates that use it.
kustomization.yaml, values.yaml
gitlab-ci file
A job as GitLab runs it: includes, extends and anchors resolved.
.gitlab-ci.yml
compose file
A service as docker compose runs it, overrides and .env applied.
compose.yaml
gitlab forge
The branch's merge request, a pipeline by stage, a failed job's log cut to what failed.
ototo ui is a dashboard over Otōto's run log, on your machine: every call from every session, how long the model server took, what the small model did turn by turn, and the exact reply your agent got. ototo tail follows calls as they run. The page listens on 127.0.0.1 only, and needs its key.
05Organisations
On your machines, for the whole team.
Otōto runs where your code is, and talks only to the model servers you give it. Nothing is sent to us, and nothing checks for updates unless you run ototo update. For a company rolling it out to many machines:
Your model servers
vLLM, llama.cpp, Ollama, LM Studio or any OpenAI-compatible server. An organisation can make its own the only ones, so code goes nowhere else.
Settings you enforce
One file under every user's own; what it lists as enforced applies whatever a user sets: model servers, telemetry, trusted plugin signers. ototo doctor says what is enforced.
Managed rollout
ototo managed writes what to push with MDM, Ansible or a golden image: Claude Code's managed settings and MCP registration, Otōto's settings and trusted keys.
Usage, savings and the fleet
Counts, never code, over OpenTelemetry beside Claude Code's own, to a hub of collector, Prometheus and Grafana: usage and savings per person and team, and the versions and servers running now.
Plugins you trust
Only plugins signed by keys you trust load. An organisation can ship its own plugins and signers, and a forge plugin reaches only the hosts granted to it.
A security review
What runs, what it reads and writes, what leaves the machine, written down. Releases are signed and carry a CycloneDX SBOM; ototo update can read from your own mirror.
06Numbers
What it saved, measured.
31%
less Claude spend on our benchmark, every answer still rightnine coding tasks we wrote, three runs each
58%
less than Claude Code's own Explore agent on 27 real bug fixes, as many fixedfour Java libraries, judged by each library's own tests
17%
less than Claude Code alone on the same fixessame tasks, same model, three runs each
Claude Code on Claude Opus 5.5. Your numbers will differ with your code, your questions and your model server.
For macOS on Apple silicon and Linux (x86_64, arm64). It downloads the newest release, checks it against Otōto's release key, and runs its installer, which finds your model server and sets up Claude Code and OpenCode, asking first. Name the server with sh -s -- --base-url http://gpu:8000/v1. Rather see it first? Read the script, then copy it or save it and run it yourself.
ototo doctor
Checks the settings, the model servers (with a tool-call test), Claude Code and OpenCode, and the plugins.
ototo update
When there is a newer release: checked the same way before anything changes. Nothing updates by itself.
08What's new
In the latest release.
2026.18 beta
search finds a line's start and end again. A pattern anchored with ^ or $ (^mod , ;$) was matched against the whole file, so it found a line only when it was the file's first or last, and mostly came back empty. Each line is now matched by itself, as grep does.
A document's links are addresses to read next. After a read of Markdown, the links of the lines shown are listed as read takes them: [setup](../install.md#linux) as docs/install.md#linux, a link to lines of code as src/main.rs:10-20, a wiki link as its page's file. One that leads nowhere is said to, with its line. outline of a document ends with where it is linked from. No model is involved, and the small model's reads are the same ones, so a question answered two pages apart follows the links.
When no model server answers, you are told at once, and what still works.ask, locate, callers and edit used to wait on each server in turn, every time. Now the first failure is remembered for half a minute, the next calls are answered at once, and the reply says which servers were tried and that search, read and outline need none. To have those four tools taken off Claude's list until a server is back, set hide_model_tools = true and run ototo init: it changes the prompt hook in settings.json, and is off unless you set it.
ototo update looks after your plugins. Plugins are published between releases, so yours may be newer than a release's: an update no longer puts an older one over it, and a plugin you switched off stays off. After its own answer, ototo update says when the channel has later builds of your plugins and offers to install them, asking first (--yes answers beforehand; --check only says).
Two plugins.archive 0.2.0 shows a class's annotations, as source writes them, on the class, its fields, methods and parameters: @GetMapping("/shelves/{id}"), @Nullable String name. codeowners is new: read CODEOWNERS#src/api/server.rs answers who owns a path, by the rule that decides it, as GitHub or GitLab reads the file. Both come with this release; on 2026.17, ototo plugins update and ototo plugins add codeowners.
The dashboard pages Recent calls (10, 25, 50 or 100 rows), keeps the Plugins table's words whole in a narrow window, and has icons (Tabler's, credited at the foot). It says how many questions were told their budget was nearly spent and how many of those then finished, and a call's detail names the Otōto that made it.
ototo doctor says which Claude Code sessions and which dashboard are still running the Otōto from before the last update: each keeps the one it started with until it is started again. For organisations, min_version is the oldest Otōto they accept, and doctor fails on an older one.
Writing a plugin:docs/plugins.md in the source takes one from an empty directory to a loaded, signed plugin.
Upgrading from 2026.17: ototo update. It changes nothing in settings.json or CLAUDE.md.
Otōto's source, its plugins and the kit for writing your own are public under MIT or Apache-2.0, your choice, on GitLab. Found a bug, or missing something? Open an issue there.
Releases are signed, and since 2.10 a download can be checked against the source: sh dist/reproduce.sh rebuilds the commit a release was built from and compares the binary and every plugin, byte for byte.