# MCP tools reference

> Seventeen MCP tools (`exec`, `search`, `links`, `write`, `edit`, `delete`, `move`, `history`, `checkpoint`, `workflow`, and others), input nesting conventions, preview envelopes, and conflict guards.

- Repository: sashimikun/open-knowledge
- GitHub: https://github.com/sashimikun/open-knowledge
- Human docs: https://grok-wiki.com/public/docs/sashimikun-open-knowledge-5c45105c876e
- Complete Markdown: https://grok-wiki.com/public/docs/sashimikun-open-knowledge-5c45105c876e/llms-full.txt

## Source Files

- `packages/server/src/mcp/tools/index.ts`
- `packages/server/src/mcp/tools/verb-schemas.ts`
- `packages/server/src/mcp/tools/write.ts`
- `packages/server/src/mcp/tools/edit.ts`
- `packages/server/src/mcp/tools/workflow.ts`
- `packages/server/src/mcp/tools/palette.ts`
- `docs/content/reference/mcp.mdx`

---

---
title: "MCP tools reference"
description: "Seventeen MCP tools (`exec`, `search`, `links`, `write`, `edit`, `delete`, `move`, `history`, `checkpoint`, `workflow`, and others), input nesting conventions, preview envelopes, and conflict guards."
---

The Open Knowledge MCP server registers **17 tools** in `packages/server/src/mcp/tools/index.ts`. Agents call them over MCP after `ok init` wires the server into Claude Code, Cursor, or Codex. Two reads work without the Hocuspocus collaboration server (`exec`, `preview_url`); `config` and `palette` also operate on disk alone. Every other read and all writes route through the server started by `ok start`.

```mermaid
flowchart LR
  subgraph agent["Agent host"]
    MCP["MCP client"]
  end
  subgraph ok["Open Knowledge"]
    MCPServer["MCP server\npackages/server"]
    Hocus["Hocuspocus server\nok start"]
    Disk["Content dir + .ok/"]
    UILock["ui.lock"]
  end
  MCP --> MCPServer
  MCPServer -->|"exec, config, palette"| Disk
  MCPServer -->|"preview_url"| UILock
  MCPServer -->|"search, links, write, …"| Hocus
  Hocus --> Disk
```

<Note>
Every tool accepts an optional `cwd` — an absolute host path to the project root. Required when the MCP server is registered globally (`npx @inkeep/open-knowledge mcp`); optional when anchored to a single project. See [Wire agent editors](/wire-agent-editors).
</Note>

## Tool catalog

| Tool | Server | Role |
| --- | --- | --- |
| `exec` | No | Read-only shell over the content directory (`cat`, `ls`, `grep`, `find`, `head`, `tail`, `wc`, `sort`, `uniq`, `cut`) with per-file enrichment |
| `search` | Yes | Ranked workspace search (title boost + BM25 + recency; optional semantic fusion) |
| `links` | Yes | Wiki-link graph (`backlinks`, `forward`, `dead`, `orphans`, `hubs`, `suggest`) |
| `history` | Yes | Document version timeline or folder activity timeline |
| `config` | No | Read effective merged config (full tree or dotted `key`) |
| `palette` | No | Authoring palette: markdown-native forms, `html preview` starters, theme tokens |
| `preview_url` | No* | Resolve a browser-openable preview URL from `ui.lock` |
| `share_link` | Yes | Build a GitHub-substrate share URL for a doc or folder |
| `write` | Yes | Create or overwrite `document`, `folder`, `template`, or `asset`; batch via `documents` |
| `edit` | Yes | Body find/replace or frontmatter merge-patch on `document`, `folder`, or `template` |
| `delete` | Yes | Delete `document` (one path or array), `folder`, `template`, or `asset` |
| `move` | Yes | Move/rename `document`, `folder`, `asset`, or `template`; rewrites affected links |
| `checkpoint` | Yes | Project-wide version snapshot; returns a 40-char `version` SHA |
| `restore_version` | Yes | Restore one document to a historical `version` |
| `conflicts` | Yes | List or inspect GitHub-sync merge conflicts |
| `resolve_conflict` | Yes | Resolve a conflict (`mine` / `theirs` / `content` / `delete`) and commit |
| `workflow` | No | Procedural guides (`ingest`, `research`, `consolidate`, `discover`, `wiki`) |

\* `preview_url` reads `ui.lock` directly but may auto-start the OK server when none is running.

<Warning>
When the Hocuspocus server is down, server-routed tools return: `Error: Hocuspocus server is not running. Start it with ok start, then retry.` Use `exec("grep …")` as the server-free search fallback.
</Warning>

## Input nesting conventions

Polymorphic write verbs (`write`, `edit`, `delete`) require **exactly one target key** per call. Nest the target's fields under that key — never flatten them to the top level.

<RequestExample>

```json
write({ "document": { "path": "meetings/standup", "content": "# Standup\n..." } })
```

</RequestExample>

<RequestExample>

```json
edit({ "document": { "path": "meetings/standup", "find": "TODO", "replace": "DONE" } })
```

</RequestExample>

Violations return a teaching error from `exactlyOneTargetError`:

- **Zero targets** — `Name exactly one of document, folder, … — the one thing you are addressing.`
- **Multiple targets** — `You named document and folder — name exactly ONE target.`

`move` is the exception: flat `from` / `to` paths (or nested `template: { from, to }`), with auto-detection of document vs folder vs asset. `conflicts`, `links`, and `workflow` dispatch on `kind` instead of a target key.

### Write targets

| Target | Tools | Notes |
| --- | --- | --- |
| `document` | `write`, `edit`, `delete` | Extension-less path (`meetings/standup`). `delete` accepts a string, array, or `{ path }`. |
| `folder` | `write`, `edit`, `delete` | `write` creates; `edit` patches `.ok/frontmatter.yml`. |
| `template` | `write`, `edit`, `delete` | Path = `<folder>/<name>` (letters, digits, `_`, `-` only). Stored at `<folder>/.ok/templates/<name>.md`. |
| `asset` | `write`, `delete` | Path includes extension (`images/diagram.png`). Upload via base64 `content` or local `source`. |
| `documents` | `write` only | Batch array; mutually exclusive with single targets. Each entry may carry its own `summary`. |

### Frontmatter merge-patch

`FrontmatterArg` is a JSON Merge Patch: set a key to update it, pass `null` to delete, omit keys to leave unchanged. A nested object **replaces** the subtree at that key.

<ParamField body="frontmatter" type="object">
Merge-patch map for document, folder, or template metadata. Use with `edit({ document: { path, frontmatter } })` for existing docs; `write` with `position: "replace"` for full rewrites.
</ParamField>

### Position and template instantiation

<ParamField body="position" type="enum" default="replace (new docs only)">
`replace` overwrites the whole body (only mode that persists frontmatter). `append` / `prepend` add content; frontmatter blocks in the payload are ignored on those modes.
</ParamField>

<ParamField body="template" type="string">
On `write({ document: { path, template } })`, resolves against the parent folder's template cascade (leaf-to-root walk). Mutually exclusive with `content`; forces `position: "replace"`. Only `{{date}}` and `{{user}}` substitution tokens are allowed.
</ParamField>

## Output shape and preview envelope

Responses mirror input nesting: `write({ folder })` returns `{ folder: { ok, path } }`; `edit({ document })` returns `{ document: { … } }`; a batch returns `{ documents: [ … ] }`.

Every tool that touches a previewable surface shares a **uniform preview envelope** at the top level of `structuredContent`:

<ResponseField name="previewUrl" type="string | null">
Route-only URL (`/#/<doc>`, no host:port). Identifies which doc to preview — not openable as-is.
</ResponseField>

<ResponseField name="previewUrlSource" type="string">
Provenance of `previewUrl` (currently `lock` from `ui.lock`).
</ResponseField>

<ResponseField name="warning" type="object">
Present on write tools when no browser is attached. Contains `action` (`attach-preview-once` or `start-ui`), `message`, `previewUrl`, and `autoOpen`.
</ResponseField>

Call `preview_url` to resolve the full browser-reachable URL (`http://localhost:<port>/#/<doc>`). `preview_url` also returns top-level `autoOpen`, reflecting `appearance.preview.autoOpen`.

<Info>
`autoOpen: true` (default) — agents may navigate the preview using host capabilities. `autoOpen: false` — the user manages their own preview window; surface the URL only on request.
</Info>

### Advisory warnings

`write` and `edit` may attach `structuredContent.document.warnings` — discriminated by `kind`:

| Kind | Meaning |
| --- | --- |
| `content-divergence` | Converged Y.Text did not byte-match composed content — re-read with `exec("cat <path>")` |
| `disk-edit-reconciled` | An out-of-band disk edit was folded in before your write landed |
| `mermaid-parse-error` | Write succeeded but a Mermaid fence will not render — fix and re-edit |

## Read tools

### `exec`

Runs one allowlisted command (or pipe) against the project content directory. Not a full shell — `&&`, `;`, redirections, and writes are rejected.

<ParamField body="command" type="string" required>
Bash-like read command. Examples: `cat articles/auth.md`, `grep -rn oauth articles/ | head -5`.
</ParamField>

Returns raw stdout plus `enrichedPaths` per referenced wiki file: frontmatter, backlink/forward-link counts, shadow-repo history, and route-only `previewUrl`. Soft cap: 500 lines / ~64 KB per response copy.

<Warning>
When the project has `.ok/`, prefer `exec` over native editor `Read`/`Grep`/`Glob` for `.md`/`.mdx` — native tools skip enrichment the MCP layer provides.
</Warning>

### `search`

<ParamField body="query" type="string" required>
Free-form query tokenized across title, path, and (with `full_text`) body.
</ParamField>

<ParamField body="intent" type="enum" default="full_text">
`omnibar` — title/path/folders only. `full_text` — includes markdown body.
</ParamField>

<ParamField body="semantic" type="boolean">
Set `false` to force pure-lexical ranking even when semantic search is enabled. Omit to use semantic when available (content egress to embeddings provider).
</ParamField>

<ParamField body="limit" type="number" default="20">
Max rows; maximum 100.
</ParamField>

Each result row carries `kind` (`page` | `folder` | `file`), `score`, `signals` breakdown, optional `snippet`, and route-only `previewUrl`. Cold start may return `ready: false` with empty results — retry after 2–3 seconds.

### `links`

<ParamField body="kind" type="string | string[]" required>
One of `backlinks`, `forward`, `dead`, `orphans`, `hubs`, `suggest` — or an array to fetch several views in one call. Results nest under each kind's key.
</ParamField>

<ParamField body="document" type="string">
Required for `backlinks`, `forward`, and `suggest`.
</ParamField>

Passing multiple kinds merges into a single payload; per-kind failures land in an `errors` map.

### `history`

Pass exactly one of `document` or `folder`.

<ParamField body="document" type="string">
Extension-less doc path. Each entry's `version` is a 40-char SHA for `restore_version`.
</ParamField>

<ParamField body="folder" type="string">
Folder timeline over `.ok/` artifacts (templates + frontmatter). Use `""` for project root.
</ParamField>

Entry kinds: `checkpoint`, `wip`, `upstream`. Optional filters: `branch`, `limit` (max 200), `offset`, `kind`, `author`, `excludeAuthor`.

### `config`

<ParamField body="key" type="string">
Dotted config key (e.g. `appearance.theme`). Omit for the full merged config (defaults → user → project).
</ParamField>

Returns `{ value, exists, key? }`. Removed keys (`server.*`, `mcp.*`, `github.*`) return `exists: false`.

### `palette`

Returns three sections: `components` (markdown-native authoring forms), `embedPatterns` (themed `html preview` starters), and `tokens` (CSS custom properties for preview iframes).

<ParamField body="components" type="string[]">
Optional canonical ids (max 32, case-sensitive) to expand full JSX prop schemas into `componentDetails`. Unmatched ids appear in `notFound`.
</ParamField>

### `preview_url`

<ParamField body="document" type="string">
Extension-less doc path. Mutually exclusive with `folder`.
</ParamField>

<ParamField body="folder" type="string">
Folder route (`…/#/<folder>/`). Mutually exclusive with `document`.
</ParamField>

<ParamField body="armPaneTarget" type="boolean">
When true with a target, arms a TTL-bounded (~30s) pane target under `.ok/local/` for Claude Code Desktop `preview_start`.
</ParamField>

<ResponseField name="url" type="string | null">
Full browser-reachable URL, or `null` when no UI is running.
</ResponseField>

<ResponseField name="baseUrl" type="string | null">
UI origin (e.g. `http://localhost:5173`).
</ResponseField>

<ResponseField name="running" type="boolean">
Whether a UI is bound for the project.
</ResponseField>

### `share_link`

Builds a read-only `https://openknowledge.ai/d/...` URL pinned to the current branch. Never publishes or pushes.

<ParamField body="path" type="string" required>
Content-dir-relative target. Extension-less for docs; directory path for folders. `""` is the content-root sentinel (requires `kind: "folder"`).
</ParamField>

<ParamField body="kind" type="enum">
`doc` or `folder`. Auto-probed from disk when omitted (`.mdx` → `.md` → directory).
</ParamField>

Preconditions: named branch, `github.com` origin, branch pushed to origin.

## Write tools

### `write`

Four native CRUD targets plus batch:

<CodeGroup>

```json title="Create a document"
{ "document": { "path": "meetings/standup", "content": "# Standup\nNotes here." } }
```

```json title="Instantiate from template"
{ "document": { "path": "fishing-log/2026-06-25", "template": "trip-log" } }
```

```json title="Batch write"
{ "documents": [
    { "path": "a", "content": "# A" },
    { "path": "b", "content": "# B", "summary": "Added section B" }
  ]}
```

</CodeGroup>

Existing docs require explicit `position`. Creating a doc with empty content errors. `extension` (`.md` | `.mdx`) applies only on create.

### `edit`

Body edit and frontmatter patch are **mutually exclusive** per call.

<ParamField body="find" type="string">
Exact body text to find. Requires `replace`. Frontmatter-intersecting finds are rejected.
</ParamField>

<ParamField body="occurrence" type="number" default="1">
Which match to edit (1 = first).
</ParamField>

Folders accept frontmatter only (no body). Templates use the same body/frontmatter split with path `<folder>/<name>`.

### `delete`

Irreversible. Call `links({ kind: "backlinks", document })` before deleting docs to see inbound references. Inbound links become redlinks.

### `move`

<ParamField body="from" type="string" required>
Current path. Doc: extension-less. Folder: relative path. Asset: path with extension.
</ParamField>

<ParamField body="to" type="string" required>
New path, same shape as `from`.
</ParamField>

Rewrites wiki-links and supported inline Markdown links across affected docs. Returns `previewUrls` map and `previousPreviewUrl` for deleted paths. Errors: 400 (invalid/excluded), 404 (missing), 409 (destination exists, with `colliding[]`).

### Edit summaries

`write`, `edit`, `move`, and `checkpoint` accept an optional `summary` (≤80 chars, transport cap 200). Summaries appear on the document timeline and persist to git history. Avoid secrets and PII.

## Version tools

### `checkpoint`

Saves a project-wide restore point via `POST /api/save-version`.

<ResponseField name="version" type="string">
40-character commit SHA. Same field name used by `history` entries and `restore_version`.
</ResponseField>

`previewUrl` is always `null` — checkpoints are not scoped to one doc.

### `restore_version`

<ParamField body="document" type="string" required>
Document to restore (extension-less path).
</ParamField>

<ParamField body="version" type="string" required>
40-char SHA from `history` or `checkpoint`.
</ParamField>

Append-only: creates a new version with old content. May return `warnings` with kind `content-divergence` when restored Y.Text does not byte-match target bytes.

## Conflict guards

GitHub-sync merge conflicts set `lifecycle.status = "conflict"` on affected documents. **All mutating operations refuse conflicted docs** with HTTP 409 (`urn:ok:error:doc-in-conflict`):

- `write`, `edit` (`/api/agent-write-md`, `/api/agent-patch`)
- `delete` (`/api/delete-path`)
- `move` (`/api/rename-path`)
- `restore_version`
- Agent undo

The error detail instructs: call `conflicts({ kind: "content" })` + `resolve_conflict` before retrying.

<Steps>
<Step title="Detect conflicts">

```json
conflicts({ "kind": "list" })
```

Returns `{ list: [{ file, detectedAt, … }] }` — empty when none.

</Step>
<Step title="Inspect merge stages">

```json
conflicts({ "kind": "content", "file": "notes/sso.md" })
```

Returns `{ content: { file, base, ours, theirs, shape, lifecycleStatus } }`. `file` must include the `.md`/`.mdx` extension. `shape` is `both-modified`, `delete-modify`, or `modify-delete`.

</Step>
<Step title="Resolve and commit">

```json
resolve_conflict({ "file": "notes/sso.md", "strategy": "content", "content": "…merged bytes…" })
```

Strategies: `mine` (`git checkout --ours`), `theirs` (`git checkout --theirs`), `content` (exact bytes), `delete` (`git rm`).

</Step>
</Steps>

<Warning>
`resolve_conflict` modifies the working tree and creates a git commit. Re-call `conflicts({ kind: "list" })` after a 500 to confirm post-state — the resolve API is best-effort and non-atomic.
</Warning>

## `workflow`

Returns numbered procedural plans (instructional text, not data). Dispatches on `kind`:

| Kind | Required param | Purpose |
| --- | --- | --- |
| `ingest` | `source` | Capture external source verbatim into the KB |
| `research` | `topic` | Gather sources and write provisional findings |
| `consolidate` | `topic` | Promote research to a canonical article |
| `discover` | — | Extract conventions from an existing repo |
| `wiki` | — | Generate a source-grounded codebase wiki into `wiki/` |

`previewUrl` is always `null` — workflow output is prose, not a previewable document.

## Structured output channel

All tools return MCP `content[0].text` plus optional `structuredContent`. `textPlusStructured` mirrors the visible body into `structuredContent.text` as a Claude Desktop client workaround. Programmatic consumers should prefer `content[0].text` for the human-readable channel and `structuredContent` for typed fields.

## Related pages

<CardGroup>
<Card title="Collaboration server" href="/collaboration-server">
Hocuspocus lifecycle, server-free vs server-routed tools, and protocol boundaries.
</Card>
<Card title="Wire agent editors" href="/wire-agent-editors">
MCP registration, bundled skills, and `exec` verification prompts.
</Card>
<Card title="GitHub sync and conflicts" href="/github-sync-and-conflicts">
Clone, auto-sync, and end-to-end conflict resolution workflows.
</Card>
<Card title="Semantic search setup" href="/semantic-search-setup">
Enable embeddings-based `search` ranking and content-egress constraints.
</Card>
<Card title="Folders and templates" href="/folders-and-templates">
Template cascade and `write({ document: { template } })` semantics.
</Card>
<Card title="Configuration reference" href="/configuration-reference">
YAML config keys including `appearance.preview.autoOpen`.
</Card>
<Card title="HTTP API reference" href="/http-api-reference">
Server routes (`/api/agent-write-md`, `/api/search`, `/api/sync/*`) behind MCP tools.
</Card>
<Card title="Karpathy LLM wiki" href="/karpathy-llm-wiki">
Three-layer workflow using `workflow` kinds `ingest`, `research`, and `consolidate`.
</Card>
</CardGroup>
