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 +40 -12
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# seekstone
|
|
2
2
|
|
|
3
|
-
**The Obsidian MCP server
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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` |
|
|
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
|
|
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.
|
|
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}
|