MCP server
webhooks
A knowledge backend other applications can actually call.
Everything the workspace does — retrieval, grounded chat with memory, document reads, proposals, generation, long-running jobs — is reachable over HTTP and over the Model Context Protocol. Access control, the retrieval barriers and the credit meter apply identically, because there is no path around them.
The MCP server holds zero references to the application.
It talks to Liminalis over HTTP with an API key, exactly like any third party would. That isn't a guideline someone might quietly break under deadline pressure — the project simply has no reference to the server assemblies, so the compiler enforces it.
It cannot bypass anything
Project access checks, the GM-only wall, the lens barrier and the credit meter all sit on the HTTP path. A tool server that can only speak HTTP inherits every one of them, permanently, without anyone having to remember to apply them.
It doubles as the API's acceptance test
If the server can only speak HTTP, then every gap in the public API surfaces as “I can't implement this tool” during development — instead of being quietly papered over with a direct internal service call that no external caller could ever make.
No repository, no cache, no vector service, no background work. Any persistence appearing inside the tool server is treated as a design bug rather than an optimisation.
Eleven tools over MCP, and the HTTP API behind them.
| Tool | What it does |
|---|---|
| describe | Self-description: the project's lenses, document types, sections, stages and commands. An agent discovers the domain rather than having it hardcoded. |
| outline | The shape of the project — structure without pulling every document. |
| search | Grounded retrieval over the corpus, through the same stack the workspace uses. |
| read_document | A document, with truncation declared rather than silent. |
| lookup | Resolve a name — title, slug or alias — to the thing it means. |
| propose_document | Create a new document as a reviewable draft. |
| propose_edits | Suggest anchored changes to an existing document. |
| apply_edits | Apply them, given the scope to do so. |
| run_command | Invoke a lens command — the same ones a user types in chat. |
| generate | Run a generation stage from the lens's build plan. |
| job_status | Poll long-running work. |
The HTTP API additionally exposes chat and chat history, job submission and cancellation, and webhook
subscriptions. Streaming chat is deliberately excluded from the tool surface.
An agent gets the memory loop for free
An agent holding one chat id inherits the entire rolling-summary memory system without implementing any part of it — retrieval, decay, summarization, the cached prefix, all of it. Verified with a deliberately anaphoric second turn that could only be answered by remembering the first, with cost reported per call.
New domains need no client changes
Because one lens descriptor feeds the workspace, the agent API's describe, and the tool registration, a
lens authored at runtime shows up to agents automatically. The domain is data on every consumer of it.
Agents propose. People decide.
Propose, don't write
Agent output lands in the draft queue marked as generated provenance. Direct write requires an explicit scope that is off by default.
Scopes only narrow
Effective permission is the minimum of the key's scope and the key owner's role on the project. A key can never grant more than the person who created it has.
Barriers before assembly
Access filtering happens before context is built. A prompt-injected agent cannot extract material that never entered its context in the first place.
No silent clipping
Matched spans are expanded in place; truncation is declared both in the text and as a structured field, with a token estimate. Retrieval that doesn't lie to you about what it showed you.
Webhooks with the boring parts done
Per-key subscriptions, an echo handshake, HMAC signatures the listener independently recomputes, per-key rate limiting, and an SSRF guard whose rejection classes — including the cloud metadata address — are covered by tests.
Thin payloads
A webhook says that something happened and what to fetch. It never carries a result body, so an event delivery is not an exfiltration channel.
Liminalis already runs headless, behind an application that isn't the workspace.
A separate consumer product — its own WebGL client, its own interface, its own users — uses Liminalis purely as a knowledge backend: documents, retrieval, generation and the derived knowledge layer, over HTTP, with none of the document workspace in front of it. The workspace is pointed at that project only as a debugging view onto its data.
That is the platform claim with a second consumer attached rather than an argument about one — the same engine serving a document-first workspace and an application that never shows a document to anybody. If you are evaluating Liminalis as a component inside someone else's architecture, that is the reference implementation, and it was built against the same public HTTP surface described above rather than a private one.
What this is for
- An application that needs grounded answers over a private corpus, without building a retrieval stack.
- An agent that should read and propose changes to a body of documents under review rather than write to it directly.
- A domain tool that wants the knowledge layer — entities, relations, facts, motifs, a reverse index — without deriving one.
- A team wanting one metered, audited place where model spend against their corpus happens.
What isn't there yet
- Search over the API runs at the deterministic assembly level; the higher fan-out thinking levels aren't exposed to it.
- Idempotency exists for jobs, not beyond them.
- Lens authoring over the agent API is deliberately out of scope.
-
Tool descriptions are compile-time, so they can't enumerate a project's lenses — callers are pointed at
describeinstead.