seekstone 0.12.0 → 0.12.1

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
@@ -1,10 +1,10 @@
1
1
  # seekstone
2
2
 
3
- **The Obsidian MCP server for Claude — search and edit your vault without burning context.**
3
+ **The Obsidian MCP server that needs no plugin, no running Obsidian app — and doesn't blow your context window.**
4
4
 
5
5
  Seekstone is an Obsidian MCP server that gives Claude (and any [Model Context Protocol](https://modelcontextprotocol.io) client) direct read and write access to your Obsidian vault. No Obsidian app needs to be running, no plugins are required, and nothing leaves your machine.
6
6
 
7
- It reads your vault **directly from disk** instead of routing through the Obsidian Local REST API plugin. The practical difference: a search that returns ~1.75 MB and ~459,000 tokens via the REST plugin returns **~3 KB and ~800 tokens** via Seekstone — a **~575× reduction**. Claude can search and read your entire note library without burning most of its context window on a single tool call.
7
+ It reads your vault **directly from disk** instead of routing through the Obsidian Local REST API plugin, and holds a warm full-text index in-process. The practical difference: searches return in **single-digit milliseconds** with **~2 KB payloads** that stay flat as your vault grows — a broad query that costs tens of megabytes of context through a REST-proxy server (up to **95 MB** at 10k notes) costs ~2 KB through Seekstone, up to a **~47,000× reduction**. Benchmarked against 7 other Obsidian MCP servers across committed 1k/5k/10k-note vaults — [fully reproducible](https://github.com/shaqmughal/seekstone/tree/main/packages/harness), full results at [seekstone.dev/benchmarks](https://seekstone.dev/benchmarks).
8
8
 
9
9
  (Previously also published as `obsidian-mcp-seekstone` — that alias is deprecated; existing installs keep working, but install `seekstone` going forward.)
10
10
 
@@ -16,6 +16,12 @@ It reads your vault **directly from disk** instead of routing through the Obsidi
16
16
 
17
17
  Choose the method that suits you best.
18
18
 
19
+ ### Using an AI agent? Paste this prompt
20
+
21
+ If you use Claude Code, Cursor, or another coding agent, paste this prompt and the agent does the install:
22
+
23
+ > Install the **seekstone** MCP server for this editor. Run `npx -y seekstone init --client code --write` (use `desktop`, `cursor`, or `vscode` for other clients). It auto-detects my Obsidian vault; if it lists several, ask me which one and re-run with `--vault "<path>"`. Relay any errors to me, then tell me to restart this session so the seekstone tools load.
24
+
19
25
  ### Option 1 — One-click (Claude Desktop, no terminal needed)
20
26
 
21
27
  Download `seekstone.mcpb` from [GitHub Releases](https://github.com/shaqmughal/seekstone/releases/latest), double-click it in Claude Desktop, and pick your Obsidian vault folder when prompted. No JSON editing, no terminal, no Node.js setup required.
@@ -28,7 +34,7 @@ Run the setup helper and let Seekstone find your vault automatically:
28
34
  npx -y seekstone init
29
35
  ```
30
36
 
31
- Seekstone reads Obsidian's own vault registry to detect your vault, validates it, and either prints the config to paste or patches Claude Desktop directly:
37
+ Seekstone reads Obsidian's own vault registry to detect your vault, validates it, and either prints the config to paste or patches the client config directly:
32
38
 
33
39
  ```bash
34
40
  # Auto-detect vault, print config to paste
@@ -40,8 +46,10 @@ npx -y seekstone init --write
40
46
  # Specify vault explicitly if you have multiple
41
47
  npx -y seekstone init --vault "/path/to/vault"
42
48
 
43
- # Generate the Claude Code command instead
44
- npx -y seekstone init --client code
49
+ # Auto-configure Claude Code / Cursor / VS Code in one step
50
+ npx -y seekstone init --client code --write
51
+ npx -y seekstone init --client cursor --write
52
+ npx -y seekstone init --client vscode --write
45
53
  ```
46
54
 
47
55
  ### Option 3 — Manual config (Claude Desktop)
@@ -84,17 +92,27 @@ One-click via the [Install in VS Code](https://vscode.dev/redirect?url=vscode:mc
84
92
  code --add-mcp '{"name":"seekstone","command":"npx","args":["-y","seekstone"],"env":{"SEEKSTONE_VAULT":"/absolute/path/to/your/vault"}}'
85
93
  ```
86
94
 
95
+ Or auto-detect and write the workspace config:
96
+
97
+ ```bash
98
+ npx -y seekstone init --client vscode --write
99
+ ```
100
+
87
101
  Other MCP clients (Windsurf, Cline, …) take the Option 3 JSON block in their own MCP config file.
88
102
 
89
103
  ---
90
104
 
91
105
  ## Tools
92
106
 
107
+ 18 tools: 10 read, 8 write.
108
+
93
109
  ### Read
94
110
 
95
111
  | Tool | Description |
96
112
  |---|---|
97
113
  | `search` | Full-text search. Returns ranked excerpts (default ~120 chars, tunable via `excerptLength`), not full notes. Fuzzy, prefix, and phrase queries. |
114
+ | `query_notes` | Structured metadata query. Filter by frontmatter key/value predicates (`eq`, `ne`, `contains`, `exists`, `missing`, `gt`/`gte`/`lt`/`lte`), tag, folder, modified time, and size; sort and select the fields you need. Returns compact rows, not note content. |
115
+ | `context_pack` | Answer-ready context for a natural-language question in one call, hard-capped at a byte budget (default 2 KB): ranked excerpts, linked neighbor notes with one-line summaries, and follow-up source paths — replaces a search → read → get_backlinks round-trip loop. |
98
116
  | `read_note` | Read the full content of a note by vault-relative path. Supports returning a single section, block, or line range. |
99
117
  | `list_notes` | List notes, optionally filtered by folder prefix or tag. |
100
118
  | `list_tags` | List all tags in the vault sorted by usage count (or alphabetically). |
@@ -107,15 +125,17 @@ Other MCP clients (Windsurf, Cline, …) take the Option 3 JSON block in their o
107
125
 
108
126
  | Tool | Description |
109
127
  |---|---|
110
- | `create_note` | Create a note (optional frontmatter + body); parent dirs created automatically. |
111
- | `delete_note` | Permanently delete a note. |
112
- | `move_note` | Move/rename a note; destination dirs created automatically. |
128
+ | `create_note` | Create a note (optional frontmatter + body); parent dirs created automatically. Never clobbers an existing note unless you pass `overwrite: true`. |
129
+ | `delete_note` | Move a note to the vault's `.trash/` folder (Obsidian-compatible, restorable). Pass `permanent: true` for an unrecoverable delete. |
130
+ | `move_note` | Move/rename a note — wikilinks and markdown links in other notes that point at it are rewritten so nothing breaks; destination dirs created automatically. |
113
131
  | `append_note` | Append to a note body without touching frontmatter. |
114
132
  | `patch_frontmatter` | Set/update/delete frontmatter keys without reordering existing keys or changing quote style. |
115
133
  | `patch_note` | Insert text immediately after a heading without touching frontmatter. |
116
134
  | `replace_in_note` | Replace the first occurrence of a word or phrase in the note body. |
117
135
  | `append_periodic_note` | Append to today's periodic note, creating it from a template if it doesn't yet exist. |
118
136
 
137
+ Every edit tool supports optional **compare-and-swap**: pass the `contentHash` you got from `read_note` as `prevHash` and the write fails cleanly if the note changed underneath you, instead of silently discarding the concurrent edit.
138
+
119
139
  ---
120
140
 
121
141
  ## Configuration
@@ -126,6 +146,14 @@ Other MCP clients (Windsurf, Cline, …) take the Option 3 JSON block in their o
126
146
  | `SEEKSTONE_LOG_LEVEL` | no | `error` \| `warn` \| `info` (default) \| `debug`. |
127
147
  | `SEEKSTONE_LOG_FILE` | no | Absolute path; when set, JSON-line logs are appended here (size-rotated). |
128
148
  | `SEEKSTONE_WATCH_POLL` | no | Set to `1` to stat-poll for changes instead of native OS events — reliable on network drives, WSL, containers. |
149
+ | `SEEKSTONE_READ_ONLY` | no | Set to `1` to run read-only: the 8 write tools are unregistered entirely (and rejected if called anyway), so the session provably cannot modify your vault. |
150
+ | `SEEKSTONE_WRITE_PATHS` | no | Comma-separated vault-relative globs (e.g. `journal/**,inbox/*.md`). Writes are permitted only under matching paths; the rest of the vault stays read-only. |
151
+
152
+ ---
153
+
154
+ ## Write safety
155
+
156
+ Giving an AI write access to your notes deserves more than "trust us." Seekstone ships the [Write-Safety Contract](https://github.com/shaqmughal/seekstone/blob/main/docs/WRITE-SAFETY.md): **eight named guarantees, each linked to the code that enforces it and the test that proves it**, verified byte-by-byte in CI on every commit and release — zero network, vault sandbox, byte-identical frontmatter on body edits, atomic writes, no-clobber creates, recoverable deletes, optional compare-and-swap, and write scoping / read-only mode.
129
157
 
130
158
  ---
131
159
 
@@ -135,7 +163,7 @@ Other MCP clients (Windsurf, Cline, …) take the Option 3 JSON block in their o
135
163
  No — Seekstone reads the vault folder from disk directly.
136
164
 
137
165
  **Do I need the Local REST API plugin?**
138
- No — Seekstone bypasses it entirely (that's where the 575× reduction comes from).
166
+ No — Seekstone bypasses it entirely (that's the source of the up-to-~47,000× payload reduction).
139
167
 
140
168
  **How does `seekstone init` find my vault automatically?**
141
169
  It reads Obsidian's own vault registry (`obsidian.json`) — the same file Obsidian uses to track your known vaults. One vault → auto-selected. Multiple → lists them and asks you to pick with `--vault`.
@@ -144,13 +172,13 @@ It reads Obsidian's own vault registry (`obsidian.json`) — the same file Obsid
144
172
  An MCP Bundle — a zip containing the server and its manifest. Claude Desktop installs it with a double-click, no terminal required.
145
173
 
146
174
  **Which AI clients does it support?**
147
- Any MCP-over-stdio client: Claude Desktop, Claude Code, Cursor, Windsurf, Continue, and others.
175
+ Any MCP-over-stdio client: Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Continue, and others.
148
176
 
149
177
  **Does it work on Windows?**
150
178
  Yes — tested on macOS, Linux, and Windows in CI on every commit.
151
179
 
152
180
  **Is it safe?**
153
- No network calls, no telemetry. The vault path is sandboxed — no tool reads or writes outside it.
181
+ No network calls, no telemetry. The vault path is sandboxed — no tool reads or writes outside it. Writes are covered by the tested Write-Safety Contract above, and `SEEKSTONE_READ_ONLY=1` removes the write tools entirely.
154
182
 
155
183
  ---
156
184
 
@@ -163,4 +191,4 @@ No network calls, no telemetry. The vault path is sandboxed — no tool reads or
163
191
 
164
192
  ## License
165
193
 
166
- MIT © Shaq Mughal · [GitHub](https://github.com/shaqmughal/seekstone) · [Issues](https://github.com/shaqmughal/seekstone/issues)
194
+ MIT © Shaq Mughal · [seekstone.dev](https://seekstone.dev) · [GitHub](https://github.com/shaqmughal/seekstone) · [Issues](https://github.com/shaqmughal/seekstone/issues)
package/dist/index.js CHANGED
@@ -643,7 +643,7 @@ async function createNote(ctx2, rawInput) {
643
643
  let raw = "";
644
644
  if (input.frontmatter && Object.keys(input.frontmatter).length > 0) {
645
645
  raw = `---
646
- ${stringifyYaml(input.frontmatter)}---
646
+ ${stringifyYaml(input.frontmatter, { lineWidth: 0 })}---
647
647
  `;
648
648
  }
649
649
  raw += input.content ?? "";
@@ -1158,7 +1158,7 @@ async function patchFrontmatter(ctx2, input) {
1158
1158
  }
1159
1159
  }
1160
1160
  newContent = `---
1161
- ${doc.toString()}---
1161
+ ${doc.toString({ lineWidth: 0 })}---
1162
1162
  ${original}`;
1163
1163
  } else {
1164
1164
  const opensWithCRLF = original.startsWith("---\r\n");
@@ -1183,7 +1183,7 @@ ${original}`;
1183
1183
  }
1184
1184
  const head = opensWithCRLF ? "---\r\n" : "---\n";
1185
1185
  const tail = opensWithCRLF ? "---\r\n" : "---\n";
1186
- newContent = `${head}${doc.toString()}${tail}${fm.body}`;
1186
+ newContent = `${head}${doc.toString({ lineWidth: 0 })}${tail}${fm.body}`;
1187
1187
  }
1188
1188
  await atomicWrite(absPath, newContent);
1189
1189
  const cached = ctx2.notes.get(input.path);
@@ -3116,7 +3116,7 @@ function startWatcher(ctx2, log2, opts) {
3116
3116
  }
3117
3117
 
3118
3118
  // src/index.ts
3119
- var VERSION = true ? "0.12.0" : "0.0.0-dev";
3119
+ var VERSION = true ? "0.12.1" : "0.0.0-dev";
3120
3120
  var intent = parseCliIntent(process.argv.slice(2));
3121
3121
  if (intent === "version") {
3122
3122
  process.stdout.write(`${VERSION}