omgbase
The omgbase binary: a versioned, addressable graph layer over authored
Markdown, served to agents as an MCP tool catalog. Rust, over
omgbase-surface.
omgbase mcp [--workspace DIR] [--repo SLUG] [--no-watch] serve the tool catalog over MCP stdio
omgbase --version
omgbase mcp locates the workspace (--workspace, $OMGBASE_WORKSPACE, or
the nearest .omgbase/ walking up from the current directory —
spec/sync §1), selects the default repo (--repo, the only repo, or the
one whose root contains the current directory), wires the embedding
provider named by the repo's embedding.* settings when it can be spawned
(otherwise semantic() fails semantic_unavailable), and speaks
newline-delimited JSON-RPC 2.0 on stdin/stdout: initialize, tools/list,
tools/call, ping. Every tool result is one text content item holding
JSON; an error result carries isError: true and the envelope
{ error, message, data?, retriable } (spec/surface §4). stdout is the
protocol channel; every diagnostic goes to stderr.
Thin by design: there is no business logic here beyond argument parsing,
the transport loop and the two long-lived helpers below, which mirror the
reference omg mcp.
The watcher
Unless --no-watch is given, the server tries to take the workspace's
watch lease (.omgbase/watch.lock, spec/sync §7). When another live
watcher holds it (omg watch, another mcp) it serves without one and says
so (watcher elsewhere). Otherwise, for a repo with an fs root, it runs
one priming freshness sweep (§4.3) so the session starts fresh, spawns the
repo's registered adapter (the fs source's `omgbase-fs-adapter --root
The adapter must be installed. The registry's command is the
omgbase-fs-adapter bin of @omgbase/fs-adapter (npm i -g @omgbase/fs-adapter, or any way that puts it on PATH). OMGBASE_FS_ADAPTER
overrides the command line — e.g. node /path/to/omgbase/packages/fs-adapter/dist/src/bin.js from a checkout after
pnpm build. When the adapter cannot be started (not found, bad handshake,
no watch capability) the server degrades to no watch (adapter unavailable) with a clear stderr line, releases the lease and serves; the
priming sweep has already run by then.
The MCP loop is synchronous on stdin and owns the surface's store; the
watch stream arrives on another thread that owns its own store
connection over the same SQLite file (WAL) and takes the workspace writer
lock (.omgbase/writer.lock) around each checkpoint — the same lock the
loop takes around every tool that can commit (Surface::is_write_tool), so a
checkpoint and a write never interleave inside one commit, in this process
or across processes. Every connection carries a 5 s busy timeout for the
read-over-commit overlaps SQLite serializes itself.
The embed drain
When a provider is configured and live, a background drainer keeps
embeddings timely (spec/search §2.6): every successful non-dry-run write
tool call and every watcher checkpoint that ingested or deleted something
schedules a drain; the drainer thread debounces 500 ms, runs one drain at a
time (blocks first, then documents — build_embed_tasks → embed_process
→ build_doc_embed_tasks → embed_process_docs), re-runs once more when a
schedule arrived mid-drain, and logs [mcp] embedded N block(s). Errors go
to stderr and are never fatal (the next schedule retries). The thread owns
its own store connection and its own provider instance (a second spawn
of the configured command); the main thread's provider serves semantic()
queries only. With no provider configured there is no thread.
Shutdown
stdin EOF, SIGINT or SIGTERM: stop the watcher (unwatch, close the
adapter, join the thread), release the lease, flush the drainer (a final
drain if anything is pending) and close it, then drop the surface (which
kills its provider process), exit 0.
Conformance seams and the interop harness
Two environment variables are the test seams of spec/surface §7.1, for
cross-engine conformance runs only (never set them in production):
OMGBASE_SPEC_MINTER=sequentialinstalls the fixture id minter (d_0, d_1, …, each prefix counting from 0, fresh at process start and shared by every thread of the process) for the workspace and the store; any other value is a startup error.OMGBASE_SPEC_CLOCK=<RFC 3339>makes that instant "now" for every commit a tool or the watcher stamps (stored as UTC milliseconds,…Z; a±HH:MMoffset is converted).
Both are announced on stderr. tests/interop.rs is the §7 harness: every
case of spec/surface/cases/interop.json for every (writer, reader) in
{typescript, rust}², both engines as child processes over MCP stdio, both
with --no-watch (the seed is the harness's observe_many, not a priming
sweep). The TypeScript peer is $OMGBASE_TS_MCP or node <repo>/packages/cli/dist/src/main.js (pnpm build); OMGBASE_INTEROP=skip
skips the pairs that need it. tests/mcp_watch.rs drives the binary as a
process: --no-watch, the adapter-unavailable degradation, the lease, a
SIGTERM shutdown, and — when node and packages/fs-adapter/dist exist
— a watched run where a file written under the workspace reads back through
the server.
License
MIT.