md-semantic-search 1.0.2 → 1.0.3
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 +154 -6
- package/bin/cli.mjs +403 -22
- package/dist/completions.d.ts +25 -0
- package/dist/completions.js +233 -0
- package/dist/core.d.ts +5 -0
- package/dist/core.js +17 -0
- package/dist/download-progress.d.ts +52 -0
- package/dist/download-progress.js +112 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/mcp.js +6 -1
- package/dist/open.d.ts +43 -0
- package/dist/open.js +84 -0
- package/dist/providers.d.ts +21 -0
- package/dist/providers.js +104 -0
- package/dist/query-cache-disk.d.ts +14 -0
- package/dist/query-cache-disk.js +175 -0
- package/dist/repair.d.ts +36 -0
- package/dist/repair.js +208 -0
- package/dist/rerank.js +3 -2
- package/dist/search.d.ts +34 -0
- package/dist/search.js +172 -5
- package/dist/serve.d.ts +126 -0
- package/dist/serve.js +579 -45
- package/dist/watcher.d.ts +16 -0
- package/dist/watcher.js +41 -0
- package/dist/webui.d.ts +13 -0
- package/dist/webui.js +231 -0
- package/package.json +1 -1
- package/src/completions.ts +250 -0
- package/src/core.ts +26 -0
- package/src/download-progress.ts +146 -0
- package/src/index.ts +4 -0
- package/src/mcp.ts +10 -3
- package/src/open.ts +119 -0
- package/src/providers.ts +134 -0
- package/src/query-cache-disk.ts +191 -0
- package/src/repair.ts +245 -0
- package/src/rerank.ts +3 -2
- package/src/search.ts +192 -6
- package/src/serve.ts +635 -42
- package/src/watcher.ts +46 -0
- package/src/webui.ts +234 -0
package/README.md
CHANGED
|
@@ -109,6 +109,9 @@ measurements that shaped its defaults.
|
|
|
109
109
|
|
|
110
110
|
## Install
|
|
111
111
|
|
|
112
|
+
Requires **Node.js ≥ 18**. Native dependencies ship as prebuilt binaries — no
|
|
113
|
+
compiler toolchain needed on any platform.
|
|
114
|
+
|
|
112
115
|
Run on demand with `npx` (no install):
|
|
113
116
|
|
|
114
117
|
```bash
|
|
@@ -131,6 +134,9 @@ npm install
|
|
|
131
134
|
node bin/cli.mjs --help
|
|
132
135
|
```
|
|
133
136
|
|
|
137
|
+
For headless server deployments there is a Docker path — see
|
|
138
|
+
[docs/deploy.md](./docs/deploy.md).
|
|
139
|
+
|
|
134
140
|
---
|
|
135
141
|
|
|
136
142
|
## Library usage
|
|
@@ -177,7 +183,14 @@ indexes and existing library consumers; schema-v3 search uses persisted BM25.
|
|
|
177
183
|
mdss index --db /path/to/your/markdown
|
|
178
184
|
```
|
|
179
185
|
|
|
180
|
-
First run downloads the model (~280 MB)
|
|
186
|
+
First run downloads the model (~280 MB) — and the download itself is
|
|
187
|
+
**visible**: an aggregated bar on stderr shows percent across every file of the
|
|
188
|
+
model directory, live MB/s and ETA (the same bar reappears when `--rerank`
|
|
189
|
+
first pulls the cross-encoder). Under `--json` the bar is replaced by throttled
|
|
190
|
+
`{"type":"download-progress", …}` / `{"type":"download-complete", …}` events
|
|
191
|
+
**on stderr**, so stdout stays machine-readable. A warm cache produces no
|
|
192
|
+
download output at all, and `--offline` behaves exactly as before. The index
|
|
193
|
+
is written to `<db>/.mdss/`
|
|
181
194
|
by default (override with `--index-dir`). Re-run after editing your notes — it's
|
|
182
195
|
incremental at two levels: unchanged files are skipped via their md5, and inside
|
|
183
196
|
a changed file only the sections whose text actually changed are re-embedded
|
|
@@ -420,11 +433,76 @@ const { results } = await r.json();
|
|
|
420
433
|
```
|
|
421
434
|
|
|
422
435
|
The API is unauthenticated by default (or secured via `--api-key`) — by default it binds to **loopback only**
|
|
423
|
-
(`127.0.0.1`), so other machines on your LAN cannot reach it.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
436
|
+
(`127.0.0.1`), so other machines on your LAN cannot reach it. Passing `--host 0.0.0.0` (or `MDSS_HOST`)
|
|
437
|
+
without authentication **refuses to start** — set an API key, or opt in explicitly with `--allow-unsecured`
|
|
438
|
+
(see [Securing remote deployments](#securing-remote-deployments)). Request bodies are capped at 64 KB
|
|
439
|
+
(oversized → `413`), malformed JSON gets a clear `400`, per-client request rate is capped at 60/min with a
|
|
440
|
+
burst of 10 (`429 + Retry-After` above that; tune via `--rate-limit`), and at most `CPU count` searches run
|
|
441
|
+
concurrently with a bounded wait queue (`503` when saturated; tune via `--max-concurrency`). Every response
|
|
442
|
+
carries `X-Content-Type-Options: nosniff` and `Cache-Control: no-store`; the `Host` header is validated
|
|
443
|
+
against a loopback allowlist (`403` otherwise) so DNS-rebinding pages cannot read your notes cross-origin,
|
|
444
|
+
and CORS stays fully off unless you pass `--cors-origin`. `/health` exposes the live counters
|
|
445
|
+
(`in_flight`, `queued`, `rejected_total`).
|
|
446
|
+
|
|
447
|
+
### Web UI (built-in, `--ui`)
|
|
448
|
+
|
|
449
|
+
By default `mdss serve` also serves a **zero-dependency web UI at `/`** — a keyboard-first search page
|
|
450
|
+
with live debounced results, term highlighting, score bars, tag/type/status filters, dark/light theme
|
|
451
|
+
(follows the system until you toggle it) and an API-key prompt when Bearer auth is enabled (the key is kept
|
|
452
|
+
in `sessionStorage`, same-origin only). It ships as plain HTML/CSS/JS split into three assets so a strict
|
|
453
|
+
CSP applies (`default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'self';
|
|
454
|
+
frame-ancestors 'none'`) — no inline code, no external calls. Use `--no-ui` to restore pure JSON at `/`;
|
|
455
|
+
the JSON contract (`/search`, `/health`, `/help`) never changes. A Playwright smoke test runs weekly via
|
|
456
|
+
the opt-in `ui-smoke` workflow.
|
|
457
|
+
|
|
458
|
+
### Securing remote deployments
|
|
459
|
+
|
|
460
|
+
`mdss serve` is designed to be private-first: loopback bind by default, refuse-to-start for unauthenticated
|
|
461
|
+
network binds, DNS-rebinding protection, and rate limiting built in. To serve a team over the network safely:
|
|
462
|
+
|
|
463
|
+
1. **Keep TLS + auth at a reverse proxy** — mdss speaks plain HTTP on loopback; nginx/caddy terminate TLS.
|
|
464
|
+
|
|
465
|
+
```nginx
|
|
466
|
+
# /etc/nginx/sites-available/mdss — TLS termination + Bearer-auth passthrough
|
|
467
|
+
server {
|
|
468
|
+
listen 443 ssl;
|
|
469
|
+
server_name search.example.com;
|
|
470
|
+
ssl_certificate /etc/letsencrypt/live/search.example.com/fullchain.pem;
|
|
471
|
+
ssl_certificate_key /etc/letsencrypt/live/search.example.com/privkey.pem;
|
|
472
|
+
|
|
473
|
+
location / {
|
|
474
|
+
proxy_pass http://127.0.0.1:8747;
|
|
475
|
+
proxy_set_header Host $host; # must match --allowed-host if not loopback-named
|
|
476
|
+
proxy_set_header X-Forwarded-For $remote_addr;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
```caddyfile
|
|
482
|
+
# Caddyfile — automatic HTTPS
|
|
483
|
+
search.example.com {
|
|
484
|
+
reverse_proxy 127.0.0.1:8747
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
2. **Set an API key without leaking it into shell history or CI logs:**
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
echo "$RANDOM_KEY" > /etc/mdss/api-key && chmod 600 /etc/mdss/api-key
|
|
492
|
+
mdss serve --db /srv/wiki --host 127.0.0.1 --api-key-file /etc/mdss/api-key
|
|
493
|
+
# or MDSS_API_KEY_FILE=/etc/mdss/api-key
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
3. **If you must expose mdss directly** (no proxy), require the key *and* keep the firewall tight:
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
MDSS_API_KEY=$(openssl rand -hex 32) mdss serve --db /srv/wiki --host 0.0.0.0
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
The startup banner warns loudly whenever an unsecured non-loopback bind was explicitly allowed.
|
|
503
|
+
|
|
504
|
+
4. **Browser clients on another origin?** Opt into CORS explicitly, exact origins only:
|
|
505
|
+
`--cors-origin https://intranet.example.com`.
|
|
428
506
|
|
|
429
507
|
### 4. MCP Server (Claude Desktop, Cursor, Copilot, Antigravity)
|
|
430
508
|
|
|
@@ -434,6 +512,13 @@ instead of being silently swallowed.
|
|
|
434
512
|
mdss mcp --db /path/to/your/markdown
|
|
435
513
|
```
|
|
436
514
|
|
|
515
|
+
**Streamable HTTP transport (issue #123):** a long-running daemon can expose
|
|
516
|
+
the same tools over HTTP — `mdss serve --db ./notes --mcp` mounts `/mcp`
|
|
517
|
+
(POST JSON-RPC, SSE channel on GET, `Mcp-Session-Id` sessions, protocol
|
|
518
|
+
`2025-03-26`). It sits behind the exact same auth, Host-allowlist and
|
|
519
|
+
rate-limit gates as `/search`, so remote agents inherit the hardening. stdio
|
|
520
|
+
via `mdss mcp` remains the default transport.
|
|
521
|
+
|
|
437
522
|
#### Available MCP Tools:
|
|
438
523
|
- **`search_markdown`**: Hybrid semantic vector + BM25 search with optional `path` glob and `tag` filters.
|
|
439
524
|
- **`get_chunk`**: Retrieve a specific section by file and heading.
|
|
@@ -485,6 +570,9 @@ Add to your `claude_desktop_config.json`:
|
|
|
485
570
|
| `--k <n>` | Number of results, positive integer (default 6). |
|
|
486
571
|
| `--json` | Machine-readable output: `index` → build result JSON, `stats` → index stats JSON, `search` → hit list JSON (each hit includes `matches` — the query terms found in the chunk). |
|
|
487
572
|
| `--semantic` | `search` only: pure vector ranking, skip lexical/RRF fusion (zero lexical tokenization). |
|
|
573
|
+
| `--open [N]` | `search`: open the top (or Nth) hit in your editor at its `startLine` — `$MDSS_EDITOR`/`$VISUAL`/`$EDITOR`, VS Code `--goto`, or the GUI opener. With `--json` on a non-TTY prints the intent instead of launching (issue #110). |
|
|
574
|
+
| `--fix` | `check`: apply safe auto-repairs — stale `.mdss.lock`, SHA-mismatched `vectors.bin` sidecar, corrupt `ivf.json`; rebuilds a corrupt index / recomputes `.hashes.json` when `--db` is available. Never touches source notes (issue #117). |
|
|
575
|
+
| `--dry-run` | `check --fix`: print the planned repairs without mutating anything. |
|
|
488
576
|
| `--rerank` | Re-rank the candidate pool with a cross-encoder (`Xenova/bge-reranker-base`, ~280 MB model, downloaded on first use). Slower, sharper results — see *Reranking* below. |
|
|
489
577
|
| `--graph-boost <n>` | Weight for Obsidian wikilink graph ranking and PageRank prior (search; default 0). Incorporates document centrality and 2-hop contextual propagation. |
|
|
490
578
|
| `--filter <expr>` | Rich boolean filter across frontmatter tags and properties (e.g. `tag:engineering AND status != archived`). |
|
|
@@ -492,12 +580,50 @@ Add to your `claude_desktop_config.json`:
|
|
|
492
580
|
| `--nprobe <n>` | Number of nearest centroid clusters to probe during ANN search (default 8). |
|
|
493
581
|
| `--port <n>` | HTTP port for `serve` (default 8747, or `MDSS_PORT`). |
|
|
494
582
|
| `--host <ip>` | Bind address for `serve` (default `127.0.0.1` — loopback only; use `0.0.0.0` to expose on the LAN, or `MDSS_HOST`). |
|
|
583
|
+
| `--api-key <key>` | `serve`: require Bearer auth on every request (`MDSS_API_KEY`). |
|
|
584
|
+
| `--api-key-file <path>` | `serve`: read the API key from a file instead of a literal value — avoids shell-history / CI-log leaks (`MDSS_API_KEY_FILE`; trailing newline trimmed, POSIX permissions checked). |
|
|
585
|
+
| `--allow-unsecured` | `serve`: explicit opt-in to bind non-loopback **without** auth; without it (and without an API key) such a bind refuses to start (issue #121). |
|
|
586
|
+
| `--allowed-host <h>` | `serve`: extra `Host` header value to accept (repeatable); anything else gets `403` — DNS-rebinding guard (issue #120). |
|
|
587
|
+
| `--cors-origin <o>` | `serve`: allow cross-origin browser access from this exact origin (repeatable); CORS is off unless set (issue #120). |
|
|
588
|
+
| `--rate-limit <n>` | `serve`: max `/search` requests per minute per client, burst 10 → `429 + Retry-After` above it; `0` disables (default 60, `MDSS_RATE_LIMIT`; issue #119). |
|
|
589
|
+
| `--max-concurrency <n>` | `serve`: max concurrent in-flight searches; queue cap 2× → `503` when full; `0` disables (default = CPU count, `MDSS_MAX_CONCURRENCY`; issue #119). |
|
|
495
590
|
| `--watch` | `serve`: re-index incrementally when files change. |
|
|
591
|
+
| `--watch-debug` | `serve --watch`: verbose trace of polls, FS-error classifications and retries (issue #116). |
|
|
592
|
+
| `--recency <days>` | `search`: time-decay half-life — passages get a `0.5^(age/halfLife)` boost after fusion; age from frontmatter `created`/`updated`, then file mtime; surfaced in `explain` (issue #127). |
|
|
593
|
+
| `--embedder <name>` | Use an external embedding provider (`ollama` or `openai`) instead of local transformers.js, together with `--embedder-model` (+ `--embedder-base-url`, `--embedder-key-file`). The provider id/model/dim are recorded in the index — switching providers invalidates vectors with the usual full re-embed. Local remains the zero-config default and the only fully-offline path (issue #124). |
|
|
594
|
+
| `--no-query-cache` | `search`: skip the persistent query-embedding cache — repeated queries in fresh processes re-embed otherwise (issue #114). |
|
|
595
|
+
| `--no-ui` | `serve`: disable the built-in web UI at `/` — restores JSON API help there (issue #111). |
|
|
496
596
|
| `--watch-interval <ms>` | `serve --watch`: poll every N ms (default 3000). |
|
|
497
597
|
| `--watch-delay <ms>` | `serve --watch`: quiet-period debounce before a burst of saves triggers ONE re-index (default 1000; issue #42). |
|
|
498
598
|
| `--offline` | Never download the model — require a cached one (or `MDSS_OFFLINE=1`). |
|
|
499
599
|
| `--version` | Print the installed version. |
|
|
500
600
|
|
|
601
|
+
### Shell completions
|
|
602
|
+
|
|
603
|
+
`mdss completions <bash|zsh|fish|powershell>` prints a self-contained completion
|
|
604
|
+
script covering every subcommand and flag, with directory completion for
|
|
605
|
+
`--db` / `--index-dir` / `--cache-dir` / `--vault` and file completion for
|
|
606
|
+
`--output` / `--config` / `--api-key-file`:
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
# bash — current session only:
|
|
610
|
+
eval "$(mdss completions bash)"
|
|
611
|
+
# bash — persistent:
|
|
612
|
+
mdss completions bash > ~/.local/share/bash-completions/completions/mdss
|
|
613
|
+
|
|
614
|
+
# zsh:
|
|
615
|
+
mkdir -p ~/.zfunc && mdss completions zsh > ~/.zfunc/_mdss # fpath before compinit
|
|
616
|
+
|
|
617
|
+
# fish:
|
|
618
|
+
mdss completions fish > ~/.config/fish/completions/mdss.fish
|
|
619
|
+
|
|
620
|
+
# PowerShell — paste into your $PROFILE:
|
|
621
|
+
mdss completions powershell >> $PROFILE
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
A unit test guards against flag drift: the generated tables must match the
|
|
625
|
+
flags `parseArgs` actually accepts.
|
|
626
|
+
|
|
501
627
|
### The base can live outside the project
|
|
502
628
|
|
|
503
629
|
The index does not need write access to your notes if they're read-only — just
|
|
@@ -517,6 +643,13 @@ mdss index
|
|
|
517
643
|
mdss search "incident runbook for db failover"
|
|
518
644
|
```
|
|
519
645
|
|
|
646
|
+
**Supported layouts** (hardened by the issue #118 suite): deep trees beyond
|
|
647
|
+
Windows MAX_PATH (>260 chars) work; unicode and spaces in names work;
|
|
648
|
+
`--index-dir` may live on a different drive than `--db`; NTFS junction loops
|
|
649
|
+
terminate the walker (bounded by path length); `\\?\` extended-length roots are
|
|
650
|
+
accepted for `--db`/`--index-dir`; remote UNC shares (`\\server\share`) are
|
|
651
|
+
**rejected** by design (local-only security boundary).
|
|
652
|
+
|
|
520
653
|
### Configuration file (`.mdssrc` / `mdss.config.json`)
|
|
521
654
|
|
|
522
655
|
Instead of repeating CLI options on every command, create a `.mdssrc.json` or `mdss.config.json` file.
|
|
@@ -650,6 +783,21 @@ is an in-memory dot-product sweep.
|
|
|
650
783
|
|
|
651
784
|
- **Obsidian**: An official plugin is available in [`integrations/obsidian`](./integrations/obsidian) providing a live semantic search panel directly in your vault connected to `mdss serve`.
|
|
652
785
|
- **Model Context Protocol (MCP)**: Native stdio JSON-RPC 2.0 server (`mdss mcp`) integrating with Claude Desktop, Cursor, Copilot, Antigravity, and AI agents for local knowledge retrieval.
|
|
786
|
+
- **VS Code / Raycast / Alfred**: see [`integrations/README.md`](./integrations/README.md). Releases are automated — tagging `integrations/vscode/vX.Y.Z` builds and attaches the VSIX to a GitHub Release from source (no binaries are committed; issue #112).
|
|
787
|
+
- **Windows tray icon**: [`integrations/windows/`](./integrations/windows/) — a zero-dependency system-tray companion for `mdss serve` (pure PowerShell + .NET WinForms, built into Windows): state-colored icon, indexing stats on hover (chunks / model / index age), a balloon when your notes get re-indexed. One-command start:
|
|
788
|
+
|
|
789
|
+
```cmd
|
|
790
|
+
integrations\windows\start-mdss.cmd D:\Notes
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
## Contributing
|
|
794
|
+
|
|
795
|
+
Contributions are welcome — bug reports with reproduction steps, docs fixes,
|
|
796
|
+
tests, and features all help. Start with
|
|
797
|
+
[CONTRIBUTING.md](./CONTRIBUTING.md) (dev setup, project tour, conventions)
|
|
798
|
+
and look for issues labeled **`good first issue`** — they are scoped for a
|
|
799
|
+
first-time contributor. By participating you agree to the
|
|
800
|
+
[Code of Conduct](./CODE_OF_CONDUCT.md).
|
|
653
801
|
|
|
654
802
|
## License
|
|
655
803
|
|