obelisk-mcp 0.1.3 → 0.1.5
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 +28 -24
- package/dist/cli.mjs +5 -2
- package/dist/mcp.mjs +8 -5
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -12,10 +12,15 @@ rename.
|
|
|
12
12
|
|
|
13
13
|
## Status
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
integration are implemented. The
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
Early. Anchoring, decoration, the sidebar, suggested edits and the agent
|
|
16
|
+
integration are implemented. The plugin is in the Obsidian community plugin
|
|
17
|
+
directory, and the CLI and the MCP server are on npm as `obelisk-mcp`. None of
|
|
18
|
+
it has been exercised against a large vault.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Search for **Obelisk** in *Settings → Community plugins → Browse*, install it,
|
|
23
|
+
and enable it. Updates come from the same panel.
|
|
19
24
|
|
|
20
25
|
## Features
|
|
21
26
|
|
|
@@ -32,7 +37,7 @@ and none of it has been exercised against a large vault.
|
|
|
32
37
|
- **Sidebar.** All of a note's comments in document order, or newest first from
|
|
33
38
|
the sort toggle. Click one to scroll the editor to it. Chips filter to open
|
|
34
39
|
comments, to comments carrying a suggestion, or to one agent's review pass.
|
|
35
|
-
Resolved comments stay in the list,
|
|
40
|
+
Resolved comments stay in the list, grayed rather than hidden, since they are
|
|
36
41
|
still highlighted in the note.
|
|
37
42
|
- **In-text highlighting.** Commented passages are highlighted, with a †
|
|
38
43
|
marker that opens the comment in the sidebar.
|
|
@@ -58,9 +63,9 @@ and none of it has been exercised against a large vault.
|
|
|
58
63
|
from the trash icon in its header, so striking one remark out of a thread
|
|
59
64
|
does not take the conversation with it. Both offer an undo rather than a
|
|
60
65
|
confirmation dialog.
|
|
61
|
-
- **Open to agents.** A command-line tool and an MCP server
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
- **Open to agents.** A command-line tool and an MCP server read and write the
|
|
67
|
+
same comments from outside Obsidian, so a model can review a note into the
|
|
68
|
+
sidebar, or answer the comments left for it. Its comments are
|
|
64
69
|
badged, and a whole review pass is one chip in the header with a *dismiss
|
|
65
70
|
all* on it. See below.
|
|
66
71
|
|
|
@@ -125,12 +130,12 @@ agent is running in, and an absolute path reaches a note in one it is not. The
|
|
|
125
130
|
server is listed in the official MCP registry as `io.github.zachhannum/obelisk`
|
|
126
131
|
for a client that installs from there.
|
|
127
132
|
|
|
128
|
-
**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
**The agent starts it.** It speaks MCP over stdio: the agent spawns a process
|
|
134
|
+
when a session opens and kills it when the session ends, so there is no port
|
|
135
|
+
and no daemon. One process per session is also what makes a session's comments
|
|
136
|
+
share one run chip in the sidebar. A session keeps the process it spawned, so a
|
|
137
|
+
new version of the package arrives at the next one. To take it sooner,
|
|
138
|
+
reconnect from `/mcp`.
|
|
134
139
|
|
|
135
140
|
To check the registration, `claude mcp list`, or `/mcp` inside a session. To
|
|
136
141
|
check the server itself with no agent in the way:
|
|
@@ -143,22 +148,21 @@ printf '%s\n' \
|
|
|
143
148
|
| npx -y obelisk-mcp
|
|
144
149
|
```
|
|
145
150
|
|
|
146
|
-
That
|
|
147
|
-
|
|
148
|
-
looking at the agent's end of it.
|
|
151
|
+
That prints the handshake and then the four tools. A server that fails this
|
|
152
|
+
fails the same way for an agent.
|
|
149
153
|
|
|
150
154
|
For an agent that does not speak MCP, paste
|
|
151
155
|
[`docs/agents-fragment.md`](docs/agents-fragment.md) into the vault's
|
|
152
156
|
`AGENTS.md` or `CLAUDE.md`.
|
|
153
157
|
|
|
154
158
|
The one rule worth knowing: **the quote is the anchor.** A line number is never
|
|
155
|
-
one. A model asked for a line
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
one. A model asked for a line produces a plausible wrong number, which attaches
|
|
160
|
+
a comment to the wrong paragraph without looking like an error, so
|
|
161
|
+
`--near-line` only picks between identical quotes and takes a number copied out
|
|
162
|
+
of `obelisk list`. `--quote` has to appear in the note character for character,
|
|
163
|
+
and if it does not, or appears twice, nothing is written and the reason is
|
|
164
|
+
printed. `obelisk list` prints the body numbered so the exact text is there to
|
|
165
|
+
copy.
|
|
162
166
|
|
|
163
167
|
Writes are frontmatter-only and leave the body byte-identical, so they are safe
|
|
164
168
|
while the note is open in Obsidian. A write also re-reads the file first and
|
package/dist/cli.mjs
CHANGED
|
@@ -7796,7 +7796,7 @@ function readNote(text, source = "<note>") {
|
|
|
7796
7796
|
const frame = frameFrom(text);
|
|
7797
7797
|
const doc = frontmatterOf(text, frame, source);
|
|
7798
7798
|
const fm = doc?.toJS();
|
|
7799
|
-
const data =
|
|
7799
|
+
const data = isRecord2(fm) ? fm : {};
|
|
7800
7800
|
return {
|
|
7801
7801
|
text,
|
|
7802
7802
|
frame,
|
|
@@ -7844,6 +7844,9 @@ function frontmatterOf(text, frame, source) {
|
|
|
7844
7844
|
}
|
|
7845
7845
|
return doc;
|
|
7846
7846
|
}
|
|
7847
|
+
function isRecord2(value) {
|
|
7848
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7849
|
+
}
|
|
7847
7850
|
function isEmptyDoc(doc) {
|
|
7848
7851
|
const value = doc.toJS();
|
|
7849
7852
|
return value == null || typeof value === "object" && Object.keys(value).length === 0;
|
|
@@ -7983,7 +7986,7 @@ The closest thing in the note is:
|
|
|
7983
7986
|
Quote that, character for character.` : "";
|
|
7984
7987
|
return fail(
|
|
7985
7988
|
"quote-not-found",
|
|
7986
|
-
"That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation,
|
|
7989
|
+
"That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation, capitalization and spacing." + hint
|
|
7987
7990
|
);
|
|
7988
7991
|
}
|
|
7989
7992
|
function looseMatch(body, quote) {
|
package/dist/mcp.mjs
CHANGED
|
@@ -42976,7 +42976,7 @@ var StdioServerTransport = class {
|
|
|
42976
42976
|
};
|
|
42977
42977
|
|
|
42978
42978
|
// package.json
|
|
42979
|
-
var version2 = "0.1.
|
|
42979
|
+
var version2 = "0.1.5";
|
|
42980
42980
|
|
|
42981
42981
|
// src/bin/vault.ts
|
|
42982
42982
|
import { existsSync, statSync } from "node:fs";
|
|
@@ -43430,7 +43430,7 @@ function readNote(text2, source = "<note>") {
|
|
|
43430
43430
|
const frame = frameFrom(text2);
|
|
43431
43431
|
const doc = frontmatterOf(text2, frame, source);
|
|
43432
43432
|
const fm = doc?.toJS();
|
|
43433
|
-
const data =
|
|
43433
|
+
const data = isRecord2(fm) ? fm : {};
|
|
43434
43434
|
return {
|
|
43435
43435
|
text: text2,
|
|
43436
43436
|
frame,
|
|
@@ -43478,6 +43478,9 @@ function frontmatterOf(text2, frame, source) {
|
|
|
43478
43478
|
}
|
|
43479
43479
|
return doc;
|
|
43480
43480
|
}
|
|
43481
|
+
function isRecord2(value) {
|
|
43482
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43483
|
+
}
|
|
43481
43484
|
function isEmptyDoc(doc) {
|
|
43482
43485
|
const value = doc.toJS();
|
|
43483
43486
|
return value == null || typeof value === "object" && Object.keys(value).length === 0;
|
|
@@ -43600,7 +43603,7 @@ The closest thing in the note is:
|
|
|
43600
43603
|
Quote that, character for character.` : "";
|
|
43601
43604
|
return fail(
|
|
43602
43605
|
"quote-not-found",
|
|
43603
|
-
"That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation,
|
|
43606
|
+
"That text is not in the note. The quote has to be verbatim \u2014 copy it straight out of the listed note body, including its punctuation, capitalization and spacing." + hint
|
|
43604
43607
|
);
|
|
43605
43608
|
}
|
|
43606
43609
|
function looseMatch(body, quote) {
|
|
@@ -43709,7 +43712,7 @@ server.registerTool(
|
|
|
43709
43712
|
"obelisk_comment",
|
|
43710
43713
|
{
|
|
43711
43714
|
title: "Comment on a passage",
|
|
43712
|
-
description: "Leave a comment on one passage of a note. It appears in the reader's Obsidian sidebar, badged as coming from an agent.\n\n`quote` is the anchor and must appear in the note verbatim \u2014 the same characters, punctuation,
|
|
43715
|
+
description: "Leave a comment on one passage of a note. It appears in the reader's Obsidian sidebar, badged as coming from an agent.\n\n`quote` is the anchor and must appear in the note verbatim \u2014 the same characters, punctuation, capitalization and spacing. Copy it from obelisk_list; do not retype it or tidy it up. There is no way to pass a line or column, and you should not try to count them: the quote is the anchor and the tool does the arithmetic. If the quote is not found, or is found more than once, nothing is written and you are told which.\n\n`body` is markdown. To propose a specific rewrite, put it in a ```suggestion fenced block inside the body \u2014 its contents replace exactly the quoted passage when the reader clicks Apply, so a comment can explain itself and propose the change at once.",
|
|
43713
43716
|
inputSchema: {
|
|
43714
43717
|
note: noteArg,
|
|
43715
43718
|
quote: external_exports.string().describe(
|
|
@@ -43767,7 +43770,7 @@ server.registerTool(
|
|
|
43767
43770
|
"obelisk_resolve",
|
|
43768
43771
|
{
|
|
43769
43772
|
title: "Resolve or reopen a comment",
|
|
43770
|
-
description: "Mark a comment settled \u2014 the reader's sidebar
|
|
43773
|
+
description: "Mark a comment settled \u2014 the reader's sidebar grays it out and drops it from the open count.\n\nThis is what closes the loop on a comment a person left for you: read it, make the edit in the note yourself, then resolve it. Resolving a comment you did not write is allowed, but it records that what the comment asked for has been done, so resolve only once it has.",
|
|
43771
43774
|
inputSchema: {
|
|
43772
43775
|
note: noteArg,
|
|
43773
43776
|
id: external_exports.string().describe("The comment's id, from obelisk_list."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "obelisk-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "The Obelisk CLI and MCP server: comment on a passage of an Obsidian note, and propose suggested edits, from outside the app.",
|
|
5
5
|
"mcpName": "io.github.zachhannum/obelisk",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"dev": "node esbuild.config.mjs",
|
|
17
|
+
"lint": "eslint --max-warnings 0 .",
|
|
17
18
|
"build": "tsc --noEmit --skipLibCheck && node esbuild.config.mjs production",
|
|
18
19
|
"prepublishOnly": "npm run build",
|
|
19
20
|
"shots": "npm run build && playwright test",
|
|
@@ -28,10 +29,13 @@
|
|
|
28
29
|
"@playwright/test": "^1.62.1",
|
|
29
30
|
"@types/node": "^20.14.0",
|
|
30
31
|
"esbuild": "^0.21.5",
|
|
31
|
-
"
|
|
32
|
+
"eslint": "9.37.0",
|
|
33
|
+
"eslint-plugin-obsidianmd": "0.4.1",
|
|
34
|
+
"obsidian": "^1.13.1",
|
|
32
35
|
"obsidian-launcher": "^3.2.0",
|
|
33
36
|
"tslib": "^2.6.3",
|
|
34
37
|
"typescript": "^5.5.0",
|
|
38
|
+
"typescript-eslint": "8.61.1",
|
|
35
39
|
"yaml": "^2.9.0",
|
|
36
40
|
"zod": "^4.5.4"
|
|
37
41
|
},
|