compendium-mcp 0.1.3 → 0.5.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
@@ -80,6 +80,8 @@ Binary packaging details for maintainers: [npm/DISTRIBUTION.md](npm/DISTRIBUTION
80
80
  ## Community
81
81
 
82
82
  - [Contributing](CONTRIBUTING.md)
83
+ - [Changelog](CHANGELOG.md)
84
+ - [Architecture](docs/architecture.md)
83
85
  - [Code of Conduct](CODE_OF_CONDUCT.md)
84
86
  - [Security policy](SECURITY.md)
85
87
  - [Support](SUPPORT.md)
@@ -99,9 +101,9 @@ Single MCP tool: **`compendium`**. Choose the operation with `action`:
99
101
 
100
102
  | `action` | Purpose | Main fields |
101
103
  |----------|---------|-------------|
102
- | `filter` | Strip ANSI, boilerplate, whitespace; densify JSON; keep/drop regexes | `text`, `filter` |
103
- | `compress` | Dense representation of text/code/logs | `text`, `compress` |
104
- | `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 |
105
107
  | `summarize` | Hierarchical summary (conversation / file tree / outline) | `text`, `summarize` |
106
108
  | `summarize_smart` | Local-SLM dense summary (heuristic fallback if unset/fails) | `text`, `smart?`, `summarize?` |
107
109
  | `filter_relevant` | Query-aware keep of relevant lines (local SLM + heuristic fallback) | `text`, `query`, `smart?` |
@@ -109,19 +111,20 @@ Single MCP tool: **`compendium`**. Choose the operation with `action`:
109
111
  | `chunk` | Split into `cmp://` chunks (session-cached) | `text`, `chunk` |
110
112
  | `resolve` | Fetch chunk content by id | `id` (+ optional `map` / `text`) |
111
113
  | `count_tokens` | Measure tokens | `text` |
112
- | `stats` | Session savings + latency/bypass/backend telemetry | `reset?` |
114
+ | `stats` | Session savings + latency/bypass/backend telemetry | `reset?` — see playbook `stats-debug` |
113
115
  | `cache_store` | Park bulky payload outside the prompt | `text`, `cache` |
114
116
  | `cache_get` | Retrieve by key | `key` |
115
117
  | `cache_invalidate` | Drop one key or clear cache | `key?` |
116
- | `sanitize` | Redact secrets + neutralize IPI phrases | `text`, `sanitize?` |
117
- | `rerank` | BM25-rank candidates / chunks for a query | `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?` |
118
120
  | `brief` | Scan a workspace; pack a structured starter briefing + cache key | `query`, `brief?` (`root`, caps), optional `text` hint |
119
- | `catalog` | Short action (+ playbook) ads; prefer before guessing | _(none)_ |
121
+ | `catalog` | Short action (+ playbook) ads; prefer before guessing | _(none)_ — call first when unsure |
120
122
  | `help` | Usage notes for one action (default **compressed**; `force: true` → full) | `id`, `force?` |
121
- | `playbooks` | List bundled token-hygiene playbook ads | _(none)_ |
122
- | `playbook` | Load one playbook body (sanitized) | `id` |
123
- | `pack` | Zip text/files into a size-capped archive (cache and/or base64) | `text` or `items`, `pack?` |
124
- | `unpack` | Unpack zip with caps chunks (**never runs scripts**) | `key` or base64 `text`, `pack?` |
123
+ | `playbooks` | List playbook ads | _(none)_ |
124
+ | `playbook` | Load one playbook body | `id` |
125
+ | `pack` | Zip text/files into a bounded archive | `text` or `items`, `pack?` |
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?` — when smart/hybrid unexpectedly heuristic |
125
128
 
126
129
  ### Progressive disclosure (skills)
127
130
 
@@ -157,22 +160,26 @@ Response envelope: `{ "ok": true, "action": "filter", "result_json": "{...}" }`.
157
160
 
158
161
  ```
159
162
  assets/ # brand mark (SVG/PNG); baked into MCP icons via data URI
163
+ docs/ # architecture notes
164
+ examples/ # sample MCP tool-call JSON payloads
165
+ testdata/ # eval fixtures (noisy logs, cargo fail, …)
160
166
  src/
161
167
  main.rs # CLI: stdio | http
162
168
  lib.rs
163
169
  brand.rs # SEP-973 icons for serverInfo + tool
164
170
  config.rs # COMPENDIUM_* env config
165
- server.rs # MCP tool + resources handlers (rmcp)
171
+ server/ # MCP tool + resources + action handlers (rmcp)
166
172
  http.rs # Streamable HTTP/SSE (feature = "http")
167
173
  pipeline/
174
+ brief/ # workspace brief (walk / window / pack / synthesize)
168
175
  tokens.rs # heuristic or tiktoken BPE (feature = "real-tokens")
169
176
  filter.rs
170
177
  compress.rs
171
178
  summarize.rs
172
179
  smart.rs # summarize_smart + filter_relevant
173
- local_llm.rs # OpenAI-compatible local SLM client
180
+ local_llm.rs # OpenAI-compatible local SLM client (+ embed cache)
174
181
  chunk.rs # chunk + resolve
175
- cache.rs # session key/value cache
182
+ cache.rs # session key/value cache (+ optional disk / embed vectors)
176
183
  catalog.rs # action ads + help (progressive disclosure)
177
184
  playbook.rs # bundled / dir playbooks
178
185
  pack.rs # zip pack/unpack with size caps
@@ -183,6 +190,9 @@ playbooks/ # embedded skill-md playbooks
183
190
  tests/
184
191
  integration.rs
185
192
  e2e_smoke.rs # spawns binary, MCP handshake, tools + resources
193
+ eval_regression.rs # B1 heuristic quality + latency smoke
194
+ CHANGELOG.md
195
+ REPORT.md # design essay + Shipped/Next/Deferred roadmap
186
196
  ```
187
197
 
188
198
  ## Build
@@ -260,6 +270,11 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
260
270
  | `COMPENDIUM_HTTP_BIND` | `127.0.0.1:8788` | Default HTTP listen address |
261
271
  | `COMPENDIUM_LOCAL_LLM_URL` | _(unset)_ | OpenAI-compatible base URL (e.g. `http://127.0.0.1:11434/v1` or `http://127.0.0.1:13305/api/v1`). Enables smart actions. |
262
272
  | `COMPENDIUM_LOCAL_LLM_MODEL` | `Qwen3-4B-GGUF` | Model id on that server (Ollama: e.g. `qwen:latest`) |
273
+ | `COMPENDIUM_LOCAL_EMBED_MODEL` | _(same as chat)_ | Embeddings model for hybrid `rerank` / `brief` (e.g. `nomic-embed-text`) |
274
+ | `COMPENDIUM_HYBRID_ALPHA` | `0.55` | BM25 weight in hybrid score (0–1); remainder is embedding cosine |
275
+ | `COMPENDIUM_RERANK_CROSS_ENCODER` | _(off)_ | When `1`/`true`, `rerank` SLM-rescores top-N after BM25/hybrid |
276
+ | `COMPENDIUM_CROSS_ENCODER_TOP_N` | `16` | Candidates passed to cross-encoder (clamped 4–64) |
277
+ | `COMPENDIUM_AUDIT_PATH` | _(unset)_ | Append-only JSONL audit log (action metadata only; no payloads) |
263
278
  | `COMPENDIUM_LOCAL_LLM_API_KEY` | _(unset)_ | Optional bearer token for locked loopback servers |
264
279
  | `COMPENDIUM_LOCAL_LLM_TIMEOUT_SECS` | `120` | HTTP timeout (first model load can be slow) |
265
280
  | `COMPENDIUM_SIGNAL_MIN_CHARS` | `1000` | Bypass compress/summarize below this length (`0` disables) |
@@ -269,21 +284,23 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
269
284
  | `COMPENDIUM_ARCHIVE_MAX_UNCOMPRESSED` | `4194304` | Max total uncompressed bytes for pack/unpack |
270
285
  | `COMPENDIUM_ARCHIVE_MAX_FILES` | `50` | Max files per archive |
271
286
  | `COMPENDIUM_SKILL_TTL_MS` | `300000` | Soft TTL (ms) on skill `resources/read` responses |
287
+ | `COMPENDIUM_CACHE_DIR` | _(unset)_ | Persist session cache (chunks/cache keys) across restarts; default size cap 64 MiB |
288
+ | `COMPENDIUM_CACHE_MAX_BYTES` | _(unset / 64MiB with dir)_ | Soft cap on total cached payload bytes |
272
289
  | `RUST_LOG` | `compendium=info` | Logs on **stderr** only |
273
290
 
274
291
  ## Example tool calls
275
292
 
293
+ All calls use the single tool **`compendium`** with an `action` field.
294
+
276
295
  **Filter noisy terminal output**
277
296
 
278
297
  ```json
279
298
  {
280
- "name": "compendium_filter",
281
- "arguments": {
282
- "text": "\u001b[31mERROR\u001b[0m boom\n\n\nINFO ok",
283
- "options": {
284
- "strip_ansi": true,
285
- "keep_patterns": ["ERROR|WARN"]
286
- }
299
+ "action": "filter",
300
+ "text": "\u001b[31mERROR\u001b[0m boom\n\n\nINFO ok",
301
+ "filter": {
302
+ "strip_ansi": true,
303
+ "keep_patterns": ["ERROR|WARN"]
287
304
  }
288
305
  }
289
306
  ```
@@ -292,13 +309,11 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
292
309
 
293
310
  ```json
294
311
  {
295
- "name": "compendium_compress",
296
- "arguments": {
297
- "text": "...",
298
- "options": {
299
- "content_type": "log",
300
- "max_tokens": 512
301
- }
312
+ "action": "compress",
313
+ "text": "...",
314
+ "compress": {
315
+ "content_type": "log",
316
+ "max_tokens": 512
302
317
  }
303
318
  }
304
319
  ```
@@ -307,14 +322,12 @@ Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTrans
307
322
 
308
323
  ```json
309
324
  {
310
- "name": "compendium_chunk",
311
- "arguments": {
312
- "text": "... huge file ...",
313
- "options": {
314
- "source": "file:///path/to/doc.md",
315
- "chunk_tokens": 400,
316
- "overlap_tokens": 40
317
- }
325
+ "action": "chunk",
326
+ "text": "... huge file ...",
327
+ "chunk": {
328
+ "source": "file:///path/to/doc.md",
329
+ "chunk_tokens": 400,
330
+ "overlap_tokens": 40
318
331
  }
319
332
  }
320
333
  ```
@@ -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.1.3",
3
+ "version": "0.5.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.1.3",
42
- "compendium-mcp-darwin-x64": "0.1.3",
43
- "compendium-mcp-linux-x64": "0.1.3",
44
- "compendium-mcp-linux-arm64": "0.1.3",
45
- "compendium-mcp-win32-x64": "0.1.3"
41
+ "compendium-mcp-darwin-arm64": "0.5.0",
42
+ "compendium-mcp-darwin-x64": "0.5.0",
43
+ "compendium-mcp-linux-x64": "0.5.0",
44
+ "compendium-mcp-linux-x64-musl": "0.5.0",
45
+ "compendium-mcp-linux-arm64": "0.5.0",
46
+ "compendium-mcp-win32-x64": "0.5.0",
47
+ "compendium-mcp-win32-arm64": "0.5.0"
46
48
  },
47
49
  "preferUnplugged": true
48
50
  }