smoking-mirror 1.0.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 +259 -0
- package/dist/index.js +1196 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ben Cassie
|
|
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,259 @@
|
|
|
1
|
+
# smoking-mirror
|
|
2
|
+
|
|
3
|
+
> Obsidian vault intelligence for Claude Code - graph queries, wikilink suggestions, and vault health
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/smoking-mirror)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## What is smoking-mirror?
|
|
9
|
+
|
|
10
|
+
"Smoking mirror" (tezcatl in Nahuatl) is the literal translation of "obsidian". This MCP server acts as a reflective surface that reveals the hidden structure of your Obsidian vault to Claude Code.
|
|
11
|
+
|
|
12
|
+
**First-to-market** features:
|
|
13
|
+
- **Graph Intelligence** - Backlinks, forward links, orphan detection, hub analysis
|
|
14
|
+
- **Wikilink Services** - Auto-suggest entities, validate links
|
|
15
|
+
- **Vault Health** - Broken link detection, comprehensive statistics
|
|
16
|
+
- **Frontmatter Queries** - Search notes by metadata, tags, folders
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### Quick Install (Claude Code CLI)
|
|
21
|
+
|
|
22
|
+
**macOS / Linux:**
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
claude mcp add smoking-mirror -e OBSIDIAN_VAULT_PATH=/path/to/your/vault -- npx -y smoking-mirror
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Windows:**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
claude mcp add smoking-mirror -e OBSIDIAN_VAULT_PATH=C:\path\to\your\vault -- cmd /c npx -y smoking-mirror
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Manual Configuration
|
|
35
|
+
|
|
36
|
+
Alternatively, add to your `.mcp.json` file:
|
|
37
|
+
|
|
38
|
+
**Windows:**
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"smoking-mirror": {
|
|
44
|
+
"command": "cmd",
|
|
45
|
+
"args": ["/c", "npx", "-y", "smoking-mirror"],
|
|
46
|
+
"env": {
|
|
47
|
+
"OBSIDIAN_VAULT_PATH": "C:\\Users\\you\\Documents\\MyVault"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**macOS / Linux:**
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"smoking-mirror": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["-y", "smoking-mirror"],
|
|
62
|
+
"env": {
|
|
63
|
+
"OBSIDIAN_VAULT_PATH": "/Users/you/Documents/MyVault"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Verify Installation
|
|
71
|
+
|
|
72
|
+
After adding, verify with:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
claude mcp list
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You should see `smoking-mirror` in the list of configured servers.
|
|
79
|
+
|
|
80
|
+
## Tools
|
|
81
|
+
|
|
82
|
+
### Graph Intelligence
|
|
83
|
+
|
|
84
|
+
#### `get_backlinks`
|
|
85
|
+
Find all notes that link TO a specific note.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Input: { path: "projects/My Project.md" }
|
|
89
|
+
Output: [{ source: "daily/2024-01-15.md", line: 42, context: "Working on [[My Project]] today" }]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### `get_forward_links`
|
|
93
|
+
Find all notes that a specific note links TO.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
Input: { path: "projects/My Project.md" }
|
|
97
|
+
Output: [{ target: "Team Members", exists: true, resolved_path: "people/Team Members.md" }]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `find_orphan_notes`
|
|
101
|
+
Find notes with no incoming links (potential cleanup candidates).
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
Input: { folder: "archive" } // optional folder filter
|
|
105
|
+
Output: [{ path: "archive/old-idea.md", title: "old-idea", modified: "2024-01-01" }]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `find_hub_notes`
|
|
109
|
+
Find highly connected notes (potential MOCs or index notes).
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Input: { min_links: 10 } // minimum total connections
|
|
113
|
+
Output: [{ path: "MOC.md", backlink_count: 45, forward_link_count: 23, total_connections: 68 }]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Wikilink Services
|
|
117
|
+
|
|
118
|
+
#### `suggest_wikilinks`
|
|
119
|
+
Analyze text and suggest where wikilinks could be added based on existing notes.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Input: { text: "Meeting with John about the API project" }
|
|
123
|
+
Output: [
|
|
124
|
+
{ entity: "John", start: 13, end: 17, target: "people/John Smith.md" },
|
|
125
|
+
{ entity: "API project", start: 28, end: 39, target: "projects/API Project.md" }
|
|
126
|
+
]
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### `validate_links`
|
|
130
|
+
Check wikilinks in a note (or all notes) and report broken links.
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
Input: { path: "projects/My Project.md" } // optional, validates all if omitted
|
|
134
|
+
Output: [{ source: "projects/My Project.md", target: "Missing Note", line: 15, exists: false }]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Vault Health
|
|
138
|
+
|
|
139
|
+
#### `get_vault_stats`
|
|
140
|
+
Get comprehensive statistics about your vault.
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
Output: {
|
|
144
|
+
total_notes: 1444,
|
|
145
|
+
total_links: 20373,
|
|
146
|
+
total_tags: 431,
|
|
147
|
+
orphan_notes: 971,
|
|
148
|
+
broken_links: 8565,
|
|
149
|
+
average_links_per_note: 14.11,
|
|
150
|
+
most_linked_notes: [...],
|
|
151
|
+
top_tags: [...],
|
|
152
|
+
folders: [...]
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `find_broken_links`
|
|
157
|
+
Find all wikilinks that point to non-existent notes.
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Input: { folder: "projects" } // optional folder filter
|
|
161
|
+
Output: [{ source: "projects/Old.md", target: "Deleted Note", line: 5 }]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Query
|
|
165
|
+
|
|
166
|
+
#### `search_notes`
|
|
167
|
+
Search notes by frontmatter fields, tags, folders, or title. Covers ~80% of Dataview use cases.
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
Input: {
|
|
171
|
+
has_tag: "project",
|
|
172
|
+
where: { status: "active" },
|
|
173
|
+
folder: "work",
|
|
174
|
+
sort_by: "modified",
|
|
175
|
+
limit: 10
|
|
176
|
+
}
|
|
177
|
+
Output: [{ path: "work/Current.md", title: "Current", frontmatter: {...}, tags: [...] }]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Parameters:**
|
|
181
|
+
- `where` - Frontmatter key-value filters (e.g., `{ type: "book", rating: 5 }`)
|
|
182
|
+
- `has_tag` - Filter by single tag
|
|
183
|
+
- `has_any_tag` - Filter by any of these tags
|
|
184
|
+
- `has_all_tags` - Filter by all of these tags
|
|
185
|
+
- `folder` - Limit to notes in this folder
|
|
186
|
+
- `title_contains` - Filter by title substring
|
|
187
|
+
- `sort_by` - Sort by "modified", "created", or "title"
|
|
188
|
+
- `order` - "asc" or "desc"
|
|
189
|
+
- `limit` - Maximum results to return
|
|
190
|
+
|
|
191
|
+
## Architecture
|
|
192
|
+
|
|
193
|
+
- **File-first**: Parses markdown directly, no database required
|
|
194
|
+
- **Works offline**: No connection to Obsidian needed
|
|
195
|
+
- **Fast startup**: <2s for 1500+ notes
|
|
196
|
+
- **Memory efficient**: ~50MB for large vaults
|
|
197
|
+
- **Parallel parsing**: Processes files in batches for speed
|
|
198
|
+
|
|
199
|
+
### Performance
|
|
200
|
+
|
|
201
|
+
| Vault Size | Startup Time | Memory |
|
|
202
|
+
|------------|--------------|--------|
|
|
203
|
+
| 100 notes | <200ms | ~20MB |
|
|
204
|
+
| 500 notes | <500ms | ~30MB |
|
|
205
|
+
| 1500 notes | <2s | ~50MB |
|
|
206
|
+
| 5000 notes | <5s | ~100MB |
|
|
207
|
+
|
|
208
|
+
### Error Handling
|
|
209
|
+
|
|
210
|
+
- Malformed YAML frontmatter: Gracefully skipped, file treated as content
|
|
211
|
+
- Binary files: Detected and skipped
|
|
212
|
+
- Empty files: Indexed with no links/tags
|
|
213
|
+
- Large files (>10MB): Skipped with warning
|
|
214
|
+
- Timeout protection: 5-minute default for vault indexing
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Install dependencies
|
|
220
|
+
bun install
|
|
221
|
+
|
|
222
|
+
# Run in development mode
|
|
223
|
+
OBSIDIAN_VAULT_PATH=/path/to/vault bun run dev
|
|
224
|
+
|
|
225
|
+
# Build for distribution
|
|
226
|
+
bun run build
|
|
227
|
+
|
|
228
|
+
# Test with MCP inspector
|
|
229
|
+
bun run inspect
|
|
230
|
+
|
|
231
|
+
# Run tests
|
|
232
|
+
bun test
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Why not Dataview?
|
|
236
|
+
|
|
237
|
+
We initially planned to use `obsidian-dataview` as a library, but discovered it requires Obsidian's internal `CachedMetadata` API and cannot run standalone. See [this discussion](https://github.com/blacksmithgu/obsidian-dataview/discussions/1811).
|
|
238
|
+
|
|
239
|
+
Instead, smoking-mirror:
|
|
240
|
+
- Parses markdown files directly using `gray-matter`
|
|
241
|
+
- Builds its own in-memory graph index
|
|
242
|
+
- Provides ~80% of Dataview functionality with simpler syntax
|
|
243
|
+
|
|
244
|
+
## Roadmap
|
|
245
|
+
|
|
246
|
+
- [ ] MarkdownDB integration for SQL-like queries
|
|
247
|
+
- [ ] Watch mode for incremental updates
|
|
248
|
+
- [ ] `rename_with_links` - Safe note renaming with reference updates
|
|
249
|
+
- [ ] Obsidian REST API integration for live Dataview queries
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
MIT - Ben Cassie
|
|
254
|
+
|
|
255
|
+
## Related
|
|
256
|
+
|
|
257
|
+
- [Model Context Protocol](https://modelcontextprotocol.io/)
|
|
258
|
+
- [Claude Code](https://claude.ai/code)
|
|
259
|
+
- [Obsidian](https://obsidian.md/)
|