The MCP Tool Surface
OpenKnowledge exposes its knowledge base to agents as a stdio MCP server with 17 tools. The plugin you "install" is a thin manifest; the real surface is server-side.
1. The plugin is a manifest, the server is the substance
packages/plugin is almost empty — its public surface is a Claude Code plugin
descriptor:
// packages/plugin/.claude-plugin/plugin.json
{ "name": "open-knowledge", "version": "0.1.0",
"description": "CRDT knowledge base with real-time collaboration and AI agent tools" }It exports no code. The actual tool implementations live in
packages/server/src/mcp/tools/, registered by registerAllTools(server, opts)
(packages/server/src/mcp/tools/index.ts:33), and the agent reaches them through
the stdio server booted by packages/cli/src/mcp/server.ts:145-260. The split is
deliberate: the plugin/skill tells the agent what exists; the server decides
what it does, so the contract can change server-side without reinstalling
anything.
2. The 17 tools
Confirmed against the registry test (packages/server/src/mcp/tools/registry.test.ts:11-29):
| # | Tool | Purpose |
|---|---|---|
| 1 | exec |
run read-only shell (ls, cat, grep, bash) over the KB |
| 2 | search |
ranked full-text + semantic search (needs the collab server) |
| 3 | history |
version history + snapshot listing |
| 4 | links |
link-graph ops — orphans, hubs, dead links, suggestions |
| 5 | config |
read project config + workspace metadata |
| 6 | palette |
folder structure + template discovery |
| 7 | preview_url |
route-only preview URL for a doc |
| 8 | share_link |
shareable public link (needs the server) |
| 9 | write |
create/overwrite docs, folders, templates, assets — via the CRDT layer |
| 10 | edit |
patch a doc (find/replace, frontmatter merge) |
| 11 | delete |
remove docs/folders/assets |
| 12 | move |
rename/relocate |
| 13 | checkpoint |
save a version with a user-facing summary |
| 14 | restore_version |
revert to a prior checkpoint |
| 15 | conflicts |
list CRDT/sync merge conflicts |
| 16 | resolve_conflict |
resolve (merge/keep strategies) |
| 17 | workflow |
dispatcher for discover / ingest / research / consolidate (see 04) |
Note what's not here: there is no "ask the LLM" tool. OK never calls a model on
the agent's behalf — the agent is the model. OK's tools are all deterministic
operations on the knowledge base. The one place an LLM is implied is search,
which uses embeddings, but the agent calling search is a different model than
the one producing the embeddings (09_agentic_search.md).
3. Schemas: Zod, with a structured-output workaround
Every tool declares its inputs as a Zod schema and is registered with
server.registerTool(name, { description, inputSchema }, handler):
// pattern from packages/server/src/mcp/tools/*.ts
const InputSchema = {
query: z.string().describe('Search query — title, path, or body terms.'),
intent: z.enum(['omnibar', 'full_text']).optional(),
limit: z.number().int().min(1).max(100).optional(),
} as const;Results come back in one of two shapes (shared.ts:99-132):
- text-only —
textResult(text)→{ content: [{ type:'text', text }] }, used by the instructionalworkflowtools. - text + structured —
textPlusStructured(text, data)→ addsstructuredContent, used by data tools likesearch/links.
A subtle detail: outputSchemaWithText() (shared.ts:113-120) folds a text
field into the structured output, a workaround for Claude Desktop hiding
structuredContent when present. Worth noting if you build MCP tools — clients
disagree about how they render structured results, so OK ships the human-readable
text both ways.
4. Per-call project routing
One MCP server, many possible workspaces. OK resolves which project a tool call
targets in a three-step fallback (packages/cli/src/mcp/server.ts:219-225):
- an explicit
cwdargument on the tool call, else - a sticky project remembered from the previous call, else
- the MCP client's single advertised root (with an ambiguity warning if the client advertises several — e.g. a multi-worktree repo).
This lets an agent start calling tools without pre-selecting a workspace, and lets one server serve a session that roams across projects. The sticky-anchor pattern is a small, reusable idea: make context optional-with-memory rather than required-up-front.
5. The exec tool is the escape hatch
exec runs read-only shell over the KB. It is what makes OK usable even in a
skill-only install (no MCP): the discovery skill tells the agent to reach the
knowledge base with plain ls/cat/grep if the MCP tools aren't present
(05_skills.md). So the tool surface degrades gracefully — full fidelity over
MCP, basic fidelity over the shell the agent already has.
What's worth stealing
- Tools are deterministic operations; the agent brings the intelligence. A
clean separation that makes the tool layer testable (
registry.test.ts) and model-agnostic. - Sticky per-call routing. Optional
cwd+ remembered anchor + client-root fallback is a tidy way to serve many workspaces from one server. - Ship the text both ways. MCP clients render structured output inconsistently; folding human-readable text into the structured payload sidesteps it.
- A shell escape hatch.
execmeans your integration still works when the rich tools aren't loaded — valuable for a meta-harness that can't assume every worker mounted your server. - Thin manifest, fat server. Keep the installed surface declarative so you can evolve behavior without asking users to reinstall.