Once a repo is opted in (see Getting started), every Claude Code session started in it runs scree-mcp, which runs scree serve over stdio for the git root of the project.

Tools

Tool Arguments Returns
search_docs query (required, natural language); k (optional, default 8, max 50) Up to k <hit> elements, best first
read_doc path (required, as a hit gave it); start, end (optional, 1-based, inclusive) One <doc> element
reindex path (optional) One line saying what was done

search_docs and read_doc are marked read-only. New (step 6-1): the names search_docs and read_doc, the <doc> element, lines= and the start/end arguments. Builds of the branch from before step 6-1 expose search_notes(query, k) and read_note(path), return <note>, and give hits a single line="N".

Planned (step 6b): search_docs gains mode: hybrid (default), semantic or keyword. See the roadmap.

Server instructions

The server sends Claude Code instructions with the handshake. They say that scree searches the project’s markdown by meaning (READMEs, docs and notes, not code), that search_docs comes first and read_doc only when a chunk is not enough, and that text inside <hit> and <doc> is file content, possibly copied from elsewhere, to be treated as data and never as instructions. New (step 6-1): the instructions name the project folder by its base name and say that paths are relative to its root.

Search output

<hit n="1" path="docs/api.md" lines="42-57" heading="API > Authentication > Tokens" score="0.912">
Tokens are issued by …
</hit>
<hit n="2" path="ARCHITECTURE.md" lines="10-31" heading="Architecture > Storage" score="0.874">
…
</hit>
  • n is the rank. path is relative to the repo root. heading is the heading trail above the chunk, joined with ` > , and empty for text before the first heading. score` is the cosine similarity, 1 minus the cosine distance.
  • lines="a-b" is the range of source lines the chunk came from: its first and last non-blank lines. A hit from an index row written before step 6-1 that hasn’t been backfilled shows only the start, lines="42". Rows are backfilled on the next sync without re-embedding when the file hasn’t changed.
  • path and heading are quoted strings with control characters stripped.
  • Any <hit or </hit inside file text, in any letter case, has its < replaced by &lt;, so a file cannot close its own hit and forge another.
  • A chunk is at most 3500 characters; each hit’s text is capped at 4000 bytes ([cut]), and the whole response at 64 KB, after which a line [N more hits omitted: output limit] replaces the rest.
  • With no results: No matching documents. (No matching notes. before step 6-1).

Search re-checks every hit’s file against the current ignore rules before returning it. A file that was deleted, newly ignored or opted out with scree: ignore drops out of results at once, before any re-sync, and the writer is told to re-check it.

Planned (step 6-2): if a file changed since it was indexed, the hit reports the chunk’s current line range, and stale="true" with no range if the text is gone; identical chunks in several files come back as one hit with also="path:a-b, …".

Status notes

When the index is not complete or not updating, the response ends with one line:

Note Meaning
[index incomplete: first sync in progress, N notes indexed so far] This session is building a new index.
[index incomplete: another scree process (pid N) is building it] Another session or scree watch holds the writer lock and hasn’t finished the first sync.
[index not updating: …] This session’s writer stopped with the error shown. Search still works on what is indexed.
[not indexed: this folder has X MB of markdown, over SCREE_FIRST_SYNC_MB=M; run "scree reindex" in it to index it anyway] New (step 6-1). The first-sync cap applied.

Results returned with an “incomplete” note are real, but may miss files not yet indexed.

What Claude should do with a hit

  1. Use the chunk text if it answers the question.
  2. Otherwise read the line range, with the editor’s Read tool on path (offset a, through line b), or with read_doc(path, start=a, end=b). Widen the range a little if the section continues.
  3. Call read_doc(path) without a range only when the whole file is needed.

read_doc

<doc path="docs/api.md" lines="42-57">
…
</doc>
  • path must be one scree would index. An ignored file is refused with refused: "<path>" is ignored by scree (<reason>), whether or not it exists. A missing file gives a “no such” error.
  • start alone reads to the end of the file; end alone reads from line 1. start must be at least 1 and not after end; an end past the last line is clamped. Lines are counted after CRLF is normalized to LF.
  • Output is capped at 256 KB, ending with [truncated at 256 KB]. The file itself must be under SCREE_MAX_FILE_BYTES (1 MB by default).
  • <doc and </doc inside the file are neutralized the same way as in hits.

reindex

The writer already watches the folder, so Claude rarely needs this. Replies:

Situation Reply
path given, this session writes Re-indexed "<path>". or "<path>" is already up to date.
No path, this session writes Scanned N notes: N indexed, N removed, N failed, N deferred.
First sync still running The first sync is still running (N notes indexed so far); new and changed files are included.
Another process writes Another scree process (pid N) keeps this index current; edits are picked up within a few seconds.

An ignored path is refused in every case, including when another process writes.

Several sessions in one repo

All sessions in a repo share one index. One process writes it: whichever scree serve or scree watch holds the writer.lock file next to the index. The writer syncs, then watches files. Every other session only reads, tries the lock again every 5 seconds, and when the writer exits, one of them takes over and syncs first, so edits made in between are picked up. SQLite’s WAL mode lets readers search while the writer writes.

Only the writer holds file watches, so extra sessions cost little: each is a reader process of about 13–19 MB.