compendium-mcp 0.6.3 → 0.7.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 +54 -351
- package/bin/run.js +32 -84
- package/npm/lib/platform.js +19 -118
- package/package.json +4 -18
package/README.md
CHANGED
|
@@ -4,32 +4,15 @@
|
|
|
4
4
|
<img src="assets/logo.svg" alt="Compendium" width="420" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
MCP server that **
|
|
7
|
+
MCP server that **shrinks noisy context before it reaches the model**: filter logs, compress bulky text/JSON, redact secrets, chunk huge files, and keep only what a question needs.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
One tool (`compendium`), one `action` field, deterministic heuristics that need **no model**. An optional local Ollama model improves the two "smart" actions.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Install (Cursor / Claude Desktop)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Requires Node.js 18+. No Rust toolchain needed — the launcher downloads a prebuilt binary once.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|-----------|------|
|
|
17
|
-
| Unsure which action | `catalog` → `help` + `id` (or read `cmp://skill/…`) |
|
|
18
|
-
| New task in a repo | `brief` with a short `query` |
|
|
19
|
-
| Noisy terminal / CLI dump | `filter` (generic) or `compress_output` (cargo/npm/docker/git/…) |
|
|
20
|
-
| Bulky text/JSON to densify | `compress` (small inputs bypass unless `force`) |
|
|
21
|
-
| Untrusted paste / secrets / IPI | `sanitize` (or `sanitize_input: true` on the next action) |
|
|
22
|
-
| Guided recipe | `playbooks` → `playbook` |
|
|
23
|
-
|
|
24
|
-
Heuristic paths work with **no local model**. Optional loopback LLM improves `summarize_smart` / hybrid `rerank` / smart `filter_relevant`.
|
|
25
|
-
|
|
26
|
-
## Quick start (Cursor)
|
|
27
|
-
|
|
28
|
-
You need **Node.js 18+**. Compendium itself arrives via npm — no Rust install required.
|
|
29
|
-
|
|
30
|
-
### 1. Add the MCP server
|
|
31
|
-
|
|
32
|
-
Open Cursor MCP settings (`~/.cursor/mcp.json` or the project `.cursor/mcp.json`) and add:
|
|
15
|
+
Add to `~/.cursor/mcp.json` (or the project's `.cursor/mcp.json`, or Claude Desktop's MCP config):
|
|
33
16
|
|
|
34
17
|
```json
|
|
35
18
|
{
|
|
@@ -42,113 +25,44 @@ Open Cursor MCP settings (`~/.cursor/mcp.json` or the project `.cursor/mcp.json`
|
|
|
42
25
|
}
|
|
43
26
|
```
|
|
44
27
|
|
|
45
|
-
|
|
28
|
+
Reload MCP. You should see a single tool named `compendium`. That's it.
|
|
46
29
|
|
|
47
|
-
|
|
30
|
+
Prebuilt binaries: macOS (arm64, x64), Linux (x64, arm64), Windows (x64). Other platforms: `cargo build --release` and set `COMPENDIUM_BINARY=/path/to/compendium` in the server `env`.
|
|
48
31
|
|
|
49
|
-
###
|
|
32
|
+
### Optional: smarter summaries with Ollama
|
|
50
33
|
|
|
51
|
-
|
|
34
|
+
`summarize_smart` and `filter_relevant` use a small local model when one is configured; otherwise they fall back to heuristics. One command sets it up:
|
|
52
35
|
|
|
53
36
|
```bash
|
|
54
37
|
npx -y compendium-mcp setup-ollama --write-mcp
|
|
55
38
|
```
|
|
56
39
|
|
|
57
|
-
|
|
40
|
+
It detects (or `--install`s) [Ollama](https://ollama.com/), pulls `qwen2.5:3b`, probes `http://127.0.0.1:11434/v1`, and writes `COMPENDIUM_LOCAL_LLM_URL` / `COMPENDIUM_LOCAL_LLM_MODEL` into `~/.cursor/mcp.json` (`--project` for `.cursor/mcp.json`). Reload MCP and call `{"action":"llm_status"}` — `reachable: true` means smart actions will report `backend: "local_llm"`.
|
|
58
41
|
|
|
59
|
-
|
|
60
|
-
npx -y compendium-mcp setup-ollama --help
|
|
61
|
-
npx -y compendium-mcp setup-ollama --install --write-mcp --project
|
|
62
|
-
npx -y compendium-mcp setup-ollama --dry-run --json
|
|
63
|
-
```
|
|
42
|
+
Any OpenAI-compatible **loopback** server works (llama.cpp, Lemonade, …): set the two env vars yourself. Remote hosts are rejected on purpose.
|
|
64
43
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Manual / Lemonade / custom models: set `COMPENDIUM_LOCAL_LLM_URL` yourself (must stay on **localhost** — Compendium blocks remote hosts on purpose). See [Environment](#environment).
|
|
68
|
-
|
|
69
|
-
**Notes**
|
|
70
|
-
|
|
71
|
-
- Package name on npm is **`compendium-mcp`** (`compendium` was already taken). The CLI binary name is still `compendium`.
|
|
72
|
-
- First Ollama reply can be slow while the model loads; later calls are faster.
|
|
73
|
-
- Other local OpenAI-compatible servers work the same way (e.g. Lemonade `http://127.0.0.1:13305/api/v1`). See [Environment](#environment).
|
|
74
|
-
|
|
75
|
-
Smoke-check from a terminal (any folder **except** this git repo root is fine):
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
npx -y compendium-mcp --help
|
|
79
|
-
```
|
|
44
|
+
## Actions
|
|
80
45
|
|
|
81
|
-
|
|
46
|
+
| `action` | Use it for | Main fields |
|
|
47
|
+
|----------|------------|-------------|
|
|
48
|
+
| `filter` | Strip ANSI/boilerplate, collapse whitespace, keep/drop regex lines | `text`, `filter?`, `query?` |
|
|
49
|
+
| `compress_output` | Domain-aware scrub of cargo / npm / docker / git / kubectl output | `text`, `output?` |
|
|
50
|
+
| `compress` | Dense rewrite of bulky text, code, or JSON | `text`, `compress?` |
|
|
51
|
+
| `summarize` | Outline / conversation / file-tree summary | `text`, `summarize?` |
|
|
52
|
+
| `summarize_smart` | Local-model summary (heuristic fallback) | `text`, `smart?` |
|
|
53
|
+
| `filter_relevant` | Keep only lines relevant to a question (local model or BM25) | `text`, `query` |
|
|
54
|
+
| `sanitize` | Redact secrets, neutralize prompt-injection phrases | `text`, `sanitize?` |
|
|
55
|
+
| `rerank` | BM25-rank candidates for a query | `query`, `items` / `text` / chunk `map` |
|
|
56
|
+
| `chunk` / `resolve` | Split a huge corpus into `cmp://` chunks; fetch one by id | `text`, `chunk?` / `id` |
|
|
57
|
+
| `cache_store` / `cache_get` / `cache_invalidate` | Park a blob outside the prompt under a short key | `text`, `cache?` / `key` |
|
|
58
|
+
| `count_tokens` | Measure size | `text` |
|
|
59
|
+
| `stats` | Session savings per action | `reset?` |
|
|
60
|
+
| `llm_status` | Is the local model configured and reachable? | `force?` |
|
|
61
|
+
| `help` | List actions, or fields + example for one action | `id?` |
|
|
82
62
|
|
|
83
|
-
|
|
63
|
+
Every response is `{ "ok": true, "action": "filter", "result_json": "{…}" }` — parse `result_json` for the action payload. Add `"sanitize_input": true` to any text action to scrub secrets first. `compress` / `summarize` leave inputs under 1000 characters untouched unless `force: true`.
|
|
84
64
|
|
|
85
|
-
|
|
86
|
-
- [Changelog](CHANGELOG.md)
|
|
87
|
-
- [Architecture](docs/architecture.md)
|
|
88
|
-
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
89
|
-
- [Security policy](SECURITY.md)
|
|
90
|
-
- [Support](SUPPORT.md)
|
|
91
|
-
|
|
92
|
-
## Transports
|
|
93
|
-
|
|
94
|
-
| Mode | Command | Notes |
|
|
95
|
-
|------|---------|-------|
|
|
96
|
-
| **stdio** (default) | `compendium` / `compendium stdio` | Cursor / Claude Desktop — dual-compat (legacy initialize or modern connect) |
|
|
97
|
-
| **Streamable HTTP** | `compendium http [BIND]` | Requires `--features http`. Endpoint: `http://{bind}/mcp`. Sessionless (`2026-07-28`); JSON preferred, SSE fallback |
|
|
98
|
-
| **Ollama setup** | `compendium setup-ollama` | Not an MCP transport — CLI helper (`--write-mcp`, `--dry-run`, `--json`). Alias: `compendium ollama` |
|
|
99
|
-
|
|
100
|
-
Default HTTP bind: `127.0.0.1:8788` (override with arg or `COMPENDIUM_HTTP_BIND`). App cache (`COMPENDIUM_CACHE_DIR`) is not an MCP session — set it for multi-request HTTP. See playbook `http-transport`.
|
|
101
|
-
|
|
102
|
-
## Tools
|
|
103
|
-
|
|
104
|
-
Single MCP tool: **`compendium`**. Choose the operation with `action`. Prefer the [Why / when](#why--when-to-use-compendium) table for the first call; use the full list below only when you need a specific field.
|
|
105
|
-
|
|
106
|
-
| `action` | Purpose | Main fields |
|
|
107
|
-
|----------|---------|-------------|
|
|
108
|
-
| `filter` | Strip ANSI, boilerplate, whitespace; densify JSON; keep/drop regexes | `text`, `filter` — not for cargo/npm dumps (`compress_output`) |
|
|
109
|
-
| `compress` | Dense representation of text/code/logs | `text`, `compress` — soft inputs under ~1000 chars bypass unless `force` |
|
|
110
|
-
| `compress_output` | Domain-aware stdout/stderr scrub (git, cargo, npm, docker, …) | `text`, `output` — prefer when CLI domain is known |
|
|
111
|
-
| `summarize` | Hierarchical summary (conversation / file tree / outline) | `text`, `summarize` |
|
|
112
|
-
| `summarize_smart` | Local-SLM dense summary (heuristic fallback if unset/fails) | `text`, `smart?`, `summarize?` |
|
|
113
|
-
| `filter_relevant` | Query-aware keep of relevant lines (local SLM + heuristic fallback) | `text`, `query`, `smart?` |
|
|
114
|
-
| `prune_history` | Drop filler / compress older chat turns | `text` or `messages`, `prune` |
|
|
115
|
-
| `chunk` | Split into `cmp://` chunks (session-cached) | `text`, `chunk` |
|
|
116
|
-
| `resolve` | Fetch chunk content by id | `id` (+ optional `map` / `text`) |
|
|
117
|
-
| `count_tokens` | Measure tokens | `text` |
|
|
118
|
-
| `stats` | Session savings + latency/bypass/backend telemetry | `reset?` — see playbook `stats-debug` |
|
|
119
|
-
| `cache_store` | Park bulky payload outside the prompt | `text`, `cache` |
|
|
120
|
-
| `cache_get` | Retrieve by key | `key` |
|
|
121
|
-
| `cache_invalidate` | Drop one key or clear cache | `key?` |
|
|
122
|
-
| `sanitize` | Redact secrets + neutralize IPI phrases | `text`, `sanitize?` — or `sanitize_input` |
|
|
123
|
-
| `rerank` | BM25 (+ optional loopback embeddings + opt-in SLM cross-encoder) rank candidates / chunks | `query`, `items` or `text` or chunk `map`, `rerank?` |
|
|
124
|
-
| `brief` | Scan a workspace; pack a structured starter briefing + cache key | `query`, `brief?` (`root`, caps), optional `text` hint |
|
|
125
|
-
| `catalog` | Short action (+ playbook) ads; prefer before guessing | _(none)_ — call first when unsure |
|
|
126
|
-
| `help` | Usage notes for one action (default **compressed**; `force: true` → full) | `id`, `force?` |
|
|
127
|
-
| `playbooks` | List playbook ads | _(none)_ |
|
|
128
|
-
| `playbook` | Load one playbook body | `id` |
|
|
129
|
-
| `pack` | Zip text/files into a bounded archive | `text` or `items`, `pack?` |
|
|
130
|
-
| `unpack` | Unpack zip with size caps into chunks (never runs scripts) | `text` or `key`, `pack?` |
|
|
131
|
-
| `llm_status` | Probe configured local LLM (models; `force` = chat ping) | `force?` — when smart/hybrid unexpectedly heuristic |
|
|
132
|
-
|
|
133
|
-
### Progressive disclosure (skills)
|
|
134
|
-
|
|
135
|
-
Tool description/instructions stay thin. Discover details on demand:
|
|
136
|
-
|
|
137
|
-
- **Tool bridge:** `action=catalog` → `action=help` with `id`, or `playbooks` → `playbook`
|
|
138
|
-
- **MCP resources:** `resources/list` / `resources/read` on:
|
|
139
|
-
- `cmp://skill/index` — JSON index of actions + playbooks
|
|
140
|
-
- `cmp://skill/action/{name}` — full action help (markdown)
|
|
141
|
-
- `cmp://skill/playbook/{id}` — playbook body
|
|
142
|
-
|
|
143
|
-
Bundled playbooks live under [`playbooks/`](playbooks/). Override/extend with `COMPENDIUM_PLAYBOOKS_DIR` (same `id` wins). Archives honor `COMPENDIUM_ARCHIVE_MAX_BYTES` / `_UNCOMPRESSED` / `_FILES` (defaults 2 MiB / 4 MiB / 50).
|
|
144
|
-
|
|
145
|
-
Optional on most text actions: `sanitize_input: true` scrubs before processing. Soft payloads under `COMPENDIUM_SIGNAL_MIN_CHARS` (default 1000) bypass `compress` / `summarize` / `summarize_smart` unless `force: true`.
|
|
146
|
-
|
|
147
|
-
`filter` accepts optional `query` (top-level or `filter.query`) for BM25 line keep. `prune_history` supports `prune.strategy: "afm"` (Critical / Thematic / Distant tiers; distant blob cached for `cache_get`).
|
|
148
|
-
|
|
149
|
-
`brief` walks `brief.root` (default: process cwd) with `.gitignore` / `.ignore`, BM25-ranks paths/chunks, window-reads oversized files (not head-truncate), and returns a structured `briefing`: **Task / Status / Evidence / Caveats / Sources / Read next**, plus `cache_key`. Status uses a local SLM when `COMPENDIUM_LOCAL_LLM_URL` is set (`backend: local_llm`); otherwise heuristic bullets. Caveats flag truncated files and docs older than selected code. **Read next** includes source paths plus suggested `cmp://skill/playbook/…` / action URIs. Optional `COMPENDIUM_BRIEF_ROOT` restricts allowed roots. Briefings are sanitized by default.
|
|
150
|
-
|
|
151
|
-
Example — noisy log (canonical first call after install):
|
|
65
|
+
Example:
|
|
152
66
|
|
|
153
67
|
```json
|
|
154
68
|
{
|
|
@@ -158,256 +72,45 @@ Example — noisy log (canonical first call after install):
|
|
|
158
72
|
}
|
|
159
73
|
```
|
|
160
74
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
Response envelope: `{ "ok": true, "action": "filter", "result_json": "{...}" }`. Parse `result_json` as JSON for the action-specific payload.
|
|
164
|
-
|
|
165
|
-
## Project layout
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
assets/ # brand mark (SVG/PNG); baked into MCP icons via data URI
|
|
169
|
-
docs/ # architecture notes
|
|
170
|
-
examples/ # sample MCP tool-call JSON payloads
|
|
171
|
-
testdata/ # eval fixtures (logs, audit, PR JSON, untrusted paste, …)
|
|
172
|
-
src/
|
|
173
|
-
main.rs # CLI: stdio | http | setup-ollama
|
|
174
|
-
setup_ollama.rs # `compendium setup-ollama` (detect/pull/probe/write MCP env)
|
|
175
|
-
lib.rs
|
|
176
|
-
brand.rs # SEP-973 icons for serverInfo + tool
|
|
177
|
-
config.rs # COMPENDIUM_* env config
|
|
178
|
-
server/ # MCP tool + resources + action handlers (rmcp)
|
|
179
|
-
http.rs # Streamable HTTP, sessionless (feature = "http")
|
|
180
|
-
pipeline/
|
|
181
|
-
brief/ # workspace brief (walk / window / pack / synthesize)
|
|
182
|
-
tokens.rs # heuristic or tiktoken BPE (feature = "real-tokens")
|
|
183
|
-
filter.rs
|
|
184
|
-
compress.rs
|
|
185
|
-
summarize.rs
|
|
186
|
-
smart.rs # summarize_smart + filter_relevant
|
|
187
|
-
local_llm.rs # OpenAI-compatible local SLM client (+ embed cache)
|
|
188
|
-
chunk.rs # chunk + resolve
|
|
189
|
-
cache.rs # session key/value cache (+ optional disk / embed vectors)
|
|
190
|
-
catalog.rs # action ads + help (progressive disclosure)
|
|
191
|
-
playbook.rs # bundled / dir playbooks
|
|
192
|
-
pack.rs # zip pack/unpack with size caps
|
|
193
|
-
stats.rs # session savings counters
|
|
194
|
-
prune.rs # conversation history pruning
|
|
195
|
-
output.rs # domain-aware compress_output
|
|
196
|
-
playbooks/ # embedded skill-md playbooks
|
|
197
|
-
tests/
|
|
198
|
-
integration.rs
|
|
199
|
-
e2e_smoke.rs # spawns binary, MCP handshake, tools + resources
|
|
200
|
-
eval_regression.rs # B1 heuristic quality + latency smoke
|
|
201
|
-
CHANGELOG.md
|
|
202
|
-
REPORT.md # design essay + Shipped (A–C) / Next ops / Deferred roadmap
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
## Build
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
# Default: heuristic tokens + stdio only
|
|
209
|
-
cargo build --release
|
|
210
|
-
|
|
211
|
-
# Exact BPE token counts (tiktoken-rs)
|
|
212
|
-
cargo build --release --features real-tokens
|
|
213
|
-
|
|
214
|
-
# Streamable HTTP transport
|
|
215
|
-
cargo build --release --features http
|
|
216
|
-
|
|
217
|
-
# Everything
|
|
218
|
-
cargo build --release --features real-tokens,http
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
Binary: `target/release/compendium`
|
|
222
|
-
|
|
223
|
-
## Configure (advanced)
|
|
224
|
-
|
|
225
|
-
The [Quick start](#quick-start-cursor) config is enough for most people. Extra options:
|
|
226
|
-
|
|
227
|
-
### Claude Desktop
|
|
228
|
-
|
|
229
|
-
Same `command` / `args` / `env` as Cursor, in Claude’s MCP config file.
|
|
230
|
-
|
|
231
|
-
### Optional tuning env
|
|
232
|
-
|
|
233
|
-
```json
|
|
234
|
-
"env": {
|
|
235
|
-
"RUST_LOG": "compendium=info",
|
|
236
|
-
"COMPENDIUM_DEFAULT_MAX_TOKENS": "2048",
|
|
237
|
-
"COMPENDIUM_TOKENIZER": "cl100k_base",
|
|
238
|
-
"COMPENDIUM_LOCAL_LLM_URL": "http://127.0.0.1:11434/v1",
|
|
239
|
-
"COMPENDIUM_LOCAL_LLM_MODEL": "qwen2.5:3b",
|
|
240
|
-
"COMPENDIUM_LOCAL_EMBED_MODEL": "nomic-embed-text"
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
### Local Cargo binary (developers)
|
|
245
|
-
|
|
246
|
-
After code changes, rebuild and **reload MCP** so the live tool schema matches source (avoid stale `npx`/Release binaries during development):
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
cargo build --release --features real-tokens,http
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
```json
|
|
253
|
-
{
|
|
254
|
-
"mcpServers": {
|
|
255
|
-
"compendium": {
|
|
256
|
-
"command": "/absolute/path/to/Compendium/target/release/compendium",
|
|
257
|
-
"env": {
|
|
258
|
-
"RUST_LOG": "compendium=info",
|
|
259
|
-
"COMPENDIUM_DEFAULT_MAX_TOKENS": "2048"
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Remote / sidecar (HTTP)
|
|
267
|
-
|
|
268
|
-
```bash
|
|
269
|
-
cargo run --features http -- http 127.0.0.1:8788
|
|
270
|
-
# MCP endpoint: http://127.0.0.1:8788/mcp
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Point an MCP streamable-HTTP client at that URL (e.g. `StreamableHttpClientTransport::from_uri`).
|
|
75
|
+
More samples in [`examples/`](examples/). Unsure which action fits? Call `{"action":"help"}`.
|
|
274
76
|
|
|
275
77
|
## Environment
|
|
276
78
|
|
|
79
|
+
All optional.
|
|
80
|
+
|
|
277
81
|
| Variable | Default | Meaning |
|
|
278
82
|
|----------|---------|---------|
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `
|
|
282
|
-
| `COMPENDIUM_MAX_BLANK_LINES` | `1` | Blank-line collapse limit |
|
|
283
|
-
| `COMPENDIUM_SIMILARITY_THRESHOLD` | `0.85` | Jaccard line-dedupe threshold |
|
|
284
|
-
| `COMPENDIUM_HTTP_BIND` | `127.0.0.1:8788` | Default HTTP listen address |
|
|
285
|
-
| `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. |
|
|
286
|
-
| `COMPENDIUM_LOCAL_LLM_MODEL` | `Qwen3-4B-GGUF` | Model id on that server (Ollama setup default: `qwen2.5:3b`) |
|
|
287
|
-
| `COMPENDIUM_LOCAL_EMBED_MODEL` | _(same as chat)_ | Embeddings model for hybrid `rerank` / `brief` (e.g. `nomic-embed-text`) |
|
|
288
|
-
| `COMPENDIUM_HYBRID_ALPHA` | `0.55` | BM25 weight in hybrid score (0–1); remainder is embedding cosine |
|
|
289
|
-
| `COMPENDIUM_RERANK_CROSS_ENCODER` | _(off)_ | When `1`/`true`, `rerank` SLM-rescores top-N after BM25/hybrid |
|
|
290
|
-
| `COMPENDIUM_CROSS_ENCODER_TOP_N` | `16` | Candidates passed to cross-encoder (clamped 4–64) |
|
|
291
|
-
| `COMPENDIUM_AUDIT_PATH` | _(unset)_ | Append-only JSONL audit log (action metadata only; no payloads) |
|
|
292
|
-
| `COMPENDIUM_LOCAL_LLM_API_KEY` | _(unset)_ | Optional bearer token for locked loopback servers |
|
|
83
|
+
| `COMPENDIUM_LOCAL_LLM_URL` | _(unset)_ | OpenAI-compatible loopback base URL; enables smart actions |
|
|
84
|
+
| `COMPENDIUM_LOCAL_LLM_MODEL` | `qwen2.5:3b` | Model id on that server |
|
|
85
|
+
| `COMPENDIUM_LOCAL_LLM_API_KEY` | _(unset)_ | Bearer token for locked loopback servers |
|
|
293
86
|
| `COMPENDIUM_LOCAL_LLM_TIMEOUT_SECS` | `120` | HTTP timeout (first model load can be slow) |
|
|
87
|
+
| `COMPENDIUM_LOCAL_LLM_REASONING_EFFORT` | `low` | `low`/`medium`/`high`/`off`; `omit` drops the field for strict servers |
|
|
88
|
+
| `COMPENDIUM_DEFAULT_MAX_TOKENS` | `2048` | Soft output cap for compress/summarize |
|
|
294
89
|
| `COMPENDIUM_SIGNAL_MIN_CHARS` | `1000` | Bypass compress/summarize below this length (`0` disables) |
|
|
295
|
-
| `
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
298
|
-
| `
|
|
299
|
-
| `COMPENDIUM_ARCHIVE_MAX_FILES` | `50` | Max files per archive |
|
|
300
|
-
| `COMPENDIUM_SKILL_TTL_MS` | `300000` | Soft TTL (ms) on skill `resources/read` responses |
|
|
301
|
-
| `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. |
|
|
302
|
-
| `COMPENDIUM_CACHE_MAX_BYTES` | _(unset / 64MiB with dir)_ | Soft cap on total cached payload bytes |
|
|
303
|
-
| `RUST_LOG` | `compendium=info` | Logs on **stderr** only |
|
|
304
|
-
|
|
305
|
-
## Example tool calls
|
|
306
|
-
|
|
307
|
-
All calls use the single tool **`compendium`** with an `action` field.
|
|
308
|
-
|
|
309
|
-
**Filter noisy terminal output**
|
|
310
|
-
|
|
311
|
-
```json
|
|
312
|
-
{
|
|
313
|
-
"action": "filter",
|
|
314
|
-
"text": "\u001b[31mERROR\u001b[0m boom\n\n\nINFO ok",
|
|
315
|
-
"filter": {
|
|
316
|
-
"strip_ansi": true,
|
|
317
|
-
"keep_patterns": ["ERROR|WARN"]
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
**Compress a large log**
|
|
323
|
-
|
|
324
|
-
```json
|
|
325
|
-
{
|
|
326
|
-
"action": "compress",
|
|
327
|
-
"text": "...",
|
|
328
|
-
"compress": {
|
|
329
|
-
"content_type": "log",
|
|
330
|
-
"max_tokens": 512
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
**Chunk a document into references**
|
|
336
|
-
|
|
337
|
-
```json
|
|
338
|
-
{
|
|
339
|
-
"action": "chunk",
|
|
340
|
-
"text": "... huge file ...",
|
|
341
|
-
"chunk": {
|
|
342
|
-
"source": "file:///path/to/doc.md",
|
|
343
|
-
"chunk_tokens": 400,
|
|
344
|
-
"overlap_tokens": 40
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
Prefer the returned `index_text` in the model context; pull individual chunk contents by id only when needed.
|
|
350
|
-
|
|
351
|
-
**Query-aware filter (local SLM or heuristic fallback)**
|
|
352
|
-
|
|
353
|
-
```json
|
|
354
|
-
{
|
|
355
|
-
"action": "filter_relevant",
|
|
356
|
-
"text": "... noisy cargo/test log ...",
|
|
357
|
-
"query": "why did the auth tests fail",
|
|
358
|
-
"smart": { "max_tokens": 512, "fallback": true }
|
|
359
|
-
}
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
Without `COMPENDIUM_LOCAL_LLM_URL`, `summarize_smart` / `filter_relevant` automatically use heuristics and set `backend: "heuristic"` plus `fallback_reason` in the result.
|
|
363
|
-
|
|
364
|
-
**Pack a workspace briefing for a fresh agent turn**
|
|
365
|
-
|
|
366
|
-
```json
|
|
367
|
-
{
|
|
368
|
-
"action": "brief",
|
|
369
|
-
"query": "fix the OAuth refresh token path",
|
|
370
|
-
"brief": {
|
|
371
|
-
"root": "/path/to/repo",
|
|
372
|
-
"max_files": 40,
|
|
373
|
-
"top_k_chunks": 12,
|
|
374
|
-
"max_brief_tokens": 2048
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
Start the new turn with the returned `briefing` (or `cache_get` the `cache_key`). The host should not paste the whole tree into the prompt first. Treat Status as a starter synthesis — verify Caveats and Read next before large edits.
|
|
380
|
-
|
|
381
|
-
## Local small language model
|
|
90
|
+
| `COMPENDIUM_CHARS_PER_TOKEN` | `4.0` | Heuristic token estimate |
|
|
91
|
+
| `COMPENDIUM_TOKENIZER` | `cl100k_base` | BPE encoding when built with `--features real-tokens` |
|
|
92
|
+
| `COMPENDIUM_BINARY` | _(unset)_ | npm launcher: use this binary instead of downloading |
|
|
93
|
+
| `RUST_LOG` | `compendium=info` | Logs go to stderr only |
|
|
382
94
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
Rules of thumb:
|
|
386
|
-
|
|
387
|
-
- **Only loopback** URLs (`127.0.0.1`, `::1`, `localhost`) — no cloud endpoints.
|
|
388
|
-
- Without `COMPENDIUM_LOCAL_LLM_URL`, smart actions use heuristics and set `backend: "heuristic"`.
|
|
389
|
-
- Calls use `temperature=0` and `seed=0` for stable outputs.
|
|
390
|
-
- Lemonade example: `COMPENDIUM_LOCAL_LLM_URL=http://127.0.0.1:13305/api/v1` and `COMPENDIUM_LOCAL_LLM_MODEL=Qwen3-4B-GGUF`.
|
|
391
|
-
- llama.cpp OpenAI server: same pattern — set URL to its `/v1` base and the served model id.
|
|
392
|
-
|
|
393
|
-
## Develop / test
|
|
95
|
+
## Build from source
|
|
394
96
|
|
|
395
97
|
```bash
|
|
98
|
+
cargo build --release # heuristic token counts
|
|
99
|
+
cargo build --release --features real-tokens # exact BPE counts via tiktoken
|
|
396
100
|
cargo test
|
|
397
|
-
cargo test --features real-tokens
|
|
398
|
-
cargo test --features http --test http_smoke
|
|
399
|
-
cargo test --test e2e_smoke
|
|
400
|
-
cargo run --features http -- http 127.0.0.1:8788
|
|
401
101
|
```
|
|
402
102
|
|
|
403
|
-
|
|
103
|
+
Point your MCP config at `target/release/compendium` directly, or keep `npx` and set `COMPENDIUM_BINARY`.
|
|
404
104
|
|
|
405
|
-
## Design
|
|
105
|
+
## Design
|
|
106
|
+
|
|
107
|
+
- **Deterministic by default.** Heuristic paths need no network and produce byte-identical output, which keeps prompt prefix caches warm.
|
|
108
|
+
- **Local-first.** Smart actions only ever call a loopback URL; there is no cloud path.
|
|
109
|
+
- **stdout is JSON-RPC.** All logging goes to stderr.
|
|
110
|
+
|
|
111
|
+
## Community
|
|
406
112
|
|
|
407
|
-
|
|
408
|
-
- **Token backends** — fast heuristic by default; opt into exact BPE with `real-tokens`.
|
|
409
|
-
- **Zero stdout pollution** (stdio mode) — tracing goes to stderr so JSON-RPC framing stays clean.
|
|
410
|
-
- **Release profile** — LTO + stripped binary for low footprint.
|
|
113
|
+
[Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md) · [Architecture](docs/architecture.md) · [Security](SECURITY.md) · [Support](SUPPORT.md) · [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
411
114
|
|
|
412
115
|
## License
|
|
413
116
|
|
package/bin/run.js
CHANGED
|
@@ -2,25 +2,17 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Compendium MCP
|
|
5
|
+
* Compendium MCP launcher.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Resolution order:
|
|
11
|
-
* 1. COMPENDIUM_BINARY env
|
|
12
|
-
* 2. Dev build: <repo>/target/release|debug/compendium(.exe)
|
|
13
|
-
* 3. optionalDependency platform package (compendium-mcp-<platform>)
|
|
14
|
-
* 4. Cached GitHub Release download (~/.cache/compendium-mcp/<version>/)
|
|
7
|
+
* Finds (or downloads once) the native `compendium` binary for this platform
|
|
8
|
+
* and runs it with inherited stdio, so MCP clients talk JSON-RPC over stdin/stdout.
|
|
15
9
|
*/
|
|
16
10
|
|
|
17
11
|
const { spawn } = require('child_process');
|
|
18
12
|
const fs = require('fs');
|
|
19
13
|
const https = require('https');
|
|
20
|
-
const http = require('http');
|
|
21
14
|
const path = require('path');
|
|
22
15
|
const { pipeline } = require('stream/promises');
|
|
23
|
-
const { createWriteStream } = require('fs');
|
|
24
16
|
|
|
25
17
|
const {
|
|
26
18
|
packageVersion,
|
|
@@ -31,46 +23,28 @@ const {
|
|
|
31
23
|
platformKey,
|
|
32
24
|
} = require('../npm/lib/platform');
|
|
33
25
|
|
|
34
|
-
function
|
|
26
|
+
function isExecutable(file) {
|
|
35
27
|
try {
|
|
36
28
|
fs.accessSync(file, fs.constants.F_OK);
|
|
37
|
-
|
|
38
|
-
if (process.platform !== 'win32') {
|
|
39
|
-
fs.accessSync(file, fs.constants.X_OK);
|
|
40
|
-
}
|
|
29
|
+
if (process.platform !== 'win32') fs.accessSync(file, fs.constants.X_OK);
|
|
41
30
|
return true;
|
|
42
31
|
} catch {
|
|
43
32
|
return false;
|
|
44
33
|
}
|
|
45
34
|
}
|
|
46
35
|
|
|
47
|
-
function
|
|
48
|
-
const spec = currentPlatform();
|
|
49
|
-
try {
|
|
50
|
-
// optionalDependency installs next to this package when available.
|
|
51
|
-
const pkgRoot = path.dirname(require.resolve(`${spec.pkg}/package.json`));
|
|
52
|
-
const candidate = path.join(pkgRoot, 'bin', binaryName(spec));
|
|
53
|
-
if (existsExecutable(candidate)) return candidate;
|
|
54
|
-
} catch {
|
|
55
|
-
// optional dep missing — fall through
|
|
56
|
-
}
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function tryDevBinary() {
|
|
36
|
+
function devBinary() {
|
|
61
37
|
const repoRoot = path.resolve(__dirname, '..');
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (existsExecutable(debug)) return debug;
|
|
38
|
+
for (const profile of ['release', 'debug']) {
|
|
39
|
+
const candidate = path.join(repoRoot, 'target', profile, binaryName());
|
|
40
|
+
if (isExecutable(candidate)) return candidate;
|
|
41
|
+
}
|
|
67
42
|
return null;
|
|
68
43
|
}
|
|
69
44
|
|
|
70
|
-
function download(url, dest) {
|
|
45
|
+
function download(url, dest, hops = 0) {
|
|
71
46
|
return new Promise((resolve, reject) => {
|
|
72
|
-
const
|
|
73
|
-
const req = client.get(
|
|
47
|
+
const req = https.get(
|
|
74
48
|
url,
|
|
75
49
|
{
|
|
76
50
|
headers: {
|
|
@@ -79,14 +53,10 @@ function download(url, dest) {
|
|
|
79
53
|
},
|
|
80
54
|
},
|
|
81
55
|
(res) => {
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
res.statusCode >= 300 &&
|
|
85
|
-
res.statusCode < 400 &&
|
|
86
|
-
res.headers.location
|
|
87
|
-
) {
|
|
56
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
88
57
|
res.resume();
|
|
89
|
-
|
|
58
|
+
if (hops >= 5) return reject(new Error('too many redirects'));
|
|
59
|
+
download(res.headers.location, dest, hops + 1).then(resolve, reject);
|
|
90
60
|
return;
|
|
91
61
|
}
|
|
92
62
|
if (res.statusCode !== 200) {
|
|
@@ -94,19 +64,16 @@ function download(url, dest) {
|
|
|
94
64
|
reject(
|
|
95
65
|
new Error(
|
|
96
66
|
`Download failed (${res.statusCode}) for ${url}. ` +
|
|
97
|
-
`
|
|
67
|
+
`No release asset for ${platformKey()}? Build from source and set COMPENDIUM_BINARY.`
|
|
98
68
|
)
|
|
99
69
|
);
|
|
100
70
|
return;
|
|
101
71
|
}
|
|
102
72
|
const tmp = `${dest}.partial`;
|
|
103
|
-
|
|
104
|
-
pipeline(res, out)
|
|
73
|
+
pipeline(res, fs.createWriteStream(tmp))
|
|
105
74
|
.then(() => {
|
|
106
75
|
fs.renameSync(tmp, dest);
|
|
107
|
-
if (process.platform !== 'win32')
|
|
108
|
-
fs.chmodSync(dest, 0o755);
|
|
109
|
-
}
|
|
76
|
+
if (process.platform !== 'win32') fs.chmodSync(dest, 0o755);
|
|
110
77
|
resolve(dest);
|
|
111
78
|
})
|
|
112
79
|
.catch((err) => {
|
|
@@ -123,44 +90,29 @@ function download(url, dest) {
|
|
|
123
90
|
});
|
|
124
91
|
}
|
|
125
92
|
|
|
126
|
-
async function
|
|
93
|
+
async function downloadedBinary() {
|
|
127
94
|
const spec = currentPlatform();
|
|
128
95
|
const version = packageVersion();
|
|
129
96
|
const dir = cacheDir(version);
|
|
130
97
|
fs.mkdirSync(dir, { recursive: true });
|
|
131
|
-
const dest = path.join(dir, binaryName(
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
const repo = githubRepo();
|
|
135
|
-
const tag = `v${version}`;
|
|
136
|
-
const url = `https://github.com/${repo}/releases/download/${tag}/${spec.asset}`;
|
|
98
|
+
const dest = path.join(dir, binaryName());
|
|
99
|
+
if (isExecutable(dest)) return dest;
|
|
137
100
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
);
|
|
101
|
+
const url = `https://github.com/${githubRepo()}/releases/download/v${version}/${spec.asset}`;
|
|
102
|
+
process.stderr.write(`[compendium-mcp] downloading ${spec.asset} v${version}…\n`);
|
|
141
103
|
await download(url, dest);
|
|
142
|
-
process.stderr.write(`[compendium-mcp] Cached at ${dest}\n`);
|
|
143
104
|
return dest;
|
|
144
105
|
}
|
|
145
106
|
|
|
146
107
|
async function resolveBinary() {
|
|
147
108
|
if (process.env.COMPENDIUM_BINARY) {
|
|
148
109
|
const forced = path.resolve(process.env.COMPENDIUM_BINARY);
|
|
149
|
-
if (!
|
|
110
|
+
if (!isExecutable(forced)) {
|
|
150
111
|
throw new Error(`COMPENDIUM_BINARY not executable: ${forced}`);
|
|
151
112
|
}
|
|
152
113
|
return forced;
|
|
153
114
|
}
|
|
154
|
-
|
|
155
|
-
// Prefer a local Cargo build when developing from this repo so `cargo build
|
|
156
|
-
// --release` is picked up immediately (optional platform packages can lag).
|
|
157
|
-
const fromDev = tryDevBinary();
|
|
158
|
-
if (fromDev) return fromDev;
|
|
159
|
-
|
|
160
|
-
const fromOptional = tryRequirePlatformBinary();
|
|
161
|
-
if (fromOptional) return fromOptional;
|
|
162
|
-
|
|
163
|
-
return ensureDownloadedBinary();
|
|
115
|
+
return devBinary() || downloadedBinary();
|
|
164
116
|
}
|
|
165
117
|
|
|
166
118
|
async function main() {
|
|
@@ -172,28 +124,24 @@ async function main() {
|
|
|
172
124
|
process.exit(1);
|
|
173
125
|
}
|
|
174
126
|
|
|
175
|
-
process.stderr.write(`[compendium-mcp] using ${binary}\n`);
|
|
176
|
-
|
|
177
127
|
const child = spawn(binary, process.argv.slice(2), {
|
|
178
128
|
stdio: 'inherit',
|
|
179
129
|
windowsHide: true,
|
|
180
130
|
});
|
|
181
131
|
|
|
182
132
|
child.on('error', (err) => {
|
|
183
|
-
process.stderr.write(`[compendium-mcp] failed to
|
|
133
|
+
process.stderr.write(`[compendium-mcp] failed to start ${binary}: ${err.message}\n`);
|
|
184
134
|
process.exit(1);
|
|
185
135
|
});
|
|
186
136
|
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
137
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
138
|
+
process.on(signal, () => {
|
|
139
|
+
if (!child.killed) child.kill(signal);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
192
142
|
|
|
193
143
|
child.on('exit', (code, signal) => {
|
|
194
|
-
if (signal)
|
|
195
|
-
process.exit(signal === 'SIGINT' ? 130 : 1);
|
|
196
|
-
}
|
|
144
|
+
if (signal) process.exit(signal === 'SIGINT' ? 130 : 1);
|
|
197
145
|
process.exit(code ?? 1);
|
|
198
146
|
});
|
|
199
147
|
}
|
package/npm/lib/platform.js
CHANGED
|
@@ -1,157 +1,59 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Platform → GitHub Release asset mapping for the `compendium-mcp` npm wrapper.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 4. Force key: COMPENDIUM_PLATFORM (e.g. linux-x64-musl).
|
|
6
|
+
* Binary resolution (see bin/run.js):
|
|
7
|
+
* 1. COMPENDIUM_BINARY env override
|
|
8
|
+
* 2. Dev build: <repo>/target/release|debug/compendium
|
|
9
|
+
* 3. Cached download from GitHub Releases (~/.cache/compendium-mcp/<version>/)
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
|
-
const fs = require('fs');
|
|
14
12
|
const os = require('os');
|
|
15
13
|
const path = require('path');
|
|
16
|
-
const { execSync } = require('child_process');
|
|
17
14
|
|
|
18
|
-
/** @
|
|
19
|
-
|
|
20
|
-
/** @type {Record<string, PlatformSpec>} */
|
|
15
|
+
/** @type {Record<string, { asset: string }>} */
|
|
21
16
|
const PLATFORMS = {
|
|
22
|
-
'darwin-arm64': {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
'darwin-x64': {
|
|
28
|
-
pkg: 'compendium-mcp-darwin-x64',
|
|
29
|
-
asset: 'compendium-darwin-x64',
|
|
30
|
-
rustTarget: 'x86_64-apple-darwin',
|
|
31
|
-
},
|
|
32
|
-
'linux-x64': {
|
|
33
|
-
pkg: 'compendium-mcp-linux-x64',
|
|
34
|
-
asset: 'compendium-linux-x64',
|
|
35
|
-
rustTarget: 'x86_64-unknown-linux-gnu',
|
|
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
|
-
},
|
|
42
|
-
'linux-arm64': {
|
|
43
|
-
pkg: 'compendium-mcp-linux-arm64',
|
|
44
|
-
asset: 'compendium-linux-arm64',
|
|
45
|
-
rustTarget: 'aarch64-unknown-linux-gnu',
|
|
46
|
-
},
|
|
47
|
-
'win32-x64': {
|
|
48
|
-
pkg: 'compendium-mcp-win32-x64',
|
|
49
|
-
asset: 'compendium-win32-x64.exe',
|
|
50
|
-
rustTarget: 'x86_64-pc-windows-msvc',
|
|
51
|
-
},
|
|
52
|
-
'win32-arm64': {
|
|
53
|
-
pkg: 'compendium-mcp-win32-arm64',
|
|
54
|
-
asset: 'compendium-win32-arm64.exe',
|
|
55
|
-
rustTarget: 'aarch64-pc-windows-msvc',
|
|
56
|
-
},
|
|
17
|
+
'darwin-arm64': { asset: 'compendium-darwin-arm64' },
|
|
18
|
+
'darwin-x64': { asset: 'compendium-darwin-x64' },
|
|
19
|
+
'linux-x64': { asset: 'compendium-linux-x64' },
|
|
20
|
+
'linux-arm64': { asset: 'compendium-linux-arm64' },
|
|
21
|
+
'win32-x64': { asset: 'compendium-win32-x64.exe' },
|
|
57
22
|
};
|
|
58
23
|
|
|
59
24
|
function packageVersion() {
|
|
60
|
-
// Keep in sync with root package.json / Cargo.toml at publish time.
|
|
61
25
|
return require('../../package.json').version;
|
|
62
26
|
}
|
|
63
27
|
|
|
64
28
|
function githubRepo() {
|
|
65
|
-
return
|
|
66
|
-
process.env.COMPENDIUM_GITHUB_REPO ||
|
|
67
|
-
process.env.GITHUB_REPOSITORY ||
|
|
68
|
-
'hocestnonsatis/Compendium'
|
|
69
|
-
);
|
|
29
|
+
return process.env.COMPENDIUM_GITHUB_REPO || 'hocestnonsatis/Compendium';
|
|
70
30
|
}
|
|
71
31
|
|
|
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
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Normalize Node's process.platform + process.arch into our key.
|
|
102
|
-
* Override with COMPENDIUM_PLATFORM when needed (e.g. linux-x64-musl).
|
|
103
|
-
* @returns {string}
|
|
104
|
-
*/
|
|
105
32
|
function platformKey() {
|
|
106
33
|
const forced = (process.env.COMPENDIUM_PLATFORM || '').trim();
|
|
107
34
|
if (forced) return forced;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
let arch = process.arch;
|
|
111
|
-
// Rosetta / rare aliases
|
|
112
|
-
if (arch === 'ia32') arch = 'x64';
|
|
113
|
-
|
|
114
|
-
if (platform === 'linux' && arch === 'x64' && isLinuxMusl()) {
|
|
115
|
-
return 'linux-x64-musl';
|
|
116
|
-
}
|
|
117
|
-
return `${platform}-${arch}`;
|
|
35
|
+
const arch = process.arch === 'ia32' ? 'x64' : process.arch;
|
|
36
|
+
return `${process.platform}-${arch}`;
|
|
118
37
|
}
|
|
119
38
|
|
|
120
|
-
/**
|
|
121
|
-
* @returns {PlatformSpec}
|
|
122
|
-
*/
|
|
123
39
|
function currentPlatform() {
|
|
124
40
|
const key = platformKey();
|
|
125
41
|
const spec = PLATFORMS[key];
|
|
126
42
|
if (!spec) {
|
|
127
|
-
const supported = Object.keys(PLATFORMS).join(', ');
|
|
128
43
|
throw new Error(
|
|
129
|
-
`Unsupported platform "${key}". Supported: ${
|
|
130
|
-
`
|
|
44
|
+
`Unsupported platform "${key}". Supported: ${Object.keys(PLATFORMS).join(', ')}.\n` +
|
|
45
|
+
`Build from source (cargo build --release) and set COMPENDIUM_BINARY to the binary.`
|
|
131
46
|
);
|
|
132
47
|
}
|
|
133
48
|
return spec;
|
|
134
49
|
}
|
|
135
50
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* @param {PlatformSpec} [spec]
|
|
139
|
-
*/
|
|
140
|
-
function binaryName(spec = currentPlatform()) {
|
|
141
|
-
return process.platform === 'win32' || spec.asset.endsWith('.exe')
|
|
142
|
-
? 'compendium.exe'
|
|
143
|
-
: 'compendium';
|
|
51
|
+
function binaryName() {
|
|
52
|
+
return process.platform === 'win32' ? 'compendium.exe' : 'compendium';
|
|
144
53
|
}
|
|
145
54
|
|
|
146
|
-
/**
|
|
147
|
-
* Cache directory for lazily downloaded binaries.
|
|
148
|
-
* @param {string} version
|
|
149
|
-
*/
|
|
150
55
|
function cacheDir(version = packageVersion()) {
|
|
151
|
-
const base =
|
|
152
|
-
process.env.COMPENDIUM_CACHE_DIR ||
|
|
153
|
-
process.env.XDG_CACHE_HOME ||
|
|
154
|
-
path.join(os.homedir(), '.cache');
|
|
56
|
+
const base = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');
|
|
155
57
|
return path.join(base, 'compendium-mcp', version);
|
|
156
58
|
}
|
|
157
59
|
|
|
@@ -163,5 +65,4 @@ module.exports = {
|
|
|
163
65
|
currentPlatform,
|
|
164
66
|
binaryName,
|
|
165
67
|
cacheDir,
|
|
166
|
-
isLinuxMusl,
|
|
167
68
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compendium-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "MCP server that compresses, summarizes, and filters context to minimize LLM token usage",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -34,21 +34,7 @@
|
|
|
34
34
|
"LICENSE"
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
|
-
"check-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
"check-residual-npm": "node npm/scripts/check-residual-npm.js",
|
|
41
|
-
"prepack": "node npm/scripts/check-wrapper.js && node npm/scripts/check-versions.js",
|
|
42
|
-
"prepare": "node npm/scripts/link-bins.js"
|
|
43
|
-
},
|
|
44
|
-
"optionalDependencies": {
|
|
45
|
-
"compendium-mcp-darwin-arm64": "0.6.3",
|
|
46
|
-
"compendium-mcp-darwin-x64": "0.6.3",
|
|
47
|
-
"compendium-mcp-linux-x64": "0.6.3",
|
|
48
|
-
"compendium-mcp-linux-x64-musl": "0.6.3",
|
|
49
|
-
"compendium-mcp-linux-arm64": "0.6.3",
|
|
50
|
-
"compendium-mcp-win32-x64": "0.6.3",
|
|
51
|
-
"compendium-mcp-win32-arm64": "0.6.3"
|
|
52
|
-
},
|
|
53
|
-
"preferUnplugged": true
|
|
37
|
+
"check-version": "node npm/scripts/check-version.js",
|
|
38
|
+
"prepack": "node npm/scripts/check-version.js"
|
|
39
|
+
}
|
|
54
40
|
}
|