Skip to content

Embedding Limma in your platform

This page is for an engineering team whose platform runs its own agents and wants Limma as the layer that holds a customer's records and documents and answers over them. Your platform keeps what it already does — connecting to the customer's sources, running the agents, the user interface. Limma takes what your connectors fetch, structures it into facts at addresses, and answers your agents' questions over MCP with every match, the sentence behind each, and a statement of whether the list is complete.

Each section names the page that holds the detail.

The shape

customer sources ──> your connectors ──> Limma (inside the customer's environment)
 (files, exports,      (fetch, track       ├── /v1/workspaces/{id}/documents  files
  databases)            changes)           ├── /v1/public/ingest              text
                                           ├── /v1/public/upload_export       export files
                                           ├── /v1/public/connect             records, tables
                                           └── /mcp  <── your agents, one key per caller
  • One process, one port. The REST API and the MCP endpoint are the same server.
  • Inside the customer's environment. Limma ships as a compiled image that starts only under a license Limma issues; nothing of a document leaves the environment (Install, Licensing).
  • Your model, inside the environment. Limma reads text that needs judgment with an OpenAI-compatible endpoint you point it at (Choose the model).

1. Install

Follow Install Limma inside your network with the image and the license file Limma sends you. The license file goes where Licensing says; without it the image does not start. The image reports usage counts — never content — every six hours, or writes a monthly file where outbound traffic is blocked (What is sent).

2. Keys

Every caller has its own key, issued as key:organisation:role[:workspaces[:documents]] (Keys and roles). A typical embedding uses:

Key Role Used by
a loading key service your connectors, over REST: put documents and records in
an agent key analyst your agents: every question, including one whose condition must be read from documents with your model — that read spends, within the search's own budget
a read-only key viewer an assistant that must never spend or change anything: it is answered from what is already structured or read, and a search that would need a new read is refused
an admin key admin your operators: keys, deletion, the audit trail

An analyst key may also load data. To keep your agents to questions, expose only the question tools over MCP — the operator's LIMMA_MCP_TOOLS=limma_search,limma_facts,limma_region — and give the agent runtime the MCP endpoint, not the REST API. Every call, by every key, is in the audit trail under that key's id.

3. Put data in

Pick the door by the shape of the data, not by its source.

The data Door What Limma does with it
A folder of files, an S3 / Azure Blob / Google Cloud Storage bucket, a Google Drive folder, a SharePoint or OneDrive for Business library limma sync <source> (pip install limma-ai) — one command, run again to keep it current (Connectors) sends each file through the upload door below — under its path, or under its Drive or SharePoint id — sends a changed one as a new version, and deletes what is gone with --delete
Files: PDF, Word, PowerPoint, Excel, HTML, email, RTF, XML, text; images (PNG, JPG, TIFF) and scanned pages, read by OCR POST /v1/workspaces/{id}/documents (multipart; SDK upload_document) reads the text and tables, keeps every fact's exact position
Text you already extracted POST /v1/public/ingest (limma_ingest) the same, from text
Records: JSON, JSONL, CSV, TSV, Excel exports POST /v1/public/connect (limma_connect) drafts the schema from the records, asks only where it cannot tell, loads each record as facts
An export file your connector holds and the API host does not POST /v1/public/upload_export (multipart; SDK upload_export), then connect with its export_id the same as a file on the host: stored for this workspace, read by the same reader, deleted by the pull that loads it
A database table POST /v1/public/connect with database_url + table reads it, read-only; every fact can re-read its row

A page is read as it is laid out, and a number at the scale its own page or sheet states. A PDF page set in two columns of text is read column by column, never straight across; a table's columns stay apart even when no rules are drawn between them. An Excel sheet is read at the scale it states itself ("in thousands"): a sheet that states none is read as written, never at a scale another sheet of the workbook states.

An export file is named by where it is. On the API host — the ./imports directory the install mounts — connect names its path. Anywhere else — your connector's own machine, a deployment whose API processes share no disk — upload it first and connect the export_id the upload answers: A file the API host cannot reach. The upload is REST and SDK only; your agents connect the id over MCP.

File each document under what it is about. Send entity_name with a document that is about one thing — a person's CV, a contract, a company's filing. Limma then reads that document when a question asks about that thing. A document sent without one is still searchable by its facts, but cannot be enumerated as "every document about X".

Load the register, then the documents. Questions about sets — "which contracts…", "which clients…", "who…" — are answered over the things a workspace holds as records. Load the register (the contract list, the client list, the directory) through connect, then the documents filed under its entries. Records pages: A document-store export, A CSV or Excel export.

A sync tool's output (Airbyte or one like it) is read through the same doors — key each record by the source's own id, never the tool's: Loading a sync tool's output.

Keep it in step with the source. Your connectors send changes, not snapshots: the external id, versions, deletion and the per-user access list are on Sync and access. For a folder or a bucket there is nothing to build: Connectors is one command that does it through those same doors.

4. Connect your agents

Point your MCP client at https://<limma-host>/mcp with the agent's key (MCP over HTTP). The default tools are:

Tool What it answers
limma_search the closed set of matches for a question, each with why, the near misses, and whether the list is complete
limma_facts every fact at an address, with receipts
limma_region the exact words a receipt points at
limma_ingest, limma_connect loading, for keys allowed to load

A search answer carries, for each match, the conditions it met, the value, and the sentence from the source; the matches one condition short; counts of everyone else; and complete, or incomplete_because with the reasons (Search, A count). Tell your agent to answer from those fields and to quote the sentence when it names a match.

5. What it does not do yet

Say these to your customers as they are:

  • Access per record of a records export: an access list applies to a whole export source, not to one row (Sync and access).
  • Questions about ideas rather than things — "where do we discuss pricing risk?" — return passages, without a completeness statement.
  • Conditions judged from free text (e.g. "sold surgical staplers through public tenders") are read by your model for each candidate: exact where the data is structured, good but not perfect where text must be judged — and a first read of a new condition takes time. A candidate whose text does not prove a condition is returned as one condition short, with the sentence, never as a match.