Get started in 5 minutes¶
Four steps: get a key, connect your agent, load your data, ask.
1. Get a key¶
Open api.limma.ai/v1/billing/checkout?plan=free and sign up — the Free plan takes no card and costs nothing, for as long as you use it (50 documents, 250 pages a month); move to a paid plan in place whenever you need more (plans and limits). When checkout is done, the welcome page shows your key once: copy it then. It is an admin key for your organization, so keep it out of files you commit and out of chat.
Put it in your shell — Claude Code, Cursor and the limma command read it from there:
2. Connect your agent¶
One setup per client. Claude Code and Cursor read the key from LIMMA_API_KEY, so it is
not written into a file you might commit; VS Code asks for it once and stores it; Claude
Desktop keeps it in its own configuration file.
Claude Code — one command:
claude mcp add --transport http limma https://api.limma.ai/mcp --header 'X-API-Key: ${LIMMA_API_KEY}'
Keep the single quotes: Claude Code expands ${LIMMA_API_KEY} itself each time it
connects.
Cursor — .cursor/mcp.json in the project (or ~/.cursor/mcp.json for every project):
{
"mcpServers": {
"limma": {
"url": "https://api.limma.ai/mcp",
"headers": {"X-API-Key": "${env:LIMMA_API_KEY}"}
}
}
}
VS Code (agent mode) — .vscode/mcp.json; VS Code asks for the key once and stores it:
{
"inputs": [
{"type": "promptString", "id": "limma-api-key", "description": "Limma API key",
"password": true}
],
"servers": {
"limma": {
"type": "http",
"url": "https://api.limma.ai/mcp",
"headers": {"X-API-Key": "${input:limma-api-key}"}
}
}
}
Claude Desktop — its claude_desktop_config.json (macOS: ~/Library/Application
Support/Claude/, Windows: %APPDATA%\Claude\), through
mcp-remote, which needs Node.js. The key is in
this file in plain text, so keep it to your machine:
{
"mcpServers": {
"limma": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://api.limma.ai/mcp",
"--header", "X-API-Key:${LIMMA_API_KEY}"],
"env": {"LIMMA_API_KEY": "<your key>"}
}
}
}
Any other MCP client — the URL https://api.limma.ai/mcp, transport "streamable
HTTP", and the header X-API-Key: <your key> (or a bearer token holding the key).
Your agent now has six tools: limma_search, limma_facts, limma_region,
limma_ingest, limma_connect and limma_documents (the documents it may read, and how
completely each was read). You never create a workspace: a call that names none
uses your organization's default one. More on the endpoint: MCP over
HTTP.
3. Load your data¶
Three ways, all into the same workspace:
-
A folder — the
limmacommand keeps a workspace equal to a folder. Run it again and only what changed is sent:pip install limma-ai read -rs LIMMA_API_KEY && export LIMMA_API_KEY # paste your key; nothing is echoed limma sync ./my-documents --url https://api.limma.aiThe same command reads an S3, Azure Blob or Google Cloud Storage bucket, a Google Drive folder and a SharePoint library (connectors).
-
From your agent — ask it: "Upload ./report.pdf to Limma." It asks
limma_ingestfor a one-time upload link and runs the command it is given; nothing else is needed (files from an agent). -
An export of records — a CSV, JSON or Excel file, each row loaded as a record (what you can connect).
Each document is read in the background. limma_ingest waits for the read (45 seconds
unless it asks for another wait, at most 120); a longer document is still read after the
call returns, and asking again tells your agent where it is.
4. Ask¶
Ask your agent in plain words. Three prompts to start with:
- "What revenue does Acme report for each year? Cite the page for each number."
- "Which of our contracts renew before March? Is that every one?"
- "Do any of our documents disagree about headcount?"
What comes back to the agent is not a passage to guess from. For the first prompt it is every revenue figure Limma holds for Acme — each with the value as printed, its period, the document, the page and the characters it sits at — and a count of how many exist. This is the reply the agent reads (run against a test workspace holding a small annual report and a restated quarterly update — how the examples work):
import json
MCP = {"Accept": "application/json, text/event-stream"}
r = client.post("/mcp", headers=MCP, json={
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "limma_facts", "arguments": {
"workspace_id": WS, "question": "Acme revenue", "mode": "enumerate",
"coordinate": {"attribute": "revenue"}, "output_mode": "compact"}}})
reply = json.loads(r.json()["result"]["content"][0]["text"])
count = reply["enumeration"]
print("exist:", count["exist"], "| delivered:", count["delivered"], "| truncated:", count["truncated"])
for fact in sorted(reply["facts"], key=lambda f: f["period"]):
print(fact["entity"], fact["period"], fact["value_token"], "| page", fact["page"],
"| characters", fact["char_start"], "to", fact["char_end"],
"| checked:", fact["byte_verified"], "| contested:", fact["contested"])
exist: 3 | delivered: 3 | truncated: False
Acme Corp FY2022 1,100 | page 0 | characters 135 to 140 | checked: True | contested: False
Acme Corp FY2023 1,250 | page 0 | characters 126 to 131 | checked: True | contested: False
Acme Corp Q1 2024 312 | page 0 | characters 52 to 55 | checked: True | contested: False
existanddelivered— how many facts exist at the address, and how many came back. When they differ,truncatedis true and the agent knows to ask for the rest (a count).checked: True(byte_verified) — Limma read those characters of the page again and they print exactly that value. The agent can read them itself withlimma_region(a receipt).contested— when two places disagree, both values come back marked contested, each with its own source, and Limma does not pick one (contested). The third prompt gets exactly that.
For the second prompt, limma_search returns the whole set of matches, the sentence
behind each, the near misses listed apart, and whether the set is complete — and when it
is not, why (search).
Next: what you can connect · security and your data · questions people ask · all guides