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 +49 -36
- package/npm/lib/platform.js +54 -2
- package/package.json +8 -6
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
|
|
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
|
|
122
|
-
| `playbook` | Load one playbook body
|
|
123
|
-
| `pack` | Zip text/files into a
|
|
124
|
-
| `unpack` | Unpack zip with caps
|
|
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
|
|
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
|
-
"
|
|
281
|
-
"
|
|
282
|
-
|
|
283
|
-
"
|
|
284
|
-
|
|
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
|
-
"
|
|
296
|
-
"
|
|
297
|
-
|
|
298
|
-
"
|
|
299
|
-
|
|
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
|
-
"
|
|
311
|
-
"
|
|
312
|
-
|
|
313
|
-
"
|
|
314
|
-
|
|
315
|
-
|
|
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
|
```
|
package/npm/lib/platform.js
CHANGED
|
@@ -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'
|
|
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.
|
|
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.
|
|
42
|
-
"compendium-mcp-darwin-x64": "0.
|
|
43
|
-
"compendium-mcp-linux-x64": "0.
|
|
44
|
-
"compendium-mcp-linux-
|
|
45
|
-
"compendium-mcp-
|
|
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
|
}
|