compendium-mcp 0.2.0 → 0.6.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.
package/README.md CHANGED
@@ -101,9 +101,9 @@ Single MCP tool: **`compendium`**. Choose the operation with `action`:
101
101
 
102
102
  | `action` | Purpose | Main fields |
103
103
  |----------|---------|-------------|
104
- | `filter` | Strip ANSI, boilerplate, whitespace; densify JSON; keep/drop regexes | `text`, `filter` |
105
- | `compress` | Dense representation of text/code/logs | `text`, `compress` |
106
- | `compress_output` | Domain-aware stdout/stderr scrub (git, cargo, npm, docker, …) | `text`, `output` |
104
+ | `filter` | Strip ANSI, boilerplate, whitespace; densify JSON; keep/drop regexes | `text`, `filter` — not for cargo/npm dumps (`compress_output`) |
105
+ | `compress` | Dense representation of text/code/logs | `text`, `compress` — soft inputs under ~1000 chars bypass unless `force` |
106
+ | `compress_output` | Domain-aware stdout/stderr scrub (git, cargo, npm, docker, …) | `text`, `output` — prefer when CLI domain is known |
107
107
  | `summarize` | Hierarchical summary (conversation / file tree / outline) | `text`, `summarize` |
108
108
  | `summarize_smart` | Local-SLM dense summary (heuristic fallback if unset/fails) | `text`, `smart?`, `summarize?` |
109
109
  | `filter_relevant` | Query-aware keep of relevant lines (local SLM + heuristic fallback) | `text`, `query`, `smart?` |
@@ -111,20 +111,20 @@ Single MCP tool: **`compendium`**. Choose the operation with `action`:
111
111
  | `chunk` | Split into `cmp://` chunks (session-cached) | `text`, `chunk` |
112
112
  | `resolve` | Fetch chunk content by id | `id` (+ optional `map` / `text`) |
113
113
  | `count_tokens` | Measure tokens | `text` |
114
- | `stats` | Session savings + latency/bypass/backend telemetry | `reset?` |
114
+ | `stats` | Session savings + latency/bypass/backend telemetry | `reset?` — see playbook `stats-debug` |
115
115
  | `cache_store` | Park bulky payload outside the prompt | `text`, `cache` |
116
116
  | `cache_get` | Retrieve by key | `key` |
117
117
  | `cache_invalidate` | Drop one key or clear cache | `key?` |
118
- | `sanitize` | Redact secrets + neutralize IPI phrases | `text`, `sanitize?` |
119
- | `rerank` | BM25 (+ optional loopback embeddings) rank candidates / chunks | `query`, `items` or `text` or chunk `map`, `rerank?` |
118
+ | `sanitize` | Redact secrets + neutralize IPI phrases | `text`, `sanitize?` — or `sanitize_input` |
119
+ | `rerank` | BM25 (+ optional loopback embeddings + opt-in SLM cross-encoder) rank candidates / chunks | `query`, `items` or `text` or chunk `map`, `rerank?` |
120
120
  | `brief` | Scan a workspace; pack a structured starter briefing + cache key | `query`, `brief?` (`root`, caps), optional `text` hint |
121
- | `catalog` | Short action (+ playbook) ads; prefer before guessing | _(none)_ |
121
+ | `catalog` | Short action (+ playbook) ads; prefer before guessing | _(none)_ — call first when unsure |
122
122
  | `help` | Usage notes for one action (default **compressed**; `force: true` → full) | `id`, `force?` |
123
123
  | `playbooks` | List playbook ads | _(none)_ |
124
124
  | `playbook` | Load one playbook body | `id` |
125
125
  | `pack` | Zip text/files into a bounded archive | `text` or `items`, `pack?` |
126
126
  | `unpack` | Unpack zip with size caps into chunks (never runs scripts) | `text` or `key`, `pack?` |
127
- | `llm_status` | Probe configured local LLM (models; `force` = chat ping) | `force?` |
127
+ | `llm_status` | Probe configured local LLM (models; `force` = chat ping) | `force?` — when smart/hybrid unexpectedly heuristic |
128
128
 
129
129
  ### Progressive disclosure (skills)
130
130
 
@@ -162,22 +162,24 @@ Response envelope: `{ "ok": true, "action": "filter", "result_json": "{...}" }`.
162
162
  assets/ # brand mark (SVG/PNG); baked into MCP icons via data URI
163
163
  docs/ # architecture notes
164
164
  examples/ # sample MCP tool-call JSON payloads
165
+ testdata/ # eval fixtures (noisy logs, cargo fail, …)
165
166
  src/
166
167
  main.rs # CLI: stdio | http
167
168
  lib.rs
168
169
  brand.rs # SEP-973 icons for serverInfo + tool
169
170
  config.rs # COMPENDIUM_* env config
170
- server.rs # MCP tool + resources handlers (rmcp)
171
+ server/ # MCP tool + resources + action handlers (rmcp)
171
172
  http.rs # Streamable HTTP/SSE (feature = "http")
172
173
  pipeline/
174
+ brief/ # workspace brief (walk / window / pack / synthesize)
173
175
  tokens.rs # heuristic or tiktoken BPE (feature = "real-tokens")
174
176
  filter.rs
175
177
  compress.rs
176
178
  summarize.rs
177
179
  smart.rs # summarize_smart + filter_relevant
178
- local_llm.rs # OpenAI-compatible local SLM client
180
+ local_llm.rs # OpenAI-compatible local SLM client (+ embed cache)
179
181
  chunk.rs # chunk + resolve
180
- cache.rs # session key/value cache (+ optional disk)
182
+ cache.rs # session key/value cache (+ optional disk / embed vectors)
181
183
  catalog.rs # action ads + help (progressive disclosure)
182
184
  playbook.rs # bundled / dir playbooks
183
185
  pack.rs # zip pack/unpack with size caps
@@ -188,6 +190,7 @@ playbooks/ # embedded skill-md playbooks
188
190
  tests/
189
191
  integration.rs
190
192
  e2e_smoke.rs # spawns binary, MCP handshake, tools + resources
193
+ eval_regression.rs # B1 heuristic quality + latency smoke
191
194
  CHANGELOG.md
192
195
  REPORT.md # design essay + Shipped/Next/Deferred roadmap
193
196
  ```
@@ -232,6 +235,12 @@ Same `command` / `args` / `env` as Cursor, in Claude’s MCP config file.
232
235
 
233
236
  ### Local Cargo binary (developers)
234
237
 
238
+ After code changes, rebuild and **reload MCP** so the live tool schema matches source (avoid stale `npx`/Release binaries during development):
239
+
240
+ ```bash
241
+ cargo build --release --features real-tokens,http
242
+ ```
243
+
235
244
  ```json
236
245
  {
237
246
  "mcpServers": {
@@ -269,6 +278,8 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
269
278
  | `COMPENDIUM_LOCAL_LLM_MODEL` | `Qwen3-4B-GGUF` | Model id on that server (Ollama: e.g. `qwen:latest`) |
270
279
  | `COMPENDIUM_LOCAL_EMBED_MODEL` | _(same as chat)_ | Embeddings model for hybrid `rerank` / `brief` (e.g. `nomic-embed-text`) |
271
280
  | `COMPENDIUM_HYBRID_ALPHA` | `0.55` | BM25 weight in hybrid score (0–1); remainder is embedding cosine |
281
+ | `COMPENDIUM_RERANK_CROSS_ENCODER` | _(off)_ | When `1`/`true`, `rerank` SLM-rescores top-N after BM25/hybrid |
282
+ | `COMPENDIUM_CROSS_ENCODER_TOP_N` | `16` | Candidates passed to cross-encoder (clamped 4–64) |
272
283
  | `COMPENDIUM_AUDIT_PATH` | _(unset)_ | Append-only JSONL audit log (action metadata only; no payloads) |
273
284
  | `COMPENDIUM_LOCAL_LLM_API_KEY` | _(unset)_ | Optional bearer token for locked loopback servers |
274
285
  | `COMPENDIUM_LOCAL_LLM_TIMEOUT_SECS` | `120` | HTTP timeout (first model load can be slow) |
@@ -279,23 +290,23 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
279
290
  | `COMPENDIUM_ARCHIVE_MAX_UNCOMPRESSED` | `4194304` | Max total uncompressed bytes for pack/unpack |
280
291
  | `COMPENDIUM_ARCHIVE_MAX_FILES` | `50` | Max files per archive |
281
292
  | `COMPENDIUM_SKILL_TTL_MS` | `300000` | Soft TTL (ms) on skill `resources/read` responses |
282
- | `COMPENDIUM_CACHE_DIR` | _(unset)_ | Persist session cache (chunks/cache keys) across restarts; default size cap 64 MiB |
293
+ | `COMPENDIUM_CACHE_DIR` | _(unset)_ | Persist session cache (chunks/cache keys) across restarts; default size cap 64 MiB. Multiple MCP processes may share one dir — **no** cross-process lock; TTL/eviction are best-effort. Prefer a dedicated dir per user/host. |
283
294
  | `COMPENDIUM_CACHE_MAX_BYTES` | _(unset / 64MiB with dir)_ | Soft cap on total cached payload bytes |
284
295
  | `RUST_LOG` | `compendium=info` | Logs on **stderr** only |
285
296
 
286
297
  ## Example tool calls
287
298
 
299
+ All calls use the single tool **`compendium`** with an `action` field.
300
+
288
301
  **Filter noisy terminal output**
289
302
 
290
303
  ```json
291
304
  {
292
- "name": "compendium_filter",
293
- "arguments": {
294
- "text": "\u001b[31mERROR\u001b[0m boom\n\n\nINFO ok",
295
- "options": {
296
- "strip_ansi": true,
297
- "keep_patterns": ["ERROR|WARN"]
298
- }
305
+ "action": "filter",
306
+ "text": "\u001b[31mERROR\u001b[0m boom\n\n\nINFO ok",
307
+ "filter": {
308
+ "strip_ansi": true,
309
+ "keep_patterns": ["ERROR|WARN"]
299
310
  }
300
311
  }
301
312
  ```
@@ -304,13 +315,11 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
304
315
 
305
316
  ```json
306
317
  {
307
- "name": "compendium_compress",
308
- "arguments": {
309
- "text": "...",
310
- "options": {
311
- "content_type": "log",
312
- "max_tokens": 512
313
- }
318
+ "action": "compress",
319
+ "text": "...",
320
+ "compress": {
321
+ "content_type": "log",
322
+ "max_tokens": 512
314
323
  }
315
324
  }
316
325
  ```
@@ -319,14 +328,12 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
319
328
 
320
329
  ```json
321
330
  {
322
- "name": "compendium_chunk",
323
- "arguments": {
324
- "text": "... huge file ...",
325
- "options": {
326
- "source": "file:///path/to/doc.md",
327
- "chunk_tokens": 400,
328
- "overlap_tokens": 40
329
- }
331
+ "action": "chunk",
332
+ "text": "... huge file ...",
333
+ "chunk": {
334
+ "source": "file:///path/to/doc.md",
335
+ "chunk_tokens": 400,
336
+ "overlap_tokens": 40
330
337
  }
331
338
  }
332
339
  ```
@@ -7,10 +7,13 @@
7
7
  * 1. Prefer optionalDependency platform packages (esbuild-style) — offline, fast.
8
8
  * 2. Fall back to downloading the matching GitHub Release asset into a local cache.
9
9
  * 3. Dev override: COMPENDIUM_BINARY or ./target/release/compendium.
10
+ * 4. Force key: COMPENDIUM_PLATFORM (e.g. linux-x64-musl).
10
11
  */
11
12
 
13
+ const fs = require('fs');
12
14
  const os = require('os');
13
15
  const path = require('path');
16
+ const { execSync } = require('child_process');
14
17
 
15
18
  /** @typedef {{ pkg: string, asset: string, rustTarget: string }} PlatformSpec */
16
19
 
@@ -31,6 +34,11 @@ const PLATFORMS = {
31
34
  asset: 'compendium-linux-x64',
32
35
  rustTarget: 'x86_64-unknown-linux-gnu',
33
36
  },
37
+ 'linux-x64-musl': {
38
+ pkg: 'compendium-mcp-linux-x64-musl',
39
+ asset: 'compendium-linux-x64-musl',
40
+ rustTarget: 'x86_64-unknown-linux-musl',
41
+ },
34
42
  'linux-arm64': {
35
43
  pkg: 'compendium-mcp-linux-arm64',
36
44
  asset: 'compendium-linux-arm64',
@@ -41,6 +49,11 @@ const PLATFORMS = {
41
49
  asset: 'compendium-win32-x64.exe',
42
50
  rustTarget: 'x86_64-pc-windows-msvc',
43
51
  },
52
+ 'win32-arm64': {
53
+ pkg: 'compendium-mcp-win32-arm64',
54
+ asset: 'compendium-win32-arm64.exe',
55
+ rustTarget: 'aarch64-pc-windows-msvc',
56
+ },
44
57
  };
45
58
 
46
59
  function packageVersion() {
@@ -56,15 +69,51 @@ function githubRepo() {
56
69
  );
57
70
  }
58
71
 
72
+ /** Best-effort musl detection (Alpine / static libc). Default is glibc. */
73
+ function isLinuxMusl() {
74
+ if (process.platform !== 'linux') return false;
75
+ try {
76
+ if (typeof process.report?.getReport === 'function') {
77
+ const report = process.report.getReport();
78
+ if (report?.header?.glibcVersionRuntime) return false;
79
+ }
80
+ } catch (_) {
81
+ /* ignore */
82
+ }
83
+ try {
84
+ if (fs.existsSync('/etc/alpine-release')) return true;
85
+ } catch (_) {
86
+ /* ignore */
87
+ }
88
+ try {
89
+ const out = execSync('ldd --version 2>&1 || true', {
90
+ encoding: 'utf8',
91
+ stdio: ['ignore', 'pipe', 'pipe'],
92
+ });
93
+ if (/musl/i.test(out)) return true;
94
+ } catch (_) {
95
+ /* ignore */
96
+ }
97
+ return false;
98
+ }
99
+
59
100
  /**
60
101
  * Normalize Node's process.platform + process.arch into our key.
102
+ * Override with COMPENDIUM_PLATFORM when needed (e.g. linux-x64-musl).
61
103
  * @returns {string}
62
104
  */
63
105
  function platformKey() {
106
+ const forced = (process.env.COMPENDIUM_PLATFORM || '').trim();
107
+ if (forced) return forced;
108
+
64
109
  const platform = process.platform;
65
110
  let arch = process.arch;
66
111
  // Rosetta / rare aliases
67
112
  if (arch === 'ia32') arch = 'x64';
113
+
114
+ if (platform === 'linux' && arch === 'x64' && isLinuxMusl()) {
115
+ return 'linux-x64-musl';
116
+ }
68
117
  return `${platform}-${arch}`;
69
118
  }
70
119
 
@@ -78,7 +127,7 @@ function currentPlatform() {
78
127
  const supported = Object.keys(PLATFORMS).join(', ');
79
128
  throw new Error(
80
129
  `Unsupported platform "${key}". Supported: ${supported}.\n` +
81
- `Set COMPENDIUM_BINARY to a local build, or open an issue for this target.`
130
+ `Set COMPENDIUM_BINARY to a local build, COMPENDIUM_PLATFORM to a known key, or open an issue for this target.`
82
131
  );
83
132
  }
84
133
  return spec;
@@ -89,7 +138,9 @@ function currentPlatform() {
89
138
  * @param {PlatformSpec} [spec]
90
139
  */
91
140
  function binaryName(spec = currentPlatform()) {
92
- return process.platform === 'win32' ? 'compendium.exe' : 'compendium';
141
+ return process.platform === 'win32' || spec.asset.endsWith('.exe')
142
+ ? 'compendium.exe'
143
+ : 'compendium';
93
144
  }
94
145
 
95
146
  /**
@@ -112,4 +163,5 @@ module.exports = {
112
163
  currentPlatform,
113
164
  binaryName,
114
165
  cacheDir,
166
+ isLinuxMusl,
115
167
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compendium-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.6.0",
4
4
  "description": "MCP server that compresses, summarizes, and filters context to minimize LLM token usage",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -38,11 +38,13 @@
38
38
  "prepare": "node npm/scripts/link-bins.js"
39
39
  },
40
40
  "optionalDependencies": {
41
- "compendium-mcp-darwin-arm64": "0.2.0",
42
- "compendium-mcp-darwin-x64": "0.2.0",
43
- "compendium-mcp-linux-x64": "0.2.0",
44
- "compendium-mcp-linux-arm64": "0.2.0",
45
- "compendium-mcp-win32-x64": "0.2.0"
41
+ "compendium-mcp-darwin-arm64": "0.6.0",
42
+ "compendium-mcp-darwin-x64": "0.6.0",
43
+ "compendium-mcp-linux-x64": "0.6.0",
44
+ "compendium-mcp-linux-x64-musl": "0.6.0",
45
+ "compendium-mcp-linux-arm64": "0.6.0",
46
+ "compendium-mcp-win32-x64": "0.6.0",
47
+ "compendium-mcp-win32-arm64": "0.6.0"
46
48
  },
47
49
  "preferUnplugged": true
48
50
  }