# MCP tools

> The two tools an agent sees — their inputs, the structured success result, the structured error, the server instructions, and the stdio transport.

Canonical URL: https://www.slivingdoc.dev/docs/reference/mcp-tools/ · Version: 0.1 · Updated: 2026-09-21

Full site index: https://www.slivingdoc.dev/llms.txt

The server registers exactly two tools. There is no third tool, no
prompt, and no resource.

| Tool           | Inputs                       | Success result |
| -------------- | ---------------------------- | -------------- |
| `notes_pull`   | `path` (optional)            | `OK`           |
| `notes_commit` | `message`, `path` (optional) | `OK`           |

The normal workflow is one call, then ordinary file editing, then the
other call:

```text
notes_pull()
        |
        v
edit UTF-8 text files in the notebook directory
        |
        v
notes_commit(message)
```

## Inputs

Tool inputs are strict JSON objects. `notes_pull` requires no field;
`notes_commit` requires only `message`. Both accept the optional `path`.
Unknown fields and explicit `null` values are invalid, and the advertised
schemas set `additionalProperties: false`.

| Field     | Tool           | Rules                                                                                                                                                                                                                                                                                                  |
| --------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `path`    | both           | Optional UTF-8 host path of the notebook directory, 1 to 4,096 bytes. Omitted or empty means the server's own notebook directory. May begin with `~/`, which expands to the current user's home directory before the absolute-path and byte checks. The result must be at or below the workspace root. |
| `message` | `notes_commit` | Non-blank UTF-8 without U+0000, at most 16,384 bytes. A message of only Unicode white space is rejected; any other message is preserved byte for byte. Retained in recent internal history only.                                                                                                       |

The byte bounds are enforced by the strict decode, not by the schema,
because JSON Schema `maxLength` counts code points rather than bytes.

A supplied `path` addresses one directory at or below the workspace root,
which is how one server can serve several notebooks. With no
`--workspace-root`, the server takes its own session directory and the
agent should simply omit `path`.

## Tool descriptions

Each tool carries a description the host shows to the model:

- **`notes_pull`** — "Write the current notebook into the notebook
  directory and record the accepted state. Omit path or pass an empty
  string to use the server's notebook directory, which the result
  reports. Edit UTF-8 text files (without U+0000) there between
  notes_pull and notes_commit; notes_commit publishes the changes and
  incorporates concurrent non-conflicting changes."
- **`notes_commit`** — "Publish the caller's changes in the notebook
  directory and incorporate concurrent non-conflicting changes. Omit path
  or pass an empty string to use the server's notebook directory, which
  the result reports. message must be non-blank UTF-8 without U+0000, at
  most 16,384 bytes; it is retained in recent internal history only."

When the process is configured with a writable or read-only set, one
sentence per configured set is appended to both descriptions, naming the
entries and the consequence. With both sets configured, the read-only
sentence ends with the rule that reconciles them rather than "write
elsewhere".

## Server instructions

The server also sends instructions for the session:

```text
The notebook directory is <path>. Call notes_pull, edit UTF-8 text files
(without U+0000) there, then call notes_commit to publish. Both tools
default to that directory; pass path only to address a subdirectory of it.
```

A configured writable set appends: "Writable paths: `<entries>`.
notes_commit refuses any change elsewhere, resets those files, and reports
READ_ONLY_PATH; write only under them." A configured read-only set
appends the matching sentence for its own entries, ending in "write
elsewhere." on its own, or in "where the two sets nest, the longest
matching entry decides." when a writable set is also configured.

The set is advertised on four surfaces — the instructions, both tool
descriptions, and the `readOnly` and `writable` arrays of every result —
so a host that drops one still leaves the others. A process with neither
set configured is byte-for-byte unchanged on every surface except the
always-present empty arrays.

## The success result

A successful call returns one MCP text item and one structured object.
The text item carries the resolved notebook directory, because many
clients forward only the text item to the model:

```text
/tmp/slivingdoc-7f3a1c/notebook
```

With path sets configured it becomes
`<path> (writable: <entries>)`, `<path> (read-only: <entries>)`, or, with
both, `<path> (writable: <entries>; read-only: <entries>; longest match decides)`.

The structured object is:

```json
{
  "code": "OK",
  "path": "/tmp/slivingdoc-7f3a1c/notebook",
  "generation": 18,
  "filesChanged": 3,
  "insertions": 3,
  "deletions": 4,
  "files": [
    { "path": "notes/a.md", "insertions": 1, "deletions": 1 },
    { "path": "notes/c.md", "insertions": 2, "deletions": 0 },
    { "path": "archive/old.md", "insertions": 0, "deletions": 3 }
  ],
  "readOnly": ["docs", "faq.md"],
  "writable": []
}
```

| Field          | Meaning                                                                                                              |
| -------------- | -------------------------------------------------------------------------------------------------------------------- |
| `code`         | Always `OK`.                                                                                                         |
| `path`         | The resolved notebook directory the operation ran against — the caller's own visible directory, never private state. |
| `generation`   | The accepted remote generation after the operation.                                                                  |
| `filesChanged` | The number of entries in `files`.                                                                                    |
| `insertions`   | Total inserted lines.                                                                                                |
| `deletions`    | Total deleted lines.                                                                                                 |
| `files`        | Per-file `path`, `insertions`, `deletions`. Always present, empty for a no-op synchronisation.                       |
| `readOnly`     | The normalised, sorted read-only set this server enforces. Always present, empty when none is configured.            |
| `writable`     | The normalised, sorted writable set. Always present, empty when none is configured. It never appears in `readOnly`.  |

The pull diffstat is the on-disk delta between the visible state before
the pull and the materialised result; the commit diffstat is the
increment the publication added over the observed remote parent tree.
Paths use the same normalised internal slash form as error files. No
success data contains credentials, S3 keys, private paths, or Git object
IDs.

## The structured error

A domain error returns a tool result with `isError=true`, one candid text
item, and this structured object:

```json
{
  "code": "CONTENT_CONFLICT",
  "reason": "MERGE_CONFLICT",
  "action": "EDIT_FILES",
  "diagnosticId": "0011223344556677",
  "retryable": false,
  "message": "Resolve the conflict blocks before notes_commit.",
  "files": [
    {
      "path": "notes/today.md",
      "reason": "TEXT_CONFLICT",
      "ranges": [{ "start": 12, "end": 18 }]
    }
  ],
  "readOnly": [],
  "writable": []
}
```

| Field          | Presence    | Meaning                                                                                                                                                                                                                                                   |
| -------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `code`         | always      | The stable error category.                                                                                                                                                                                                                                |
| `reason`       | always      | One stable token classifying the error one level below `code`.                                                                                                                                                                                            |
| `action`       | always      | One stable token naming the caller's next step.                                                                                                                                                                                                           |
| `diagnosticId` | always      | A fresh 16-character lowercase hexadecimal ID correlating the result with the two server log records for the call.                                                                                                                                        |
| `retryable`    | always      | Whether a retry of the same call can help.                                                                                                                                                                                                                |
| `message`      | always      | Human-readable text. It can change between releases.                                                                                                                                                                                                      |
| `detail`       | conditional | For `ENGINE_FAILED` only, and only when the engine classified the cause itself: a fixed description from a closed set. An unrecognised cause carries no `detail`, and the operator reads the full cause from the log record with the same `diagnosticId`. |
| `files`        | always      | The affected files. Empty array when the error is not about files.                                                                                                                                                                                        |
| `recovery`     | conditional | For `RECOVERY_FAILURE` only: `stage` (string), `remoteAccepted` (`yes`, `no`, or `unknown`), and `resynchronized` (boolean).                                                                                                                              |
| `readOnly`     | always      | The normalised read-only set, as on success.                                                                                                                                                                                                              |
| `writable`     | always      | The normalised writable set, as on success.                                                                                                                                                                                                               |

Each `files` entry carries `path`, its own `reason`, and `ranges`:

- The request `path` is absolute, and every `files[].path` is relative to
  it, in the normalised internal slash form.
- Ranges are one-based, inclusive, ordered, and non-overlapping. A file
  with no marker range carries an empty `ranges` array.

`code`, `reason`, `action`, and every `files[].reason` are stable across
releases; message text is not. Every domain error carries a non-empty
`reason` and `action`, and every file entry carries a non-empty `reason`,
so an agent can branch on the tokens instead of parsing prose and is
never left without a next step. [Errors](/docs/reference/errors/) is the
complete table.

The candid text item repeats the code, reason, message, optional detail,
affected files, action, retryable verdict, diagnostic ID, recovery
report, and both path sets, so a client that discards structured content
still keeps the whole safe diagnostic:

```text
CONTENT_CONFLICT · MERGE_CONFLICT
Resolve the conflict blocks before notes_commit.
file: notes/today.md · TEXT_CONFLICT · lines 12-18
action: EDIT_FILES
retryable: false
diagnosticId: 0011223344556677
```

> **Note:** A cancelled request is not a domain error. It stays a protocol
> error and never appears inside this envelope, and neither does the
> startup-only `INCOMPATIBLE_STORE` diagnostic.

No error text or data contains credentials, S3 keys, private paths, or
Git object IDs. Pack keys, probe keys, 40- and 64-character hexadecimal
identifiers, AWS access key IDs, and URL user information are all
scrubbed from diagnostic text as defence in depth.

## Transport

An MCP host starts the server as a local child process and speaks MCP
JSON-RPC over stdio. Stdout carries only protocol messages; logs go to
stderr. The host and the server share the visible directory, so agents
and humans edit files there and the server scans them at each call. The
npm package is the primary stdio installation path:

```json
{
  "command": "npx",
  "args": ["-y", "slivingdoc", "serve"]
}
```

The server advertises the implementation name `slivingdoc` and the
release version, and it logs one correlated pair of records per tool call
— a start record and a completion record, both carrying the `mcpReqID`
that becomes the result's `diagnosticId`.

## Next

- [Connect an MCP host](/docs/guides/mcp-hosts/) — registering the server
  in a specific host.
- [Errors](/docs/reference/errors/) — every code, reason, file reason, and
  action.
- [CLI](/docs/reference/cli/) — the same operations as subcommands.
