obelisk-mcp 0.1.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/LICENSE +21 -0
- package/README.md +211 -0
- package/dist/cli.mjs +8342 -0
- package/dist/mcp.mjs +43807 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zachary Hannum
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Obelisk
|
|
2
|
+
|
|
3
|
+
Inline comments and GitHub-style suggested edits for [Obsidian](https://obsidian.md).
|
|
4
|
+
|
|
5
|
+
Select a passage, leave a comment on it, and optionally propose a replacement
|
|
6
|
+
that can be applied with one click. Comments are stored in the note's own
|
|
7
|
+
frontmatter, so they travel with the file through sync, git, export and rename.
|
|
8
|
+
There is no sidecar database to fall out of step with the vault.
|
|
9
|
+
|
|
10
|
+
> An *obelus* (†) was the mark ancient editors drew in the margin of a
|
|
11
|
+
> manuscript to say: this passage is disputed.
|
|
12
|
+
|
|
13
|
+
## Status
|
|
14
|
+
|
|
15
|
+
Pre-release. Anchoring, decoration, the sidebar, suggested edits and the agent
|
|
16
|
+
integration are implemented. Not yet released or submitted to the community
|
|
17
|
+
plugin list, and not yet exercised against a large vault.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Comment on any passage.** Select text, right-click, *Add comment*.
|
|
22
|
+
- **Markdown everywhere.** Comments and replies are ordinary markdown, with a
|
|
23
|
+
Write/Preview pair while you type. Links, lists, callouts, embeds and math
|
|
24
|
+
render in the sidebar exactly as they would in a note.
|
|
25
|
+
- **Suggested edits.** A proposal is a fenced ` ```suggestion ` block *inside*
|
|
26
|
+
the comment, the way GitHub does it. It renders as a diff against the quoted
|
|
27
|
+
passage with an Apply button, so one comment can explain itself and propose a
|
|
28
|
+
change, and a reply can offer a counter-proposal. Applying is refused if the
|
|
29
|
+
underlying text has changed since. It also resolves the comment, and *Reopen*
|
|
30
|
+
is there if the comment asked something the edit did not answer.
|
|
31
|
+
- **Sidebar.** All of a note's comments in document order, or newest first from
|
|
32
|
+
the sort toggle; click one to scroll the editor to it. Chips filter to open
|
|
33
|
+
comments, to comments carrying a suggestion, or to one agent's review pass.
|
|
34
|
+
Resolved comments stay in the list, greyed rather than hidden, since they are
|
|
35
|
+
still highlighted in the note.
|
|
36
|
+
- **In-text highlighting.** Commented passages are highlighted, with a †
|
|
37
|
+
marker that opens the comment in the sidebar.
|
|
38
|
+
- **Stored in frontmatter.** Plain YAML under an `obelisk` key. Readable,
|
|
39
|
+
diffable, portable.
|
|
40
|
+
- **Survives editing.** A comment is anchored to the text it quotes, so it
|
|
41
|
+
keeps up with edits anywhere else in the note, including ones made outside
|
|
42
|
+
Obsidian. Edit or delete the quoted passage itself and the comment detaches:
|
|
43
|
+
flagged in the sidebar, highlighting nothing, never moved onto a different
|
|
44
|
+
passage and never dropped. Restore the text and it reattaches. Resolved
|
|
45
|
+
comments are exempt from the flag, because a resolved comment usually
|
|
46
|
+
detaches when the edit it asked for is made.
|
|
47
|
+
- **The editor you already use.** Comments are written in Obsidian's own
|
|
48
|
+
markdown editor, so Cmd+B, list continuation, `[[` autocompletion and live
|
|
49
|
+
preview work in a comment exactly as they do in a note.
|
|
50
|
+
- **Threaded replies.** On any comment, stored alongside it, and markdown all
|
|
51
|
+
the way down.
|
|
52
|
+
- **Editable.** Rewrite a comment or a reply in the same composer that wrote
|
|
53
|
+
it, suggestion button and all, from the card's *Edit* button or by
|
|
54
|
+
right-clicking the passage. Edited bodies are marked as such; the anchor is
|
|
55
|
+
left alone, so changing what you said never changes what you said it about.
|
|
56
|
+
- **Deletable.** A whole comment from the card's *Delete*, or a single reply
|
|
57
|
+
from the trash icon in its header, so striking one remark out of a thread
|
|
58
|
+
does not take the conversation with it. Both offer an undo rather than a
|
|
59
|
+
confirmation dialog.
|
|
60
|
+
- **Open to agents.** A command-line tool and an MCP server can read and write
|
|
61
|
+
the same comments from outside Obsidian, so a model can review a note into
|
|
62
|
+
your sidebar, or answer the comments you left for it. Its comments are
|
|
63
|
+
badged, and a whole review pass is one chip in the header with a *dismiss
|
|
64
|
+
all* on it. See below.
|
|
65
|
+
|
|
66
|
+
## Data format
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
---
|
|
70
|
+
obelisk:
|
|
71
|
+
- id: cq7fk2m9x
|
|
72
|
+
author: zach
|
|
73
|
+
created: 2026-08-29T14:02:11.000Z
|
|
74
|
+
body: |-
|
|
75
|
+
This paragraph does two things at once.
|
|
76
|
+
|
|
77
|
+
```suggestion
|
|
78
|
+
The horse bolted.
|
|
79
|
+
```
|
|
80
|
+
anchor:
|
|
81
|
+
from: { line: 12, col: 0 }
|
|
82
|
+
to: { line: 12, col: 47 }
|
|
83
|
+
quote: The horse, which had been standing there, bolted.
|
|
84
|
+
---
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
A comment is one piece of markdown. A proposed edit is a ` ```suggestion `
|
|
88
|
+
fenced block inside it, whose content replaces exactly the anchored range when
|
|
89
|
+
applied, so one comment can hold prose, a link and a proposal at once.
|
|
90
|
+
|
|
91
|
+
The `quote` is what a comment is anchored by. The line/column range records
|
|
92
|
+
where the passage was when the comment was written. It orders the sidebar and
|
|
93
|
+
breaks ties when a quote appears twice, and is never rewritten. Lines are
|
|
94
|
+
counted from the first line *after* the frontmatter block, so adding a comment
|
|
95
|
+
never invalidates the others.
|
|
96
|
+
|
|
97
|
+
A comment written by a model carries one more key, `origin`, holding the model
|
|
98
|
+
and an id shared by every comment in that review pass. Its absence means a
|
|
99
|
+
person wrote it, so nothing already in a vault needs migrating.
|
|
100
|
+
|
|
101
|
+
## Agents
|
|
102
|
+
|
|
103
|
+
The same comments, from outside Obsidian. A model reviews a note and its
|
|
104
|
+
remarks appear in the sidebar of the note you already have open; or you leave
|
|
105
|
+
comments asking for things and a model reads them, makes the edits, and
|
|
106
|
+
resolves them.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm run build
|
|
110
|
+
npm link # puts `obelisk` and `obelisk-mcp` on PATH
|
|
111
|
+
|
|
112
|
+
obelisk list note.md
|
|
113
|
+
obelisk comment note.md --quote "The horse, which had been standing there, bolted." \
|
|
114
|
+
--body "Two clauses fighting over one sentence." --run r7k2mq
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
For an agent that speaks MCP, register the server with it. Nothing is published
|
|
118
|
+
to npm yet, so point at the built file:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm run build # from the root of this repo
|
|
122
|
+
claude mcp add obelisk --scope user -- \
|
|
123
|
+
node "$PWD/dist/mcp.mjs" --vault /path/to/your/vault
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`$PWD` fills in this repo's path, so the vault is the only one you have to
|
|
127
|
+
supply, and it has to be a real one. A path that does not exist makes `node`
|
|
128
|
+
exit before it says anything, which reaches the agent as `CONNECTION_CLOSED`
|
|
129
|
+
and names nothing; if you see that, the path in `claude mcp get obelisk` is the
|
|
130
|
+
first thing to check.
|
|
131
|
+
|
|
132
|
+
Two things bite here, both of them silent:
|
|
133
|
+
|
|
134
|
+
- **Scope.** `claude mcp add` defaults to `local`, which files the server under
|
|
135
|
+
the directory you happened to run it in. Run it from this repo and you get a
|
|
136
|
+
server that exists only while you are in this repo, the one place you have no
|
|
137
|
+
notes to comment on. `--scope user` registers it everywhere; `--scope
|
|
138
|
+
project` writes a `.mcp.json` in the vault instead, if the vault is a repo
|
|
139
|
+
and everyone working in it should get the server.
|
|
140
|
+
- **`npx obelisk-mcp` only works inside this repo.** The package is not on npm
|
|
141
|
+
yet, and inside the repo npx resolves the bin out of the local
|
|
142
|
+
`package.json`. Anywhere else it goes to the registry and 404s. Use the
|
|
143
|
+
absolute path above, or `npm link` first and register `obelisk-mcp`.
|
|
144
|
+
|
|
145
|
+
Both paths have to be absolute. The server is spawned without a shell, so a `~`
|
|
146
|
+
in a config file stays a literal tilde, and the process inherits the agent's
|
|
147
|
+
working directory rather than the vault's.
|
|
148
|
+
|
|
149
|
+
**There is nothing to start.** It speaks MCP over stdio: the agent spawns a
|
|
150
|
+
process when a session opens and kills it when the session ends, so
|
|
151
|
+
`obelisk-mcp` is never run by hand and there is no port and no daemon. One
|
|
152
|
+
process per session is also what makes a session's comments share one run chip
|
|
153
|
+
in the sidebar. Rebuild while a session is open and it keeps the old process;
|
|
154
|
+
reconnect it from `/mcp`, or start a new session.
|
|
155
|
+
|
|
156
|
+
To check the registration, `claude mcp list`, or `/mcp` inside a session. To
|
|
157
|
+
check the server itself with no agent in the way:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
printf '%s\n' \
|
|
161
|
+
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
|
|
162
|
+
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
|
|
163
|
+
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
|
|
164
|
+
| node dist/mcp.mjs --vault /abs/path/to/vault
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
That should print the handshake and then the four tools. A server that fails
|
|
168
|
+
this fails the same way for an agent, which is worth knowing before you go
|
|
169
|
+
looking at the agent's end of it.
|
|
170
|
+
|
|
171
|
+
For an agent that does not speak MCP, paste
|
|
172
|
+
[`docs/agents-fragment.md`](docs/agents-fragment.md) into the vault's
|
|
173
|
+
`AGENTS.md` or `CLAUDE.md`.
|
|
174
|
+
|
|
175
|
+
The one rule worth knowing: **the quote is the anchor.** A line number is never
|
|
176
|
+
one. A model asked for a line will produce a plausible wrong number, and a
|
|
177
|
+
plausible wrong number attaches a comment to the wrong paragraph without ever
|
|
178
|
+
looking like an error, so `--near-line` only picks between identical quotes and
|
|
179
|
+
takes a number copied out of `obelisk list`. `--quote` has to appear in the
|
|
180
|
+
note character for character, and if it does not, or appears twice, nothing is
|
|
181
|
+
written and the tool says so. `obelisk list` prints the body line-numbered so
|
|
182
|
+
the exact text is there to copy.
|
|
183
|
+
|
|
184
|
+
Writes are frontmatter-only and leave the body byte-identical, so they are safe
|
|
185
|
+
while the note is open in Obsidian; a write also re-reads the file first and
|
|
186
|
+
refuses if it changed underneath.
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm install
|
|
192
|
+
npm run dev # watch build, plugin only
|
|
193
|
+
npm run build # typecheck, then main.js plus dist/cli.mjs and dist/mcp.mjs
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`src/core/` is the half that does not import Obsidian: the model, the anchor
|
|
197
|
+
arithmetic, the YAML and the four verbs. The plugin, the CLI and the MCP server
|
|
198
|
+
are three front ends over it.
|
|
199
|
+
|
|
200
|
+
`site/` is the documentation site, an Astro project of its own with its own
|
|
201
|
+
`npm install`. It is not part of `npm run build`.
|
|
202
|
+
|
|
203
|
+
Symlink the repo into a test vault to try it:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
ln -s "$PWD" /path/to/vault/.obsidian/plugins/obelisk
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT
|