lema-mcp 0.6.0 → 0.8.0

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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/package.json +8 -5
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lema
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,305 @@
1
+ # lema-mcp
2
+
3
+ Your coding agent reasons well — it just doesn't know *your* system, and it keeps
4
+ re-proposing the options your team already ruled out. `lema-mcp` is a local
5
+ [MCP](https://modelcontextprotocol.io) server that fixes the second problem the
6
+ way no read-only context tool can: it **captures the decisions you make** — the
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**.
22
+
23
+ ```bash
24
+ npx lema-mcp demo # 30-second never-reopen walkthrough on a throwaway temp dir
25
+ npx lema-mcp init # wire this repo for capture + enforcement (idempotent)
26
+ ```
27
+
28
+ Then open the repo in your agent and ask *"why did we choose X?"* — or just let it
29
+ work, and watch it record decisions and get nudged off the ones you settled.
30
+
31
+ ## Try it on React, Kubernetes, or Rust — no account
32
+
33
+ Before it knows *your* repo, `lema-mcp` can already answer **why a popular project
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:
36
+
37
+ ```bash
38
+ npx lema-mcp try react # or: kubernetes · rust
39
+ ```
40
+
41
+ That writes a **read-only** `lema-try` server to `.mcp.json` (it coexists with a
42
+ captured `lema` server — it doesn't touch your repo). Reload your agent's MCP
43
+ servers and ask:
44
+
45
+ > *"why did React adopt Hooks over mixins?"* — or, before you reach for a pattern,
46
+ > *"was a global event bus ever ruled out in Kubernetes?"*
47
+
48
+ You get **one synthesized, cited answer** — each `[n]` links to its GitHub source
49
+ (the RFC/PR) where available — and an honest **"no recorded ruling"** when the
50
+ record is silent, instead of a confident guess. Two tools light up (`public_ask`,
51
+ `why_not_public`); nothing local is registered. Ready for the same on *your own*
52
+ repo? `npx lema-mcp init` adds capture + enforcement.
53
+
54
+ Grounded only in recorded decisions; claims are **summarized, not verbatim**;
55
+ there are **no** relitigation/blast lenses (a cold import writes no
56
+ decision→decision edges) and no source-authored date. It's a curated three-repo
57
+ demo corpus, not analytics over your own graph.
58
+
59
+ ## What never-reopen looks like
60
+
61
+ Your agent settles a choice and calls `record_decision` with the chosen option
62
+ and the rejected alternatives. Later, anyone's agent reaches for a dead option —
63
+ in a `check_decided` call, in a `search_decisions` result, or while drafting an
64
+ edit the guard inspects — and the killed option comes back **CLOSED**, with the
65
+ original reason attached:
66
+
67
+ > ⛔ **CLOSED — do not propose "SWR":** no first-class mutation / cache
68
+ > invalidation — we'd hand-roll it *(decided 2026-06-04 · "Data fetching for the
69
+ > web app" · chose TanStack Query)*
70
+
71
+ So the agent surfaces the prior decision instead of re-litigating it. Supersede a
72
+ decision and the *previously chosen* option goes CLOSED too — never-reopen,
73
+ enforced both ways. (That's the real output of `npx lema-mcp demo`, run against a
74
+ throwaway temp dir.)
75
+
76
+ The guard is **advisory and fail-open today**: in its default `context` mode it
77
+ injects that CLOSED note as a non-blocking nudge for the agent — it never emits an
78
+ `allow` (which would skip your normal Edit/Write confirmation on the very edit
79
+ it's flagging) and it never hard-blocks (`deny`) in v1. Opt into
80
+ `LEMA_GUARD_MODE=ask` and a strong match prompts *you* before the edit; `off` is a
81
+ kill switch. Any error → it emits nothing and gets out of the way.
82
+
83
+ Decisions are captured to `.lema/decisions.jsonl` — a plain append-only file you
84
+ can commit, so your whole team's agents share the same memory through git. No key,
85
+ no LLM call on our side: your agent forms the decision; `lema-mcp` stores it and
86
+ serves it back.
87
+
88
+ ## Does enforcement actually change what the agent does?
89
+
90
+ We measured it on **two real public repos we didn't write** (Backstage, vite),
91
+ transcribing six of their own documented decisions into the `record_decision`
92
+ format and running the **real `lema-mcp guard` binary** on the agent's draft edits.
93
+ Agent and an arm-blind judge were Claude Sonnet 4.6; 168 trials, 0 errors; a
94
+ deterministic code check agreed with the judge on 163/168 (the 5 diffs
95
+ hand-verified in lema's favor). Three arms: **blind** (no doc), **docs** (the
96
+ decision pre-loaded in context), **lema** (guard only, no doc in context).
97
+
98
+ The honest result is an **existence proof**, not "agents are wrong 58% of the
99
+ time":
100
+
101
+ - Of the six well-documented public decisions, only **one** cut against the 2026
102
+ frontier model (`node-fetch` → native `fetch`). On that one contrarian decision,
103
+ a blind agent re-proposed the killed library **58.3% of the time (14/24, N=24)**;
104
+ lema drove it to **0%** — matching the docs-preloaded arm *without carrying the
105
+ doc in context*.
106
+ - On the **five decisions the model already gets right**, lema stayed silent: **0%
107
+ re-proposal and 0% false-abstain** across 48 trials. The nagging / false-positive
108
+ failure people fear about enforcement didn't happen.
109
+
110
+ The honest caveats, stated plainly: it's a **single contrarian decision-type**
111
+ (node-fetch, N=24) — a solid existence proof, not breadth. And a public-repo
112
+ benchmark *structurally understates* enforcement's value: public decisions are
113
+ disproportionately the ones the model already absorbed in training (that's why
114
+ they're widespread enough to be documented). The decisions where enforcement
115
+ actually moves the needle are **proprietary, contrarian, recent, team-specific** —
116
+ which by construction aren't in any public repo. That's the capture-forward thesis:
117
+ enforcement catches the decisions where your team disagrees with the AI's defaults,
118
+ and gets out of the way everywhere else.
119
+
120
+ Full method, the result table, and every raw trial: [`./docs/enforcement-lift`](./docs/enforcement-lift).
121
+
122
+ ## Install
123
+
124
+ `npx` needs only Node — no Go toolchain, no account:
125
+
126
+ ```bash
127
+ npx lema-mcp init # one-time: wire this repo for capture + enforcement
128
+ ```
129
+
130
+ `init` is non-destructive and idempotent — existing config is merged, not
131
+ clobbered, and re-running changes nothing. It writes **three files** via five
132
+ idempotent steps:
133
+
134
+ 1. **`.mcp.json`** — registers the `lema` MCP server (preserving any servers
135
+ already there).
136
+ 2. **`AGENTS.md`** — appends a short, managed capture-protocol block: *when you
137
+ settle a non-trivial decision call `record_decision` with what you chose and
138
+ rejected; before proposing a direction call `check_decided`; treat a CLOSED
139
+ result as binding.* This protocol is what actually drives capture — a hook
140
+ can't form the decision itself.
141
+ 3. **`.claude/settings.json`** — installs **three hooks**: a PostToolUse commit
142
+ reminder (fires only on `git commit`), the PostToolUse **capture-nudge**
143
+ (`lema-mcp nudge`, prompts `record_decision` when you edit a dependency
144
+ manifest), and the PreToolUse **never-reopen guard** (`lema-mcp guard`, the
145
+ enforcement above).
146
+
147
+ `init` does **not** create `.lema/decisions.jsonl` — the capture store is created
148
+ lazily on the first `record_decision`.
149
+
150
+ Prefer Go, or want a pinned binary?
151
+
152
+ ```bash
153
+ go install github.com/lemahq/lema-mcp/cmd/lema-mcp@latest
154
+ ```
155
+
156
+ Or wire it by hand — add to your agent's MCP config (Claude Code `.mcp.json`).
157
+ Note this gets you the read + capture tools, but not the guard/nudge hooks that
158
+ `init` installs:
159
+
160
+ ```json
161
+ {
162
+ "mcpServers": {
163
+ "lema": { "command": "npx", "args": ["-y", "lema-mcp@latest"] }
164
+ }
165
+ }
166
+ ```
167
+
168
+ With no flags, `lema-mcp` auto-discovers a decisions directory in the working
169
+ directory (`docs/adr`, `doc/adr`, `docs/adrs`, `docs/decisions`,
170
+ `docs/architecture/decisions`, `architecture/decisions`, `adr`, `.adr`) and an
171
+ `openspec/` tree if present. You can also point it explicitly:
172
+
173
+ ```bash
174
+ lema-mcp --adr-dir docs/adr # a local directory (no account, no network)
175
+ lema-mcp --repo github.com/org/name # a public repo (GITHUB_TOKEN for private)
176
+ ```
177
+
178
+ Other flags: `--ref` (branch/ref for a remote `--repo`), `--pattern` (ADR filename
179
+ regex), `--openspec-dir`, `--capture-file` (override the JSONL path), and
180
+ `--http`/`--port` (see *serve mode* below).
181
+
182
+ ## The tools
183
+
184
+ Your agent calls these over MCP. In the standard server **eight are always on**;
185
+ `search_docs` / `get_doc` also register in local mode once a markdown tree is
186
+ indexed. (The `npx lema-mcp try` public-demo server runs a public-only subset —
187
+ just `public_ask` / `why_not_public`.)
188
+
189
+ **Enforce + capture (the part read-only tools don't have):**
190
+
191
+ - **`record_decision`** — capture a decision you just settled: the chosen option,
192
+ the **rejected** alternatives (with why each was killed), optional rationale /
193
+ refs / constraint / consequence, and `supersedes` to retire an earlier one.
194
+ Rejected and superseded options come back CLOSED. Appends to
195
+ `.lema/decisions.jsonl`.
196
+ - **`check_decided`** — before proposing a direction, check whether it's already
197
+ decided and CLOSED. Matched off **both** the capture store **and the repo's own
198
+ ADRs**, so a documented decision stops a fresh agent even if it was never
199
+ captured live.
200
+
201
+ **Read (the entry point — query before you write code):**
202
+
203
+ - **`search_decisions`** — natural-language query → the most relevant atomic
204
+ claims (chosen / rejected / constraint / consequence) with their source ADR,
205
+ under a token budget. CLOSED flags surface here too.
206
+ - **`get_decision`** — one decision's full body, status, and typed edges.
207
+ - **`list_decisions`** — the decisions recorded in the repo, optionally by status.
208
+ - **`get_decision_graph`** — traverse typed edges (`supersedes`, `superseded_by`,
209
+ `depends_on`, `related_to`) to connected decisions.
210
+ - **`search_docs`** / **`get_doc`** *(local mode, when a doc tree is indexed)* —
211
+ sectioned, budgeted retrieval over the repo's project markdown (specs, READMEs,
212
+ agent instructions, ADR/openspec full text) so the agent reads the sections that
213
+ matter instead of whole files.
214
+
215
+ **Public demo (no account — `npx lema-mcp try <repo>`):**
216
+
217
+ - **`public_ask`** — ask why **React / Kubernetes / Rust** made a decision; one
218
+ cited answer over their recorded RFC/KEP decisions, surfacing status and the
219
+ ruled-out alternatives, with an honest abstain when the record is silent.
220
+ Tokenless, over the public demo.
221
+ - **`why_not_public`** — before you propose a library / pattern / approach, check
222
+ whether one of those projects already ruled it out: a cited answer, or a plain
223
+ "no recorded decision against it" — which means *not on the record*, **not
224
+ *approved***.
225
+
226
+ ```
227
+ > search_decisions "why did we choose an MCP-first architecture?"
228
+ ```
229
+
230
+ ```jsonc
231
+ // illustrative shape — atom text/ids vary by repo
232
+ {
233
+ "repo": "docs/adr",
234
+ "claims": [
235
+ { "id": "16-2", "type": "chosen", "text": "One MCP server is the single surface agents call — all writes route through it.", "ref": "ADR-0016" },
236
+ { "id": "27-4", "type": "rejected", "text": "Folding inference into lema was rejected on the serve path: the agent reasoning over the atoms is the customer's own.", "ref": "ADR-0027" }
237
+ ],
238
+ "tokens_used": 211,
239
+ "usage": { "atoms_tokens": 211, "source_decisions": 2, "source_tokens": 1840, "tokens_saved": 1629, "compression_ratio": 8.7 },
240
+ "truncated": false
241
+ }
242
+ ```
243
+
244
+ A note on token cost: the read tools return tight, sourced atoms (default
245
+ ~1500-token budget, tunable with `max_tokens`) instead of whole documents, and
246
+ every `search_decisions` response carries a self-reported `usage` block estimating
247
+ the tokens returned versus the source-document tokens for that call (the numbers
248
+ above are illustrative). On a large decision record that's a meaningful per-call
249
+ saving — but it's a local, self-measured estimate, not an audited benchmark, and
250
+ it's a side benefit. The reason to run lema is never-reopen, above.
251
+
252
+ ## The subcommands
253
+
254
+ - **`init [dir]`** — wire a repo for capture (the three files / three hooks above);
255
+ idempotent. The same code path backs the lema Workbench GUI's "enable capture"
256
+ button.
257
+ - **`demo`** — a ~30-second never-reopen walkthrough using the real capture +
258
+ enforce path against a throwaway temp dir that's deleted afterward. Nothing is
259
+ written to your repo. This is the fastest way to see the CLOSED behavior.
260
+ - **`try <react|kubernetes|rust>`** — wire a **read-only public-demo** server
261
+ (`lema-try` in `.mcp.json`, public mode) so your agent can ask why those
262
+ projects decided things, no account. Distinct from `init` (which sets up
263
+ capture for *your* repo); the two coexist. Reload your agent's MCP servers
264
+ afterward.
265
+ - **`guard`** — the PreToolUse hook body: reads the tool-call payload on stdin,
266
+ emits a never-reopen permission decision on stdout. Advisory, fail-open, always
267
+ exits 0. `init` installs it; you don't call it directly.
268
+ - **`nudge`** — the PostToolUse hook body: on an Edit/Write/MultiEdit to a
269
+ dependency manifest (`go.mod`, `package.json`, `cargo.toml`, `pyproject.toml`,
270
+ `requirements.txt`, `gemfile`, `build.gradle`, `pom.xml`), emits a non-blocking
271
+ reminder to `record_decision`. Fail-open. Installed by `init`.
272
+ - **`serve`** (≡ the `--http` flag) — serve the same engine over localhost HTTP
273
+ (default `:4321`, `--port`) for the lema Workbench desktop GUI instead of stdio
274
+ MCP.
275
+
276
+ ## Configuration & privacy
277
+
278
+ - **`LEMA_GUARD_MODE`** — `context` (default, non-blocking nudge), `ask` (prompt
279
+ the human on a strong match), or `off` (kill switch).
280
+ - **`LEMA_DISABLE_QUERY_LOGGING=1`** — drop query text from the usage log entirely.
281
+ Otherwise queries are scrubbed for credential-shaped substrings before logging.
282
+ - **`LEMA_USAGE_LOG` / `LEMA_QUESTION_LOG` / `LEMA_GUARD_LOG`** — opt-in local log
283
+ files (tool usage, unanswered questions, guard fires) for measuring and
284
+ calibrating; all off unless set.
285
+
286
+ ## Hosted retrieval (optional)
287
+
288
+ By default everything is local and lexical. To point `search_decisions` at hosted
289
+ hybrid retrieval over your full decision layer, set two env vars — no other change:
290
+
291
+ ```bash
292
+ LEMA_API_URL=https://<your-lema-api> LEMA_API_TOKEN=<bearer> lema-mcp
293
+ ```
294
+
295
+ In hosted mode `search_decisions` runs against the atom layer over `POST /retrieve`;
296
+ this is **search-only** in the MVP, so `get_decision` / `list_decisions` /
297
+ `get_decision_graph` return a search-only error and the doc tools aren't
298
+ registered. Capture and enforcement (`record_decision` / `check_decided` / the
299
+ guard) are **always local**.
300
+
301
+ ## License
302
+
303
+ MIT. `lema-mcp` is the free, local wedge of [**lema**](https://lema.sh) — the
304
+ system of record for *why*. The hosted decision graph, the team why-surface, and
305
+ the manager-facing Intelligence layer are coming at [lema.sh](https://lema.sh).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lema-mcp",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Local, database-less MCP server that makes your repo's decisions — and what it ruled out — queryable by your coding agent, and lets the agent record new ones with enforced never-reopen.",
5
5
  "bin": {
6
6
  "lema-mcp": "bin/lema-mcp.js"
@@ -8,11 +8,14 @@
8
8
  "files": [
9
9
  "bin"
10
10
  ],
11
+ "scripts": {
12
+ "prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE"
13
+ },
11
14
  "optionalDependencies": {
12
- "lema-mcp-darwin-arm64": "0.6.0",
13
- "lema-mcp-darwin-x64": "0.6.0",
14
- "lema-mcp-linux-x64": "0.6.0",
15
- "lema-mcp-linux-arm64": "0.6.0"
15
+ "lema-mcp-darwin-arm64": "0.8.0",
16
+ "lema-mcp-darwin-x64": "0.8.0",
17
+ "lema-mcp-linux-x64": "0.8.0",
18
+ "lema-mcp-linux-arm64": "0.8.0"
16
19
  },
17
20
  "engines": {
18
21
  "node": ">=18"