# Use a coding agent

Sponge is a local read-later library. Agents can use it two ways: the `sponge`
CLI with `--json`, or the `sponge mcp` server. Both work on the same local
library, and capture always happens on your own device. Claude Code, Codex,
OpenCode, and any agent that can execute commands or connect a local MCP
server can use these interfaces. No particular model or coding agent is required.

## Install and set up

```sh
npm install -g @hraness/sponge
sponge init                  # create the library in ~/.local/share/sponge
sponge list --json
```

Use Node.js 24.20 or newer. `sponge init` creates the local library. Read and
keyword search need no account or model download. Run `sponge setup --digest` when you
want local summaries. CLI semantic search needs `sponge setup --embedding`, on Apple Silicon Macs
with macOS 26; see [Requirements and platforms](/docs/requirements). `sponge doctor` shows what is
installed. Run `sponge login` and
`sponge sync` when you want sync, the web library, and the save queue.

`sponge login` opens the approval page in the default browser (or prints the link
over SSH, in CI and with `--no-browser`) and waits. In scripts, use
`sponge login --no-wait`, approve the link, then run `sponge login --finish`.

## Connect an MCP client

Claude Code:

```sh
claude mcp add --transport stdio sponge -- sponge mcp
```

Codex:

```sh
codex mcp add sponge -- sponge mcp
codex mcp list
```

You can also configure Codex in `~/.codex/config.toml` or a project’s
`.codex/config.toml`:

```toml
[mcp_servers.sponge]
command = "sponge"
args = ["mcp"]
```

OpenCode (`opencode.json`):

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "sponge": {
      "type": "local",
      "command": ["sponge", "mcp"],
      "enabled": true
    }
  }
}
```

Other clients:

```json
{ "mcpServers": { "sponge": { "command": "sponge", "args": ["mcp"] } } }
```

To serve a library outside the default location, add
`"--library", "/absolute/path"` to `args`.
For OpenCode, append those two values to its `command` array instead. The
client starts a process that can read and change that library as you. Keep
its ordinary tool approval settings and choose the library you intend to expose.

Client configuration follows the official [Claude Code MCP guide](https://code.claude.com/docs/en/mcp),
[Codex MCP guide](https://learn.chatgpt.com/docs/extend/mcp?surface=cli), and
[OpenCode MCP guide](https://docs.opencode.ai/docs/mcp-servers/).

| Tool             | What it does                                                                     |
| ---------------- | -------------------------------------------------------------------------------- |
| `search_library` | Search by meaning and keywords. Falls back to keyword search without models.     |
| `list_items`     | List items newest first. Filters: `reading`, `kind`, `limit`, `offset`.          |
| `read_item`      | Read Markdown a page at a time (`offset`, `maxChars`), or `format: "summary"`.   |
| `save_url`       | Capture a URL into the local library now. An optional `note` becomes a comment.  |
| `queue_save`     | Queue a URL in your account. Your ingest leader captures it on its next sync.    |
| `add_highlight`  | Highlight a verbatim quote, with an optional comment; `occurrence` picks a match. |
| `add_comment`    | Comment on an item.                                                              |
| `sync`           | Sync; on the ingest leader, also capture queued saves.                           |

A failed tool call returns `isError: true` with `{ "code", "hint" }`. The hint
says what to do next, such as "Run `sponge login`". Some failures add a narrower
`reason`: `item-not-found`, `item-deleted` (a deleted source cannot be saved
again), `sync-busy` (another sync is running; retry), `quote-not-found` and
`quote-ambiguous`, which also carries `matches`, the number of places the quote
occurs.

`read_item` sends Markdown as plain text in `content`, 40,000 characters at a
time unless `maxChars` (up to 200,000) asks for more. `structuredContent` holds
the item's fields with `offset`, `nextOffset` and `totalChars`, and not the
Markdown again. While `nextOffset` is a number there is more: call again with
`offset` set to it.

`save_url` attaches its `note` when the save is queued, so a capture still
`pending` when the call returns keeps it. Calling again with the same URL and
note adds nothing.
The [MCP reference](/docs/mcp) lists each tool’s input schema and effects.

## Complete a reading task

After connecting the server, ask your agent to search for a topic in the library,
read one result, and add a note explaining why it is useful. The sequence is
`search_library`, `read_item`, and `add_comment`. The first two calls read local
content; the final call writes an annotation. Confirm the result with
`read_item` using `format: "summary"`, which includes annotations.

Without MCP, the same task works through the CLI:

```sh
sponge search "your topic" --json
sponge show ITEM --json
sponge comment ITEM "Why this source is useful" --json
```

Replace `ITEM` with an `itemId` returned by search. [CLI reference](/docs/cli)
covers commands, flags, and failure statuses. Sponge has no separately supported
JavaScript SDK; use these stable interfaces when integrating another tool.

## CLI with --json

For ordinary CLI commands, `--json` returns a single JSON object with
`"ok": true` on stdout after success. On failure, stdout is empty and stderr
gets JSON containing `code`, `reason` and `hint`. `phase` appears only when a
capture failed while it ran. Without `--json`, a failure is the same JSON line
unless stdout is a terminal, where it is one plain line and the hint. The
long-running `sponge mcp` server uses the [MCP JSON-RPC message stream](/docs/mcp)
instead. The process exits with one of these statuses:

| Exit | Meaning         |
| ---- | --------------- |
| 0    | ok              |
| 1    | failed          |
| 2    | invalid input   |
| 3    | partial         |
| 4    | action required |
| 130  | cancelled       |

Commands never prompt. Output written for a person (dates in `list`, next steps,
readable sizes) appears only when stdout is a terminal; piped output and `--json`
keep their shapes. `reason` narrows `code` without changing the exit status:
`library-missing` (run `sponge init`), `item-not-found`, `item-deleted`,
`sync-busy` (another sync held the library for 30 seconds; retry),
`already-signed-in`, `unsupported-file`, `quote-not-found` and `quote-ambiguous`.
`sponge list --json` lists every item, oldest first, each with `createdAt`;
`--limit N` asks for the N newest instead.

```sh
sponge add https://example.com/essay --wait --json     # save
sponge search "attention economy" --json               # search (add --mode lexical without models)
sponge list --json
sponge show ITEM --json                                 # read: Markdown plus annotations and digest
sponge highlight ITEM "exact quote" --note "why" --json
sponge comment ITEM "a thought" --json
```

## Save queue

A save request is a URL waiting in your account. You can queue one from the web
library, `queue_save`, or `sponge saves add`. The account's ingest leader, one
signed-in device chosen by the cloud, claims it, captures it with the normal
`sponge add` pipeline, summarizes it with its local model when it has one and
syncs the item back. There is no summary tool: summaries happen on the leader. The
cloud keeps only the URL, the request's state and a short error code; it never
captures pages itself.

```sh
sponge saves --json                            # open and recent requests
sponge saves add https://example.com/a --json  # queue a URL
sponge saves run --json                        # capture queued requests on this device now
sponge sync                                    # also captures and summarizes on the leader (--no-saves, --no-digest)
sponge sync --watch                            # keeps doing both every 30 seconds
sponge status --json                           # "ingest": which device leads, from its last heartbeat
```

A request moves through these states:

- `queued` becomes `claimed` when a device takes it.
- A `claimed` request becomes `saved` (with an `itemId`) or `failed` (with an `errorCode`).
- A queued request can be cancelled.
- A failed request can be retried, which puts it back in `queued`.

A claim lasts 10 minutes. If the device does not finish in time, another device
can claim the request again.

Leadership prefers a device with a local summary model (`sponge setup --digest`)
and moves to another capable device when the leader stops checking in for two
minutes. `sponge saves` starts with "Saving on NAME", or says no capable device
is online.
