MCP Tools
The server exposes five tools. Four are read-only; to_markdown writes to disk.
The intended loop: parse a document, read the headings that look relevant, search when none of them do, get_outline if the outline scrolls out of context, and to_markdown when the user wants the document itself rather than an answer drawn from it.
parse
Parse a document and return its heading outline. Call this first.
Accepts a local path or an http(s) URL; the format is detected automatically.
| Parameter | Type | Default | Description |
|---|---|---|---|
source | str | — | File path or URL to parse |
password | str | None | None | For an encrypted document |
refresh | bool | False | Re-parse even if an identical parse is cached |
Returns
| Field | Description |
|---|---|
doc_id | Handle to pass to every other tool |
title | Document title from metadata |
pages | Page count |
headings | Number of headings in the hierarchy |
document_tokens | Size of the whole document — what reading everything would cost, not the size of this response |
outline | Heading outline, each line marked with its read cost |
is_complete | false when an outline was returned, true when the full text was |
text | Present only when is_complete is true — the whole document |
renderer | HTML sources only: "static" means no browser rendered the page |
A short document comes back as text with is_complete: true instead of an outline. There is nothing left to fetch — read and search on it would return only what the model already holds. The threshold is controlled by DOCSLICER_MCP_FULL_TEXT.
renderer: "static" means Playwright was not available, so scripts never ran and heading detection is weaker. A thin outline on a static render may be incomplete rather than accurate — see Installation.
get_outline
Re-fetch a parsed document's outline, with per-heading read costs. For when the outline has scrolled out of context. Cheap — no re-parse.
| Parameter | Type | Description |
|---|---|---|
doc_id | str | Handle from parse |
Returns doc_id, title, and outline.
read
Return the text under one or more headings, chosen from the outline.
| Parameter | Type | Description |
|---|---|---|
doc_id | str | Handle from parse |
headings | list[str] | Exact outline headings, or "Parent > Heading" |
Pass heading text exactly as the outline prints it, and pass every heading you want in a single call. A heading includes its subsections, so it returns roughly the tokens the outline printed against it — prefer a subsection when the parent is large.
Disambiguation. Where a heading appears in more than one place, prefix any ancestor with > to pick one — "Notes > Revenue". The full chain is never required. Unqualified, every match is returned.
Nesting. Asking for a heading and one nested under it returns the ancestor alone, since its text already carries the other. The folded-in headings come back under already_included with the parent they were returned under — nothing is missing.
Returns
| Field | Description |
|---|---|
doc_id | Echoed back |
text | The section text, with [Page X] markers |
tokens_returned | The same per-heading figures the outline quoted |
already_included | Headings folded into an ancestor, if any |
note | Explains already_included when present |
A [Page X] line means the text below it is on page X until the next such line, numbered as the document numbers it (S-23, iv). Cite the nearest marker above the quote — sections run over several pages, so the page a section opens on is right only for its first part.
search
Find where something is discussed. Use when the outline is not enough.
| Parameter | Type | Default | Description |
|---|---|---|---|
doc_id | str | — | Handle from parse |
query | str | — | A term, a phrase, or a description |
limit | int | 8 | Most sections to return, each a distinct place |
Try the outline first — it is faster. Search is for when it does not settle the question: headings that name nothing useful (Note 14, Item 7A), a figure buried in a table no heading mentions, or a section too big to read whole.
Two matchers run together. A whole-word literal match catches NCT03785249, €1.2m, a surname — the things a tokenizer would split or BM25's idf would misjudge. BM25 over the chunks finds sections about a topic the query only describes. Literal hits rank first: naming a string exactly is a stronger signal of intent than resembling one. One hit per heading — the same place twice is a wasted slot, not a stronger match.
Returns doc_id, query, hits, a note, and unmatched_terms when the query contained words the document does not have. Each hit:
| Field | Description |
|---|---|
heading | Pass this to read |
page | Page reference for the matching chunk |
snippet | A window cut around the match |
snippet_is_partial | Present when the snippet was cut from something longer |
read_tokens | What reading that heading costs |
Search returns places, not answers. Stop at a snippet only when it answers the question alone and depends on nothing outside the window. A partial snippet is missing what sat around it — often a table's header row, so figures appear without the column naming them. Never infer a truncated table's columns.
unmatched_terms exists because score magnitude cannot tell a good query from a bad one: a nonsense query whose one real word happens to be rare will outscore a sensible query about a common topic. Listing the words that appear nowhere makes that recognisable.
to_markdown
Write the whole document to disk as markdown. Returns a path, not the text.
| Parameter | Type | Default | Description |
|---|---|---|---|
source | str | None | None | File path or URL. Omit if passing doc_id |
doc_id | str | None | None | Handle from a previous parse — reuses it instead of parsing again |
password | str | None | None | For an encrypted document. Ignored with doc_id |
output_path | str | None | None | Where to write it |
For when the user wants the document as a file rather than an answer drawn from it. Nothing enters the model's context, so document size stops mattering. Tables, charts, page markers, and structure are all preserved.
Output path resolution. Absolute paths are used as given; relative paths resolve against the source file's directory. The default is the source filename with a .md extension, written next to the source — for a URL source, to a temp directory. Parent directories are created.
Returns doc_id, title, pages, output_path (always absolute), markdown_size_bytes, overwrote, and document_tokens.
An existing file at the destination is overwritten without prompting. The response sets overwrote: true when that happened — worth surfacing to the user.
Chunking options
Every parse on the server uses one fixed set of options, chosen for reading rather than embedding — larger chunks, no headers/footers, no comments, tables as markdown, and exact token counts via tiktoken. They are not model-facing and cannot be set per call. To control chunking yourself, use the Python API.
Next steps
- Configuration — transports, sandboxing, cache limits
- MCP Server — install and how the outline-first design works