Model API
Everything Folio knows of the folder reads as JSON at ?api=<resource> on a path, over the port and the socket alike. The API only reads: it answers GET and HEAD alone, a query in it is one SELECT as anywhere else, and nothing it answers is recorded as seen. A request from another site's page - an Origin that is not a localhost address, or a browser marking it cross-site or same-site - is refused with 403, as is a foreign Host. While sharing is paused, reads, changes, needs, presence and stale answer only {"paused": true} and a message saying so; everything else answers as before. A path is relative to the served folder, or absolute under it - starting with the served folder's own path, as /srv/notes/reports/weekly.md?api=document does for the folder /srv/notes where nothing lies at srv/notes/reports/weekly.md inside it - and so is a source, absolute only when it starts with /; query takes a quoted path absolute under the served folder too, and embed a reference, beside those relative to its folder and those from the served folder after a slash. Answers name paths relative to the served folder, and every time as ISO 8601 in UTC. A missing or malformed argument answers 400, a path naming no document 404 for a resource that reads one, and document 503 while Folio has not read that document yet, each with the reason as text - a document in a dot-folder or node_modules, which the folder model leaves out, is read when asked about and answers as any other, while documents, health, digest and the document tables still leave it out; a refusal of a path names the served folder. A failure Folio did not expect answers 500 with a fixed text naming the part of Folio that failed - its declarations, history, read record, data engine or health check wherever the failure escapes them, otherwise the part answering the resource, else its server - and saying the reason is in Folio's log, in the terminal running it; the error's own text goes only to the log. /?api lists every resource.
| Resource | Answers |
|---|---|
/<folder>/?api=documents | Every document under the folder, with its kind when a declaration places it |
/<path>?api=document | A document's blocks - id, kind, lines, the start of its text - its blocks as its page places them through includes, its kind and fields, and the data files it reads |
/<path>?api=provenance&block=<id> | Where one block of the page came from, as its ⓘ shows it; &source=<file> for a block an include brought in |
/<path>?api=related&block=<id>, /?api=related&question=<text> | The blocks of other documents closest in meaning to blocks of the page - each block given counts, taken together; &source=<file> for a block an include brought in - or to a question in your own words, closest first, each with its document, lines, words and closeness, as Related by meaning lists them; {"asked": false} where no declaration asks for it, and while the folder is being related, how far it has got |
/<folder>/?api=query&sql=<select> | The result of one SELECT, run from the folder as a query in it runs: its columns, its first 5,000 rows and the files it read; a query that fails answers 400 saying why, naming files by their place in the folder, unless the failure arises in Folio's declarations, history or read record, which answers 500 as any failure Folio did not expect |
/<path>?api=embed&reference=<reference> | What a reference written in the document names, as its embed would show it |
/<path>?api=kinds | The declarations applying at the path, and the kinds, rules and health entries they layer to |
/?api=health, /<path>?api=health | Everything wrong in the folder, as the health view lists it, each problem with its severity where it has one; on a path, only the document's problems, or those of the documents under the folder |
/?api=needs, /<path>?api=needs | What needs you, as the landing page lists it: each item's source, document, line and what is wrong; on a path, only what lies there |
/?api=presence | What you are looking at now: the page in each open tab, and the block under the pointer with the start of its text, the most recent first; a hidden tab shows nothing |
/?api=stale, /<path>?api=stale | Documents that have probably outlived their use, each with when its file last changed and why; on a path, only those there |
/?api=changes, /<path>?api=changes | The catch-up list: every document read before whose blocks changed since, newest change first; on a path, only those there |
/?api=reads, /<path>?api=reads | Every document read, with when it was last read; for a document, each block with its version, the version and time it was read, and what is marked new, changed or removed |
/<path>?api=history | A document's versions, newest first; &version=<id> gives a version's text, &block=<id> each text a block has had, &reference=<reference> each result an embed has shown |
/<path>?api=used-by | The documents including the file, or whose embeds and queries read it |
/?api=digest | Every document created, changed or removed in a period, and every value that moved; the last day unless &from=<time>&to=<time> names one, each an ISO 8601 date or time - 2026-11-02, 2026-11-02T09:00:00+01:00 - a date alone and a time without an offset read as UTC, a time standing for its whole millisecond, and a number or an impossible date refused; a + in an offset is written %2B in an address |
The MCP server's tools are the same resources, each named folio_ and the resource, used-by as folio_used_by: folio_documents, folio_document, folio_provenance, folio_related, folio_query, folio_embed, folio_kinds, folio_health, folio_needs, folio_presence, folio_stale, folio_changes, folio_reads, folio_history, folio_used_by and folio_digest. A tool's path starting with / is absolute: one under the served folder is sent as the path relative to it, any other refused as outside it. A tool takes the path as path - or folder for folio_documents and folio_query; folio_presence and folio_digest take none, folio_health, folio_needs, folio_stale and folio_changes one only to narrow, and folio_related none when given a question - and the resource's other arguments by their names, folio_digest's from and to as ISO 8601 times, and answers the resource's JSON as text, or an error with Folio's text. An answer longer than 20,000 characters has its lists cut, each to at most the same number of entries, the most that fits, so a short list stays whole, folio_query's rows aside, and carries omitted - the entries each list left out - and narrow, how to ask for less; the HTTP API answers whole. Each tool has a title, a description saying what it answers, when to use it rather than another tool, what it reveals of the reader and what each field of its answer means, described arguments, and all four hints: it only reads, destroys nothing, changes nothing when called again, and calls no service beyond Folio. The server's introduction names the moments to call a tool - before editing, moving or deleting a file, after editing a document, before writing a document of a kind and before quoting a number from a data file - says the reader is the person reading the folder in their browser, whose five tools are called only for a request about what they see, have read or must catch up on, or to fit a report to what they have not read yet, and routes questions to tools; the introduction says the user starts Folio, never the agent, and a call made while no Folio answers - or while another program answers there with a page of its own - says where folio mcp looked, that the user starts Folio or starts folio mcp with --port or --socket to match one already serving - or, when a Claude Code plugin started folio mcp, sets the plugin's Folio setting to its address or socket - and that the agent starts no process.