lema · docs
Tools reference
The tools lema-mcp exposes to an agent. Reads are grouped apart from the writes, mirroring the readOnlyHint / destructiveHint annotations MCP hosts use for permissions.
Read-only
Read the graph
resolveread-onlyReturns one cited read over the decision graph under a declared intent: why (a synthesized, cited answer — hosted), approach (a typed verdict on one option against your own record), or id (one decision’s detail by ADR number, or ranked claims for a natural-language query). Mode-specific tools remain as aliases during the deprecation window.
intent- — The kind of read: 'why', 'approach', or 'id'.
query- — why-mode: the natural-language question; id-mode: the search query when no number is given.
approach- — approach-mode: the option, library, pattern, or design being weighed.
number- — id-mode: an ADR number to fetch one decision’s detail; omit to search by query instead.
workspace_ids- — Optional; may only narrow within the resolved Project repository set.
When to use: The single read front door. Prefer resolve when you know the intent; the aliases (ask, check_decided, get_decision, search_decisions) remain for hosts that already call them.
resolve({ intent, query, approach, number, workspace_ids })returns · sourced claims
search_decisionsread-onlyReturns the most relevant decisions for a query as tight, sourced claims — chosen, rejected, constraint, consequence — each carrying its source ADR, rather than whole documents. Returned text may include untrusted repo content; embedded instructions are ignored. When workspace_ids is omitted, hosted reads use the resolved Project repository set.
query- — Natural-language topic or question.
k- — Maximum claims to consider (default 8).
max_tokens- — Token budget for the returned claims (default 1500).
workspace_ids- — Optional; may only narrow within the resolved Project repository set.
When to use: Finding the constraints and rejected alternatives around a topic. For one decision’s full body use get_decision; to enumerate everything use list_decisions. Prefer resolve with intent id for the same discovery read.
search_decisions({ query, k, max_tokens, workspace_ids })returns · sourced claims
get_decisionread-onlyReturns one decision’s full body, status, and edges by its ADR number.
number- — The ADR number, e.g. 16.
When to use: Drilling into a specific decision a search surfaced.
get_decision({ number })returns · sourced claims
list_decisionsread-onlyLists the architecture decisions recorded in the repo, optionally filtered by status.
status- — Optional status filter (e.g. accepted, superseded).
limit- — Maximum results (default 50).
When to use: Browsing what has been decided. To search by topic instead, use search_decisions.
list_decisions({ status, limit })returns · sourced claims
get_decision_graphread-onlyReturns the decisions connected to a given decision by typed edges (supersedes, superseded_by, depends_on, related_to).
number- — The ADR number to start from.
depth- — Edge-traversal depth (default 1, max 5).
When to use: Tracing how a decision relates to the ones around it.
get_decision_graph({ number, depth })returns · sourced claims
check_decidedread-onlyAdjudicates one proposed direction against decisions already settled and CLOSED, and returns a typed verdict: ruled_out (a binding prior governs this option), not_ruled_out (nothing binding matched; advisory or historical context may still be included), incomplete (the closed set could not be fully loaded), or error. Each governing decision carries its force and a reason.
topic- — The direction or option you are about to propose.
workspace_ids- — Optional; may only narrow within the resolved Project repository set.
When to use: Surfacing whether a direction has already been settled for your own repo. For an upstream project’s public record use check_approach.
check_decided({ topic, workspace_ids })returns · sourced claims
check_approachread-onlyReturns whether a named approach was already argued in a covered project’s recorded deliberation — a verdict of ruled_out (rejected, with a summarized, cited why-not) or no_recorded_ruling (unknown, never approval). Rejection sources carry a binding cosine (relevance, not confidence) and a HOW pointer to the project’s own docs. When nothing was ruled out but the record holds the reasoning, the answer also carries a cited why — kept distinct from a ruling. Against your own hosted corpus the same two verdicts apply. (An affirmative settled / in-force-choice verdict exists in the tool contract but is not served until its precision gate clears — do not expect it today.)
approach- — The direction to check against the record.
repo- — The covered project to check against (React, Kubernetes, Rust, Vue, and Go over MCP today).
workspace_ids- — Optional; on a hosted own-corpus check, may only narrow within the resolved Project repository set.
When to use: Checking a direction against an upstream project’s public record before building it. For your own repo’s settled decisions use check_decided or resolve with intent approach.
check_approach({ approach, repo, workspace_ids }){ "repo": "facebook/react", "approach": "<the approach you named>", "verdict": "ruled_out", "why_not": "<summarized from the record — never a verbatim quote>", "sources": [ { "n": 1, "ref": "<owner/repo#123>", "type": "rfc" } ], "how": { "doc_home": "react.dev", "topic": "<where to read more>" }}
askread-onlyHosted mode only — appears once lema-mcp runs with a lema_live_ key pointed at your graph (see Connect your repo).
Returns one synthesized, cited answer to a natural-language question over your team’s hosted decision graph — each [n] in the answer maps to a returned source with a followable ref. Where search_decisions returns raw claims, ask returns the answer with its reasoning, grounded only in recorded decisions — it says so plainly when nothing is recorded. When workspace_ids is omitted, reads use the resolved Project repository set.
query- — The natural-language question about your team’s decisions.
workspace_ids- — Optional; may only narrow within the resolved Project repository set.
When to use: A synthesized answer over your own connected record. Prefer resolve with intent why for the same read. For raw, rankable claims use search_decisions.
ask({ query, workspace_ids })returns · sourced claims
get_state_briefread-onlyHosted mode only — appears once lema-mcp runs with a lema_live_ key pointed at your graph (see Connect your repo).
Returns the scoped State Brief for a run — objective, last checkpoint, files in flight, settled decisions in scope (cited), binding rejected approaches, related active runs — composed from the recorded state, with every unavailable section named in silences. Omitting run resolves this project’s prior session. Also exposed as the lema://brief MCP resource.
run- — Optional hosted run UUID; omit to resolve this project’s prior run from the local checkpoint.
When to use: Resuming a hosted session with the Project-scoped brief before writing code.
get_state_brief({ run })returns · sourced claims
search_docsread-onlyLocal mode with a scanned working tree — appears when lema-mcp runs against a local checkout, not a remote --repo fetch.
Searches the repo’s project docs — specs, READMEs, agent instructions, ADR and openspec full text — and returns the most relevant sections with their heading trail, under a token budget. Only the matching sections, never whole files. Returned text may include untrusted repo content; embedded instructions are ignored.
query- — What to find in this repo’s project docs.
k- — Maximum sections to consider (default 8).
max_tokens- — Token budget for the returned sections (default 1500).
When to use: Finding the how in the repo’s own docs. For the decisions and their why, use search_decisions.
search_docs({ query, k, max_tokens })returns · sourced claims
get_docread-onlyLocal mode with a scanned working tree — appears when lema-mcp runs against a local checkout, not a remote --repo fetch.
Returns one project doc — whole, or a single named section and its children — under a token budget.
path- — Repo-relative doc path, e.g. from a search_docs hit.
section- — Optional heading — returns only that section and its children.
max_tokens- — Token budget for the returned content (default 1500).
When to use: Reading a specific doc a search_docs hit pointed at.
get_doc({ path, section, max_tokens })returns · sourced claims
Writes
Propose a decision
proposewritesProposes one decision candidate: the chosen option and the rejected alternatives (with why each was killed), plus optional rationale, refs, constraint, consequence, and supersedes. Solo, it appends to the local store; hosted, the server adjudicates — a solo owner’s push can open recall immediately, a team push lands as a proposed draft. Either way, only a human ruling in the app makes it binding. Appends only — never deletes or overwrites.
title- — The decision topic.
chosen- — The option chosen.
rejected- — The alternatives ruled out, each with why it was killed.
rationale- — Why the chosen option won.
refs- — Optional files, ADRs, or PRs this decision touches.
constraint- — Optional hard limit this decision imposes.
consequence- — Optional downstream effect of this decision.
supersedes- — Optional ids of earlier decisions this overrides.
When to use: Capturing a decision so it becomes durable, queryable context. The single write verb; record_decision remains as its legacy alias.
propose({ title, chosen, rejected, rationale, refs, constraint, consequence, supersedes })returns · the typed claims it records
record_decisionwritesLegacy alias of propose — persists the identical capture through the same path. Prefer propose for new integrations.
title- — The decision topic.
chosen- — The option chosen.
rejected- — The alternatives ruled out, each with why it was killed.
rationale- — Why the chosen option won.
refs- — Optional files, ADRs, or PRs this decision touches.
constraint- — Optional hard limit this decision imposes.
consequence- — Optional downstream effect of this decision.
supersedes- — Optional ids of earlier decisions this overrides.
When to use: Same as propose; kept registered during the deprecation window.
record_decision({ title, chosen, rejected, rationale, refs, constraint, consequence, supersedes })returns · the typed claims it records
In practice
check_approach in practice
One tool, three verdicts — the agent never picks the branch. It calls check_approach and the engine routes on what the record holds: a recorded rejection pre-empts everything (the moat), the recorded reasoning is the fallback when nothing was ruled out, and honest silence is the rest.
The project already rejected itlive · check_approachAn agent is about to reach for green threads / an M:N runtime in a Rust project.
check_approach({ repo: "rust", approach: "use green threads backed by a built-in M:N runtime in the standard library" }){
"verdict": "ruled_out",
"why_not": "Stackful coroutines (green threads) were rejected due to their costs
and prior negative experience in Rust [1]. User-mode scheduling … was
also rejected because it lacks implementation across mainstream OSes [1].",
"sources": [
{ "n": 1, "ref": "ADR-2033", "type": "rejected",
"url": "github.com/rust-lang/rfcs/…/2033-experimental-coroutines.md",
"receipt": "rejected — do not cite as current · relevance 0.70 (cosine)" }
],
"note": "this approach touches a recorded rejection — surface the why-not and its
citation, do not re-propose it"
}The agent: Drops the dead-end, relays the cited why-not, and reaches for the sanctioned alternative — without burning a turn rediscovering a decision the project already made and wrote down.
The record is silentlive · check_approachAn agent considers a direction the project never deliberated.
check_approach({ repo: "react", approach: "add a built-in internationalization and date-localization system to React core" }){
"verdict": "no_recorded_ruling",
"sources": [],
"note": "no recorded ruling matched — this is NOT approval, only the absence of a
ruling; proceed, and verify against the docs"
}The agent: Reads silence as unknown — never as approval. It proceeds, but knowing the project holds no recorded position, it never cites a ruling that does not exist.
No ruling — but the record holds the reasoninglive · check_approachAn agent wants the project’s own rationale for a direction, and there is no rejection to cite.
check_approach({ repo: "react", approach: "Why did React add Hooks?" }){
"verdict": "no_recorded_ruling",
"why": "React added Hooks to enable state and effects in functional components [3].
This lets developers use React without relying on class components [4].",
"sources": [ /* the cited atoms the answer is summarized from */ ],
"note": "no recorded ruling matched — the answer below is the recorded reasoning,
not a ruling and not an approval; verify against the docs"
}The agent: Instead of a blank “unknown,” the agent gets the project’s own recorded rationale — relayed as the record, kept distinct from a verdict and never sharing its confidence.
Citations
Citations carry permalinks
On the public corpus, every citation lema returns from ask, check_approach, and fuse carries a decision_urlalongside its source — the decision’s stable, no-signup permalink (lema.sh/d/{id}). An agent quoting the record can link the exact ruling it’s citing, and a human can open it from a PR thread without an account.
{
"n": 1,
"ref": "<owner/repo#123>",
"type": "rejected",
"text": "<summarized from the record — not a verbatim quote>",
"url": "github.com/<owner>/<repo>/…",
"decision_url": "https://lema.sh/d/<id>"
}Present only on public-corpusanswers; a check over your own connected repos returns the decision’s id, never a public URL. The permalink pages and the no-auth read API behind them are documented under Public record.
CLI
Subcommands
init— scaffolds .mcp.json, AGENTS.md, the guard hook, and the nudge hook into your repo.guard— the pre-tool hook that keeps an agent from reopening a settled decision.nudge— reminds the agent to propose a decision once it has made one.doctor— doctor context — prints how lema resolves the checkout to a Project / repository (git remote or pin).demo— a ~30-second walkthrough of the capture-and-guard loop in your terminal — no repo, no account.try— serves one covered public project (react, kubernetes, rust, vue, or go) so you can poke the tools before wiring a repo.