lema-mcp 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +293 -246
- package/package.json +12 -7
package/README.md
CHANGED
|
@@ -1,162 +1,103 @@
|
|
|
1
1
|
# lema-mcp
|
|
2
2
|
|
|
3
|
-
Your coding agent
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
chosen option *and* the alternatives you killed — and then **enforces them**, so a
|
|
8
|
-
settled decision stops getting reopened. No account, no database, no network.
|
|
9
|
-
|
|
10
|
-
Most "context" tools are read-only: a nicer way to grep your docs. `lema-mcp`
|
|
11
|
-
reads too, but its job is **never-reopen**:
|
|
12
|
-
|
|
13
|
-
- Your agent settles a choice → it calls **`record_decision`** with what it chose
|
|
14
|
-
**and the alternatives it rejected, with why each was killed** (the part that
|
|
15
|
-
never survives into the code).
|
|
16
|
-
- Before anyone's agent proposes a direction → **`check_decided`** returns the
|
|
17
|
-
prior decision if that option is **CLOSED**.
|
|
18
|
-
- And on every edit, a **PreToolUse guard hook** (installed for you by `init`)
|
|
19
|
-
reads the draft change and surfaces a CLOSED decision *before* the dead option
|
|
20
|
-
gets re-proposed — enforced off both your captured decisions **and the repo's
|
|
21
|
-
own ADRs**.
|
|
3
|
+
**Your coding agent can read the code. It can't read the _argument_ behind it.**
|
|
4
|
+
`lema-mcp` gives your agent the recorded *why* — and the alternatives a project
|
|
5
|
+
already ruled out — cited to the source. For React, Kubernetes, and Rust out of
|
|
6
|
+
the box, and for your own repo with one command.
|
|
22
7
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```
|
|
8
|
+
[](https://www.npmjs.com/package/lema-mcp)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
[](https://modelcontextprotocol.io)
|
|
27
11
|
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
A local [MCP](https://modelcontextprotocol.io) server. No account, no database, no
|
|
13
|
+
network for your own repo — install it in 30 seconds and ask why a project decided
|
|
14
|
+
something, or whether the approach you're about to take was already rejected.
|
|
30
15
|
|
|
31
|
-
|
|
16
|
+
---
|
|
32
17
|
|
|
33
|
-
|
|
34
|
-
decided something** — over the recorded RFC/KEP decisions of React, Kubernetes,
|
|
35
|
-
and Rust, served from lema's public demo. No account, no token:
|
|
18
|
+
## ❌ Without lema
|
|
36
19
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
20
|
+
- The agent invents the **why** from training recall — fluently, and sometimes
|
|
21
|
+
wrongly. The rationale lives in RFC / KEP / PR threads; it was never in the code.
|
|
22
|
+
- It re-proposes the approach the maintainers **already rejected** two years ago —
|
|
23
|
+
because a rejected alternative leaves *no trace* in the source.
|
|
24
|
+
- Ask "was this ever ruled out?" and you get a confident guess, with no way to tell
|
|
25
|
+
a real ruling from a hallucinated one.
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
you already have the authed `lema` server from `init`, it's left untouched (that
|
|
43
|
-
server already serves these public tools). Reload your agent's MCP servers and
|
|
44
|
-
ask:
|
|
27
|
+
## ✅ With lema
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
- One **cited** answer from the project's actual recorded deliberation — every `[n]`
|
|
30
|
+
links to the RFC / PR where the call was made.
|
|
31
|
+
- A typed **`ruled_out`** verdict when a project already rejected your approach,
|
|
32
|
+
with the recorded reason *and* a pointer to where the docs say to do it instead.
|
|
33
|
+
- An honest **"no recorded ruling"** when the record is silent — which means
|
|
34
|
+
*unknown*, **not** *approved*. lema never fills the gap with a guess.
|
|
48
35
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`settled`); nothing local is registered. Ready for the same on *your own*
|
|
53
|
-
repo? `npx lema-mcp init` adds capture + enforcement.
|
|
36
|
+
> lema holds **reasoning** — why a decision was made, what was rejected — not API
|
|
37
|
+
> syntax or code samples. For those, reach for a docs tool. lema is the right place
|
|
38
|
+
> for *why*.
|
|
54
39
|
|
|
55
|
-
|
|
56
|
-
there are **no** relitigation/blast lenses (a cold import writes no
|
|
57
|
-
decision→decision edges) and no source-authored date. It's a curated three-repo
|
|
58
|
-
demo corpus, not analytics over your own graph.
|
|
40
|
+
---
|
|
59
41
|
|
|
60
|
-
##
|
|
42
|
+
## Try it in 30 seconds — no account
|
|
61
43
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
edit the guard inspects — and the killed option comes back **CLOSED**, with the
|
|
66
|
-
original reason attached:
|
|
44
|
+
```bash
|
|
45
|
+
npx lema-mcp try react # or: kubernetes · rust
|
|
46
|
+
```
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
48
|
+
That writes a read-only public server to your project's `.mcp.json`. Reload your
|
|
49
|
+
agent's MCP servers (in Claude Code: `/mcp`) and try the flagship tool,
|
|
50
|
+
**`check_approach`** — name a direction, get the recorded verdict:
|
|
71
51
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
enforced both ways. (That's the real output of `npx lema-mcp demo`, run against a
|
|
75
|
-
throwaway temp dir.)
|
|
52
|
+
```text
|
|
53
|
+
> "Let's add a delayMs prop to Suspense to debounce the fallback." (repo: react)
|
|
76
54
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
`LEMA_GUARD_MODE=ask` and a strong match prompts *you* before the edit; `off` is a
|
|
82
|
-
kill switch. Any error → it emits nothing and gets out of the way.
|
|
55
|
+
⛔ ruled_out — the React team considered and rejected this.
|
|
56
|
+
"<the recorded rationale, summarized — not a quote>" [1]
|
|
57
|
+
Where to look instead → https://react.dev/reference/react
|
|
58
|
+
[1] reactjs/rfcs#212
|
|
83
59
|
|
|
84
|
-
|
|
85
|
-
can commit, so your whole team's agents share the same memory through git. No key,
|
|
86
|
-
no LLM call on our side: your agent forms the decision; `lema-mcp` stores it and
|
|
87
|
-
serves it back.
|
|
60
|
+
> "I'll add a global event bus for cross-component communication." (repo: react)
|
|
88
61
|
|
|
89
|
-
|
|
62
|
+
◦ no_recorded_ruling — React's public record doesn't settle this.
|
|
63
|
+
(Unknown — not approved.)
|
|
64
|
+
```
|
|
90
65
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
The honest result is an **existence proof**, not "agents are wrong 58% of the
|
|
100
|
-
time":
|
|
101
|
-
|
|
102
|
-
- Of the six well-documented public decisions, only **one** cut against the 2026
|
|
103
|
-
frontier model (`node-fetch` → native `fetch`). On that one contrarian decision,
|
|
104
|
-
a blind agent re-proposed the killed library **58.3% of the time (14/24, N=24)**;
|
|
105
|
-
lema drove it to **0%** — matching the docs-preloaded arm *without carrying the
|
|
106
|
-
doc in context*.
|
|
107
|
-
- On the **five decisions the model already gets right**, lema stayed silent: **0%
|
|
108
|
-
re-proposal and 0% false-abstain** across 48 trials. The nagging / false-positive
|
|
109
|
-
failure people fear about enforcement didn't happen.
|
|
110
|
-
|
|
111
|
-
The honest caveats, stated plainly: it's a **single contrarian decision-type**
|
|
112
|
-
(node-fetch, N=24) — a solid existence proof, not breadth. And a public-repo
|
|
113
|
-
benchmark *structurally understates* enforcement's value: public decisions are
|
|
114
|
-
disproportionately the ones the model already absorbed in training (that's why
|
|
115
|
-
they're widespread enough to be documented). The decisions where enforcement
|
|
116
|
-
actually moves the needle are **proprietary, contrarian, recent, team-specific** —
|
|
117
|
-
which by construction aren't in any public repo. That's the capture-forward thesis:
|
|
118
|
-
enforcement catches the decisions where your team disagrees with the AI's defaults,
|
|
119
|
-
and gets out of the way everywhere else.
|
|
120
|
-
|
|
121
|
-
Full method, the result table, and every raw trial: [`./docs/enforcement-lift`](./docs/enforcement-lift).
|
|
66
|
+
Or just ask in plain language — *"why did React adopt Hooks over mixins?"* — and
|
|
67
|
+
get one cited answer, with an honest abstain when the record is silent.
|
|
68
|
+
|
|
69
|
+
Covered today: **React · Kubernetes · Rust**, served from lema's public API
|
|
70
|
+
(`api.lema.sh`). Tokenless. It's a curated three-project demo corpus — not
|
|
71
|
+
analytics over a graph you own.
|
|
72
|
+
|
|
73
|
+
---
|
|
122
74
|
|
|
123
75
|
## Install
|
|
124
76
|
|
|
125
|
-
`npx` needs only Node — no Go toolchain, no account
|
|
77
|
+
`npx` needs only Node — no Go toolchain, no account. Two commands cover both ways
|
|
78
|
+
to use lema:
|
|
126
79
|
|
|
127
80
|
```bash
|
|
128
|
-
npx lema-mcp
|
|
81
|
+
npx lema-mcp try react # read-only: ask React/Kubernetes/Rust why + what's ruled out
|
|
82
|
+
npx lema-mcp init # your repo: decision capture + the never-reopen guard
|
|
129
83
|
```
|
|
130
84
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
1. **`.mcp.json`** — registers the `lema` MCP server (preserving any servers
|
|
136
|
-
already there).
|
|
137
|
-
2. **`AGENTS.md`** — appends a short, managed capture-protocol block: *when you
|
|
138
|
-
settle a non-trivial decision call `record_decision` with what you chose and
|
|
139
|
-
rejected; before proposing a direction call `check_decided`; treat a CLOSED
|
|
140
|
-
result as binding.* This protocol is what actually drives capture — a hook
|
|
141
|
-
can't form the decision itself.
|
|
142
|
-
3. **`.claude/settings.json`** — installs **three hooks**: a PostToolUse commit
|
|
143
|
-
reminder (fires only on `git commit`), the PostToolUse **capture-nudge**
|
|
144
|
-
(`lema-mcp nudge`, prompts `record_decision` when you edit a dependency
|
|
145
|
-
manifest), and the PreToolUse **never-reopen guard** (`lema-mcp guard`, the
|
|
146
|
-
enforcement above).
|
|
85
|
+
Both are **non-destructive and idempotent** — they merge into existing config and
|
|
86
|
+
re-running changes nothing. `init` and `try` share the same `lema` server key; the
|
|
87
|
+
authed `init` server is a superset (it serves the public tools too), so the two
|
|
88
|
+
coexist and `try` never downgrades it.
|
|
147
89
|
|
|
148
|
-
|
|
149
|
-
|
|
90
|
+
<details>
|
|
91
|
+
<summary><b>Claude Code</b></summary>
|
|
150
92
|
|
|
151
|
-
|
|
93
|
+
Easiest — let lema write the config and hooks for you:
|
|
152
94
|
|
|
153
95
|
```bash
|
|
154
|
-
|
|
96
|
+
npx lema-mcp init # or: npx lema-mcp try react
|
|
155
97
|
```
|
|
156
98
|
|
|
157
|
-
Or
|
|
158
|
-
|
|
159
|
-
`init` installs:
|
|
99
|
+
Or add it by hand to `.mcp.json` (this gets the read + capture tools, but not the
|
|
100
|
+
guard/nudge hooks that `init` installs):
|
|
160
101
|
|
|
161
102
|
```json
|
|
162
103
|
{
|
|
@@ -165,144 +106,250 @@ Note this gets you the read + capture tools, but not the guard/nudge hooks that
|
|
|
165
106
|
}
|
|
166
107
|
}
|
|
167
108
|
```
|
|
109
|
+
</details>
|
|
168
110
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
`docs/architecture/decisions`, `architecture/decisions`, `adr`, `.adr`) and an
|
|
172
|
-
`openspec/` tree if present. You can also point it explicitly:
|
|
111
|
+
<details>
|
|
112
|
+
<summary><b>Cursor</b></summary>
|
|
173
113
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
114
|
+
Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"mcpServers": {
|
|
119
|
+
"lema": { "command": "npx", "args": ["-y", "lema-mcp@latest"] }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
177
122
|
```
|
|
178
123
|
|
|
179
|
-
|
|
180
|
-
regex), `--openspec-dir`, `--capture-file` (override the JSONL path), and
|
|
181
|
-
`--http`/`--port` (see *serve mode* below).
|
|
182
|
-
|
|
183
|
-
## The tools
|
|
184
|
-
|
|
185
|
-
Your agent calls these over MCP. In the standard server **eight are always on**;
|
|
186
|
-
`search_docs` / `get_doc` also register in local mode once a markdown tree is
|
|
187
|
-
indexed. (The `npx lema-mcp try` public-demo server runs a public-only subset —
|
|
188
|
-
just `why_decided` / `settled`.)
|
|
189
|
-
|
|
190
|
-
**Enforce + capture (the part read-only tools don't have):**
|
|
191
|
-
|
|
192
|
-
- **`record_decision`** — capture a decision you just settled: the chosen option,
|
|
193
|
-
the **rejected** alternatives (with why each was killed), optional rationale /
|
|
194
|
-
refs / constraint / consequence, and `supersedes` to retire an earlier one.
|
|
195
|
-
Rejected and superseded options come back CLOSED. Appends to
|
|
196
|
-
`.lema/decisions.jsonl`.
|
|
197
|
-
- **`check_decided`** — before proposing a direction, check whether it's already
|
|
198
|
-
decided and CLOSED. Matched off **both** the capture store **and the repo's own
|
|
199
|
-
ADRs**, so a documented decision stops a fresh agent even if it was never
|
|
200
|
-
captured live.
|
|
201
|
-
|
|
202
|
-
**Read (the entry point — query before you write code):**
|
|
203
|
-
|
|
204
|
-
- **`search_decisions`** — natural-language query → the most relevant atomic
|
|
205
|
-
claims (chosen / rejected / constraint / consequence) with their source ADR,
|
|
206
|
-
under a token budget. CLOSED flags surface here too.
|
|
207
|
-
- **`get_decision`** — one decision's full body, status, and typed edges.
|
|
208
|
-
- **`list_decisions`** — the decisions recorded in the repo, optionally by status.
|
|
209
|
-
- **`get_decision_graph`** — traverse typed edges (`supersedes`, `superseded_by`,
|
|
210
|
-
`depends_on`, `related_to`) to connected decisions.
|
|
211
|
-
- **`search_docs`** / **`get_doc`** *(local mode, when a doc tree is indexed)* —
|
|
212
|
-
sectioned, budgeted retrieval over the repo's project markdown (specs, READMEs,
|
|
213
|
-
agent instructions, ADR/openspec full text) so the agent reads the sections that
|
|
214
|
-
matter instead of whole files.
|
|
215
|
-
|
|
216
|
-
**Public demo (no account — `npx lema-mcp try <repo>`):**
|
|
217
|
-
|
|
218
|
-
- **`why_decided`** — ask why **React / Kubernetes / Rust** made a decision; one
|
|
219
|
-
cited answer over their recorded RFC/KEP decisions, surfacing status and the
|
|
220
|
-
ruled-out alternatives, with an honest abstain when the record is silent. The
|
|
221
|
-
abstain is also machine-readable (`record_silent: true`), and a grounded answer
|
|
222
|
-
carries `caveats` naming what the public demo does *not* cover. Tokenless.
|
|
223
|
-
- **`settled`** — before you propose a library / pattern / approach, check whether
|
|
224
|
-
one of those projects already ruled it out. Returns a typed `state`
|
|
225
|
-
(`settled` / `not_settled` / `unsure`) plus each governing decision's ref and
|
|
226
|
-
recorded reasoning; `not_settled` means *not on the record*, **not *approved***.
|
|
227
|
-
(`why_not_public` remains as a deprecated alias.)
|
|
124
|
+
For the no-account public demo instead, add the env block:
|
|
228
125
|
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"mcpServers": {
|
|
129
|
+
"lema": {
|
|
130
|
+
"command": "npx",
|
|
131
|
+
"args": ["-y", "lema-mcp@latest"],
|
|
132
|
+
"env": { "LEMA_MCP_MODE": "public", "LEMA_PUBLIC_REPO": "react-rfcs" }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
229
136
|
```
|
|
230
|
-
>
|
|
231
|
-
|
|
137
|
+
</details>
|
|
138
|
+
|
|
139
|
+
<details>
|
|
140
|
+
<summary><b>Claude Desktop</b></summary>
|
|
141
|
+
|
|
142
|
+
Settings → Developer → Edit Config, then add to `mcpServers`:
|
|
232
143
|
|
|
233
|
-
```
|
|
234
|
-
// illustrative shape — atom text/ids vary by repo
|
|
144
|
+
```json
|
|
235
145
|
{
|
|
236
|
-
"
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
"truncated": false
|
|
146
|
+
"mcpServers": {
|
|
147
|
+
"lema": {
|
|
148
|
+
"command": "npx",
|
|
149
|
+
"args": ["-y", "lema-mcp@latest"],
|
|
150
|
+
"env": { "LEMA_MCP_MODE": "public", "LEMA_PUBLIC_REPO": "react-rfcs" }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
244
153
|
}
|
|
245
154
|
```
|
|
155
|
+
</details>
|
|
246
156
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
every `search_decisions` response carries a self-reported `usage` block estimating
|
|
250
|
-
the tokens returned versus the source-document tokens for that call (the numbers
|
|
251
|
-
above are illustrative). On a large decision record that's a meaningful per-call
|
|
252
|
-
saving — but it's a local, self-measured estimate, not an audited benchmark, and
|
|
253
|
-
it's a side benefit. The reason to run lema is never-reopen, above.
|
|
254
|
-
|
|
255
|
-
## The subcommands
|
|
256
|
-
|
|
257
|
-
- **`init [dir]`** — wire a repo for capture (the three files / three hooks above);
|
|
258
|
-
idempotent. The same code path backs the lema Workbench GUI's "enable capture"
|
|
259
|
-
button.
|
|
260
|
-
- **`demo`** — a ~30-second never-reopen walkthrough using the real capture +
|
|
261
|
-
enforce path against a throwaway temp dir that's deleted afterward. Nothing is
|
|
262
|
-
written to your repo. This is the fastest way to see the CLOSED behavior.
|
|
263
|
-
- **`try <react|kubernetes|rust>`** — wire a **read-only public-demo** server
|
|
264
|
-
(`lema-try` in `.mcp.json`, public mode) so your agent can ask why those
|
|
265
|
-
projects decided things, no account. Distinct from `init` (which sets up
|
|
266
|
-
capture for *your* repo); the two coexist. Reload your agent's MCP servers
|
|
267
|
-
afterward.
|
|
268
|
-
- **`guard`** — the PreToolUse hook body: reads the tool-call payload on stdin,
|
|
269
|
-
emits a never-reopen permission decision on stdout. Advisory, fail-open, always
|
|
270
|
-
exits 0. `init` installs it; you don't call it directly.
|
|
271
|
-
- **`nudge`** — the PostToolUse hook body: on an Edit/Write/MultiEdit to a
|
|
272
|
-
dependency manifest (`go.mod`, `package.json`, `cargo.toml`, `pyproject.toml`,
|
|
273
|
-
`requirements.txt`, `gemfile`, `build.gradle`, `pom.xml`), emits a non-blocking
|
|
274
|
-
reminder to `record_decision`. Fail-open. Installed by `init`.
|
|
275
|
-
- **`serve`** (≡ the `--http` flag) — serve the same engine over localhost HTTP
|
|
276
|
-
(default `:4321`, `--port`) for the lema Workbench desktop GUI instead of stdio
|
|
277
|
-
MCP.
|
|
157
|
+
<details>
|
|
158
|
+
<summary><b>Windsurf</b></summary>
|
|
278
159
|
|
|
279
|
-
|
|
160
|
+
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
280
161
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"mcpServers": {
|
|
165
|
+
"lema": { "command": "npx", "args": ["-y", "lema-mcp@latest"] }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
</details>
|
|
288
170
|
|
|
289
|
-
|
|
171
|
+
<details>
|
|
172
|
+
<summary><b>VS Code (GitHub Copilot)</b></summary>
|
|
290
173
|
|
|
291
|
-
|
|
292
|
-
|
|
174
|
+
Add to `.vscode/mcp.json` — note VS Code uses the `servers` key:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"servers": {
|
|
179
|
+
"lema": { "command": "npx", "args": ["-y", "lema-mcp@latest"] }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
</details>
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary><b>Go install / pinned binary</b></summary>
|
|
293
187
|
|
|
294
188
|
```bash
|
|
295
|
-
|
|
189
|
+
go install github.com/lemahq/lema-mcp/cmd/lema-mcp@latest
|
|
296
190
|
```
|
|
297
191
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
192
|
+
For the public demo, set `LEMA_MCP_MODE=public` and `LEMA_PUBLIC_REPO=react-rfcs`
|
|
193
|
+
(`k8s-enhancements` · `rust-rfcs`). The public API URL is baked into the binary.
|
|
194
|
+
</details>
|
|
195
|
+
|
|
196
|
+
The public config sets only `LEMA_MCP_MODE` + `LEMA_PUBLIC_REPO`
|
|
197
|
+
(`react-rfcs` · `k8s-enhancements` · `rust-rfcs`) — the API URL is compiled in.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Two ways to use it
|
|
202
|
+
|
|
203
|
+
### 1. The public record — React, Kubernetes, Rust (read-only, no account)
|
|
204
|
+
|
|
205
|
+
Ask why a popular project decided something, or check whether a direction was
|
|
206
|
+
already rejected, over its recorded RFC/KEP deliberation. This is the `try` server.
|
|
207
|
+
|
|
208
|
+
### 2. Your own repo — capture + never-reopen (local, no account)
|
|
209
|
+
|
|
210
|
+
Most "context" tools are read-only — a nicer way to grep your docs. lema reads too,
|
|
211
|
+
but its real job is **never-reopen**:
|
|
212
|
+
|
|
213
|
+
- Your agent settles a choice → it calls **`record_decision`** with the option it
|
|
214
|
+
chose **and the alternatives it rejected, with why each was killed** (the part
|
|
215
|
+
that never survives into the code).
|
|
216
|
+
- Before anyone proposes a direction → **`check_decided`** returns the prior
|
|
217
|
+
decision if that option is **CLOSED**.
|
|
218
|
+
- On every edit, a **PreToolUse guard hook** (installed by `init`) reads the draft
|
|
219
|
+
change and surfaces a CLOSED decision *before* the dead option gets re-proposed —
|
|
220
|
+
enforced off both your captured decisions **and the repo's own ADRs**.
|
|
221
|
+
|
|
222
|
+
Decisions are captured to `.lema/decisions.jsonl` — a plain append-only file you
|
|
223
|
+
commit, so your whole team's agents share the same memory through git. No key, no
|
|
224
|
+
LLM call on our side: your agent forms the decision; lema stores it and serves it
|
|
225
|
+
back.
|
|
226
|
+
|
|
227
|
+
#### What never-reopen looks like
|
|
228
|
+
|
|
229
|
+
Your agent reaches for an option you already killed — and it comes back **CLOSED**,
|
|
230
|
+
with the original reason attached:
|
|
231
|
+
|
|
232
|
+
> ⛔ **CLOSED — do not propose "SWR":** no first-class mutation / cache
|
|
233
|
+
> invalidation — we'd hand-roll it *(decided 2026-06-04 · "Data fetching for the
|
|
234
|
+
> web app" · chose TanStack Query)*
|
|
235
|
+
|
|
236
|
+
So the agent surfaces the prior decision instead of re-litigating it. Supersede a
|
|
237
|
+
decision and the *previously chosen* option goes CLOSED too — enforced both ways.
|
|
238
|
+
(That's the real output of `npx lema-mcp demo`, run against a throwaway temp dir.)
|
|
239
|
+
|
|
240
|
+
The guard is **advisory and fail-open**: in its default `context` mode it injects
|
|
241
|
+
that note as a non-blocking nudge — it never hard-blocks and never auto-approves an
|
|
242
|
+
edit. `LEMA_GUARD_MODE=ask` prompts *you* on a strong match; `off` is a kill switch.
|
|
243
|
+
Any error → it emits nothing and gets out of the way.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Available tools
|
|
248
|
+
|
|
249
|
+
Your agent calls these over MCP.
|
|
250
|
+
|
|
251
|
+
### The public record (no account)
|
|
252
|
+
|
|
253
|
+
| Tool | What it does |
|
|
254
|
+
|------|--------------|
|
|
255
|
+
| **`check_approach`** ★ | Name an approach → a typed `ruled_out` verdict with the recorded **why** (cited) **and** a pointer to where the docs cover the supported path — or an honest `no_recorded_ruling`. The Fusion tool. |
|
|
256
|
+
| **`settled`** | Typed `state` (`settled` / `not_settled` / `unsure`) for a direction, plus each governing decision's ref and reasoning. `not_settled` means *not on the record*, **not approved**. |
|
|
257
|
+
| **`why_decided`** | One cited answer to "why did React / Kubernetes / Rust decide X?", surfacing status and ruled-out alternatives — with a machine-readable abstain (`record_silent: true`) when silent. (`why_not_public` is a deprecated alias.) |
|
|
258
|
+
|
|
259
|
+
### Your own repo
|
|
260
|
+
|
|
261
|
+
| Tool | What it does |
|
|
262
|
+
|------|--------------|
|
|
263
|
+
| **`record_decision`** | Capture a settled decision: the chosen option and the **rejected** alternatives (with why each was killed), plus rationale / refs / `supersedes`. Rejected and superseded options come back CLOSED. Append-only. |
|
|
264
|
+
| **`check_decided`** | Adjudicate one proposed direction against decisions already CLOSED → typed verdict (`ruled_out` / `not_ruled_out` / `incomplete` / `error`), off **both** your capture store **and** the repo's ADRs. |
|
|
265
|
+
| **`search_decisions`** | Natural-language query → the most relevant atomic claims (chosen / rejected / constraint / consequence) with their source ADR, under a token budget. |
|
|
266
|
+
| **`get_decision`** · **`list_decisions`** · **`get_decision_graph`** | One decision's full body; the list by status; traversal of typed edges (`supersedes`, `depends_on`, …). |
|
|
267
|
+
| **`search_docs`** · **`get_doc`** | Sectioned, budgeted retrieval over the repo's project markdown (local mode, once a doc tree is indexed) — the matching sections, not whole files. |
|
|
268
|
+
| **`ask`** | One cited, synthesized answer over your team's **hosted** decision graph (hosted mode). |
|
|
269
|
+
|
|
270
|
+
In your own repo the full server registers the read + capture tools (and the public
|
|
271
|
+
tools too); the `npx lema-mcp try` server runs the public subset only.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Why lema is different (the honest part)
|
|
276
|
+
|
|
277
|
+
lema's brand *is* its honesty — that's what makes a "why" tool trustworthy:
|
|
278
|
+
|
|
279
|
+
- **Abstain ≠ approval.** Silence is reported as silence. lema would rather say
|
|
280
|
+
"no recorded ruling" than manufacture one.
|
|
281
|
+
- **Cited, summarized — not quoted.** Answers are grounded in recorded decisions and
|
|
282
|
+
paraphrased ("the record indicates …"), each claim tied to a followable ref.
|
|
283
|
+
- **Local-first.** Capture and enforcement run entirely on your machine, in a file
|
|
284
|
+
you own. No key, no upload, no model call on our side.
|
|
285
|
+
- **No fabricated graph.** A cold import writes no decision→decision edges and no
|
|
286
|
+
source-authored dates; lema shows what's actually on the record, nothing it can't
|
|
287
|
+
stand behind.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Does enforcement change what the agent does?
|
|
292
|
+
|
|
293
|
+
We measured it on **two real public repos we didn't write** (Backstage, vite),
|
|
294
|
+
transcribing six of their documented decisions into `record_decision` format and
|
|
295
|
+
running the **real `lema-mcp guard` binary** on the agent's draft edits. 168 trials,
|
|
296
|
+
0 errors. The honest result is an **existence proof**, not "agents are wrong 58% of
|
|
297
|
+
the time":
|
|
298
|
+
|
|
299
|
+
- On the one decision that cut against the 2026 frontier model (`node-fetch` →
|
|
300
|
+
native `fetch`), a blind agent re-proposed the killed library **58.3%** of the
|
|
301
|
+
time (14/24); lema drove it to **0%** — matching a docs-preloaded arm *without*
|
|
302
|
+
carrying the doc in context.
|
|
303
|
+
- On the five decisions the model already gets right, lema stayed silent: **0%
|
|
304
|
+
re-proposal and 0% false-abstain** across 48 trials. No nagging.
|
|
305
|
+
|
|
306
|
+
A public-repo benchmark *understates* the value — public decisions are
|
|
307
|
+
disproportionately the ones the model already absorbed in training. The decisions
|
|
308
|
+
where enforcement moves the needle are proprietary, contrarian, recent,
|
|
309
|
+
team-specific. Full method and every raw trial:
|
|
310
|
+
[`./docs/enforcement-lift`](./docs/enforcement-lift).
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Configuration & privacy
|
|
315
|
+
|
|
316
|
+
- **`LEMA_GUARD_MODE`** — `context` (default, non-blocking), `ask` (prompt the human
|
|
317
|
+
on a strong match), or `off`.
|
|
318
|
+
- **`LEMA_DISABLE_QUERY_LOGGING=1`** — drop query text from the usage log entirely.
|
|
319
|
+
Otherwise queries are scrubbed for credential-shaped substrings before logging.
|
|
320
|
+
- **`LEMA_USAGE_LOG` / `LEMA_QUESTION_LOG` / `LEMA_GUARD_LOG`** — opt-in local log
|
|
321
|
+
files; all off unless set.
|
|
322
|
+
|
|
323
|
+
<details>
|
|
324
|
+
<summary><b>Subcommands & flags</b></summary>
|
|
325
|
+
|
|
326
|
+
- **`init [dir]`** — wire a repo for capture: registers the server in `.mcp.json`,
|
|
327
|
+
appends a managed capture-protocol block to `AGENTS.md`, and installs three hooks
|
|
328
|
+
(a commit reminder, the `nudge` capture prompt on dependency-manifest edits, and
|
|
329
|
+
the `guard` never-reopen check). Idempotent.
|
|
330
|
+
- **`try <react|kubernetes|rust>`** — wire the read-only public-demo server.
|
|
331
|
+
- **`demo`** — a ~30-second never-reopen walkthrough against a throwaway temp dir
|
|
332
|
+
(nothing written to your repo). The fastest way to see the CLOSED behavior.
|
|
333
|
+
- **`guard`** / **`nudge`** — the hook bodies `init` installs; advisory, fail-open,
|
|
334
|
+
always exit 0. You don't call them directly.
|
|
335
|
+
- **`serve`** (≡ `--http`, default `:4321`) — serve the same engine over localhost
|
|
336
|
+
HTTP for the lema Workbench desktop GUI instead of stdio MCP.
|
|
337
|
+
|
|
338
|
+
With no flags, lema auto-discovers a decisions directory (`docs/adr`, `doc/adr`,
|
|
339
|
+
`docs/adrs`, `docs/decisions`, `docs/architecture/decisions`,
|
|
340
|
+
`architecture/decisions`, `adr`, `.adr`) and an `openspec/` tree. Point it
|
|
341
|
+
explicitly with `--adr-dir`, `--repo github.com/org/name` (`GITHUB_TOKEN` for
|
|
342
|
+
private), `--ref`, `--pattern`, `--openspec-dir`, or `--capture-file`.
|
|
343
|
+
|
|
344
|
+
**Hosted retrieval (optional).** Set `LEMA_API_URL` + `LEMA_API_TOKEN` to point
|
|
345
|
+
`search_decisions` at hosted hybrid retrieval over your full decision layer
|
|
346
|
+
(search-only in the MVP). Capture and enforcement are always local.
|
|
347
|
+
</details>
|
|
348
|
+
|
|
349
|
+
---
|
|
303
350
|
|
|
304
351
|
## License
|
|
305
352
|
|
|
306
|
-
MIT. `lema-mcp` is the free, local wedge of [**lema**](https://lema.sh) — the
|
|
307
|
-
|
|
308
|
-
|
|
353
|
+
MIT. `lema-mcp` is the free, local wedge of [**lema**](https://lema.sh) — the system
|
|
354
|
+
of record for *why*. The hosted decision graph, the team why-surface, and the
|
|
355
|
+
manager-facing Intelligence layer are at [lema.sh](https://lema.sh).
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lema-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"mcpName": "io.github.lemahq/lema-mcp",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "MCP server that gives your coding agent the recorded why behind decisions — and what was already ruled out — cited. Check React, Rust, and Kubernetes out of the box, or capture and enforce never-reopen on your own repo. Local, no account.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"lema-mcp": "bin/lema-mcp.js"
|
|
8
8
|
},
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE"
|
|
14
14
|
},
|
|
15
15
|
"optionalDependencies": {
|
|
16
|
-
"lema-mcp-darwin-arm64": "0.
|
|
17
|
-
"lema-mcp-darwin-x64": "0.
|
|
18
|
-
"lema-mcp-linux-x64": "0.
|
|
19
|
-
"lema-mcp-linux-arm64": "0.
|
|
16
|
+
"lema-mcp-darwin-arm64": "0.10.1",
|
|
17
|
+
"lema-mcp-darwin-x64": "0.10.1",
|
|
18
|
+
"lema-mcp-linux-x64": "0.10.1",
|
|
19
|
+
"lema-mcp-linux-arm64": "0.10.1"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=18"
|
|
@@ -38,6 +38,11 @@
|
|
|
38
38
|
"decisions",
|
|
39
39
|
"ai",
|
|
40
40
|
"agent",
|
|
41
|
-
"context"
|
|
41
|
+
"context",
|
|
42
|
+
"rfc",
|
|
43
|
+
"react",
|
|
44
|
+
"rust",
|
|
45
|
+
"kubernetes",
|
|
46
|
+
"documentation"
|
|
42
47
|
]
|
|
43
48
|
}
|