OMGDB ships a minimal Model Context Protocol server that exposes the engine directly to LLM coding agents. Started with omgdb mcp, it speaks newline-delimited JSON-RPC 2.0 over stdin/stdout and advertises the database as a set of agent-callable tools built on the engine’s own JSON codec.
The defining feature is its capability-scope model. The server runs at a single ceiling — read, read-write, or dangerous — and a tool is only callable if its required scope is within that ceiling. Enforcement happens in two layers: tools above the ceiling are never advertised in tools/list, and every tools/call re-checks the ceiling before executing. As a result, an untrusted agent can be handed a read-only database it is structurally unable to mutate.
Running the server
omgdb mcp
This starts a stdio server at the default read-write scope. The process reads JSON-RPC requests one per line from stdin and writes one JSON response per line to stdout, flushing after each. It runs until stdin closes (EOF). Blank lines, lines that do not parse as a JSON object, and objects without a string method are silently ignored.
To restrict the server to read-only tools:
omgdb mcp --scope read
| Flag | Description |
|---|---|
--scope | Capability ceiling enforced on every tool call. read (alias ro), read-write (default; aliases write, rw), or dangerous (alias all). An unrecognized value aborts with invalid --scope '<x>' (use read, read-write, or dangerous). |
Note: The server is stateless across calls. Every tool that touches data is passed a
pathargument and reopens the store withStore::openfor that call; no connection or session handle is held between requests.
Capability scopes
There are three ordered scopes. The ordering is read < read-write < dangerous, and a tool is allowed if and only if its required scope is less than or equal to the server’s scope.
| Scope | Aliases | Tools permitted |
|---|---|---|
read | ro | The 7 read-only tools (find, aggregate, describe, explain, diagnose, vsearch, context_pack) |
read-write | write, rw | All 11 tools (the 7 read tools plus insert, plan_update, apply, rollback) |
dangerous | all | Reserved for tools marked destructive (currently none — see below) |
Two-layer enforcement
The capability ceiling is enforced at both discovery and execution time:
-
Discovery.
tools/listfilters the full 11-tool catalog down to only those whose required scope is within the server scope. Areadserver returns 7 tools; aread-writeserver returns all 11. A read-only agent never even sees the mutating tools. -
Execution.
tools/callre-checks the ceiling before doing any work. A disallowed call returns a successful JSON-RPC result whose content is a text block withisErrorset totrueand a message like:Error: tool `insert` requires `read-write` scope but this server runs with `read` scope
Because the check is repeated at call time, hiding a tool from tools/list is not the only line of defense — an agent that guesses a tool name it was never shown is still refused.
Limitation: No tool currently requires the
dangerousscope. The scope resolver maps every unknown or unregistered tool name todangerous, but dispatch returns an error for any name outside the 11-tool set. In practice--scope dangerousadvertises and permits exactly the same 11 tools as--scope read-write; there is no destructive (drop/delete) tool implemented.
Tools
The server exposes 11 tools. All document, filter, pipeline, and update arguments are JSON values; path names the on-disk store.
| Tool | Required scope | Description |
|---|---|---|
find | read | Find documents matching a MongoDB-style filter (path, collection, optional filter); returns NDJSON. |
aggregate | read | Run an aggregation pipeline — an array of stages (path, collection, pipeline); returns NDJSON. |
describe | read | Get a Markdown manual of the database (path). |
explain | read | Explain how a query will run, index scan vs full scan (path, collection, filter). |
diagnose | read | Why-not debugger: per-predicate selectivity for a query (path, collection, filter). |
vsearch | read | Semantic search over a text field (path, collection, field, query; optional k, filter). |
context_pack | read | Build a token-budgeted, cited context bundle for a task over a text field (path, collection, field, query; optional budget, filter). |
insert | read-write | Insert a JSON document into a collection (path, collection, document); returns the new ObjectId. |
plan_update | read-write | Dry-run a $set/$unset/$inc update: returns a token plus a before/after sample, writes no data (path, collection, optional filter, update). |
apply | read-write | Apply a planned change by token, reversibly (path, token). |
rollback | read-write | Roll back a previously applied change by id (path, change_id). |
Argument schemas
Each tool’s inputSchema lists its declared properties under properties (each with a JSON type) and marks them required — with one exception: a property literally named filter is never listed in required. For find, explain, and diagnose this leaves filter out of the schema’s required array while path and collection remain required. Note that this is a schema-level optionality only: at execution time, just find defaults a missing filter to empty, whereas explain and diagnose require filter to be present at call time and return a missing argument \filter“ error if it is omitted.
vsearch accepts an optional k (default 5) and context_pack an optional budget (default 1000). Both accept an optional filter object for hybrid search and embed text with the engine’s default hashing embedder. See vector search and context packs for details.
The mutation safety workflow
The mutating tools implement the reversible change workflow described in agent mutations:
plan_updateis a dry run that writes nothing and returns a token plus a before/after sample.applycommits a planned change by its token.rollbackreverses a previously applied change by itschange_id.
All three require read-write scope. Plan tokens are produced and consumed by the underlying change engine, not held in MCP server memory, which keeps the server stateless.
JSON-RPC methods
| Method | Behavior |
|---|---|
initialize | Returns protocolVersion "2024-11-05", capabilities.tools, and serverInfo { "name": "omgdb", "version": <crate version> }. Client params are ignored; there is no version negotiation. |
tools/list | Returns the scope-filtered tool catalog. |
tools/call | Dispatches a tool by params.name with params.arguments. |
notifications/* | Any method beginning with notifications/ receives no reply, per JSON-RPC notification semantics. |
Error handling
Two kinds of failures are distinguished. Protocol-level problems are returned as JSON-RPC errors:
| Code | Message | Cause |
|---|---|---|
-32601 | method not found | Method is not initialize, tools/list, tools/call, or a notifications/* notification. |
-32602 | invalid params | params is not an object. |
-32602 | missing tool name | params.name is not a string. |
Tool-level failures — including a refused-by-scope call, a missing argument, an unknown tool name, or an engine error — are not JSON-RPC errors. They return a successful result envelope whose content is a text block with isError set to true.
Safety annotations
Every tool carries an annotations object so an MCP host can reason about tool safety:
| Annotation | Value |
|---|---|
readOnlyHint | true for the 7 read tools, false for the 4 mutating tools. |
idempotentHint | Same as readOnlyHint. |
destructiveHint | Always false, including for apply and rollback. |
requiredScope | The tool’s required scope as a string (read / read-write / dangerous). |
Note:
requiredScopeis an OMGDB-specific annotation, not part of the standard MCP annotation set (readOnlyHint/idempotentHint/destructiveHint/openWorldHint). Also note that becausedestructiveHintis hard-coded tofalse, hosts relying solely ondestructiveHintwill treat every OMGDB tool — evenapplyandrollback— as non-destructive. UserequiredScopeor run the server at--scope readto fence off mutations.
Example: initialize and list tools
Send these two requests on stdin (one JSON object per line):
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
The initialize response identifies the server:
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"omgdb","version":"..."}}}
At the default read-write scope, tools/list returns all 11 tools, each with an inputSchema and annotations. A single entry looks like:
{
"name": "find",
"description": "Find documents matching a MongoDB-style filter",
"inputSchema": {
"type": "object",
"properties": {
"path": {"type": "string"},
"collection": {"type": "string"},
"filter": {"type": "object"}
},
"required": ["path", "collection"]
},
"annotations": {
"readOnlyHint": true,
"idempotentHint": true,
"destructiveHint": false,
"requiredScope": "read"
}
}
Example: calling a tool
A tools/call for find against the store app.omgdb:
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"find","arguments":{"path":"app.omgdb","collection":"users","filter":{"age":{"$gte":21}}}}}
If the server were started with --scope read, a call to a write tool such as insert would return a result flagged as an error rather than performing the insert:
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"insert","arguments":{}}}
{"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"Error: tool `insert` requires `read-write` scope but this server runs with `read` scope"}],"isError":true}}
Related
- Agent mutations — the
plan_update/apply/rollbackchange workflow surfaced by the write tools. - CLI reference — the full
omgdbcommand surface, including the equivalent direct commands.