obsidian-native-mcp 0.2.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/.github/workflows/ci.yml +69 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +3 -0
- package/.prettierrc +7 -0
- package/.releaserc.json +24 -0
- package/DEVELOPER.md +158 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/handlers/prompts.js +127 -0
- package/dist/handlers/prompts.js.map +1 -0
- package/dist/handlers/tools.js +113 -0
- package/dist/handlers/tools.js.map +1 -0
- package/dist/mcp/http-transport.js +124 -0
- package/dist/mcp/http-transport.js.map +1 -0
- package/dist/mcp/protocol.js +38 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/server.js +257 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio-transport.js +41 -0
- package/dist/mcp/stdio-transport.js.map +1 -0
- package/dist/mcp/transport.js +3 -0
- package/dist/mcp/transport.js.map +1 -0
- package/dist/plugin/main.js +1201 -0
- package/dist/plugin/main.js.map +1 -0
- package/dist/plugin/manifest.json +10 -0
- package/dist/plugin/settings.js +63 -0
- package/dist/plugin/settings.js.map +1 -0
- package/dist/utils/fs-utils.js +268 -0
- package/dist/utils/fs-utils.js.map +1 -0
- package/dist/utils/search.js +62 -0
- package/dist/utils/search.js.map +1 -0
- package/dist/utils/vaults.js +165 -0
- package/dist/utils/vaults.js.map +1 -0
- package/eslint.config.mjs +16 -0
- package/manifest.json +10 -0
- package/package.json +48 -0
- package/scripts/build-plugin.mjs +35 -0
- package/scripts/sync-version.cjs +12 -0
- package/src/cli/index.ts +25 -0
- package/src/handlers/prompts.ts +148 -0
- package/src/handlers/tools.ts +146 -0
- package/src/mcp/http-transport.ts +138 -0
- package/src/mcp/protocol.ts +69 -0
- package/src/mcp/server.ts +272 -0
- package/src/mcp/stdio-transport.ts +43 -0
- package/src/mcp/transport.ts +8 -0
- package/src/plugin/main.ts +91 -0
- package/src/plugin/settings.ts +69 -0
- package/src/utils/fs-utils.ts +358 -0
- package/src/utils/search.ts +84 -0
- package/src/utils/vaults.ts +175 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
name: Lint and Type Check
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: "22"
|
|
21
|
+
cache: npm
|
|
22
|
+
- run: npm ci
|
|
23
|
+
- run: npm run lint
|
|
24
|
+
- run: npm run format:check
|
|
25
|
+
- run: npm run check
|
|
26
|
+
|
|
27
|
+
build:
|
|
28
|
+
name: Build
|
|
29
|
+
needs: lint
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: actions/setup-node@v4
|
|
34
|
+
with:
|
|
35
|
+
node-version: "22"
|
|
36
|
+
cache: npm
|
|
37
|
+
- run: npm ci
|
|
38
|
+
- run: npm run build
|
|
39
|
+
- run: npm run build:plugin
|
|
40
|
+
- uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
|
|
45
|
+
release:
|
|
46
|
+
name: Release
|
|
47
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
48
|
+
needs: build
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
permissions:
|
|
51
|
+
contents: write
|
|
52
|
+
issues: write
|
|
53
|
+
pull-requests: write
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
with:
|
|
57
|
+
fetch-depth: 0
|
|
58
|
+
persist-credentials: false
|
|
59
|
+
- uses: actions/setup-node@v4
|
|
60
|
+
with:
|
|
61
|
+
node-version: "22"
|
|
62
|
+
cache: npm
|
|
63
|
+
- run: npm ci
|
|
64
|
+
- run: npm run build
|
|
65
|
+
- name: Semantic Release
|
|
66
|
+
env:
|
|
67
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
68
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
69
|
+
run: npx semantic-release
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/.releaserc.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": ["main"],
|
|
3
|
+
"plugins": [
|
|
4
|
+
"@semantic-release/commit-analyzer",
|
|
5
|
+
"@semantic-release/release-notes-generator",
|
|
6
|
+
"@semantic-release/changelog",
|
|
7
|
+
"@semantic-release/npm",
|
|
8
|
+
[
|
|
9
|
+
"@semantic-release/exec",
|
|
10
|
+
{
|
|
11
|
+
"prepareCmd": "node scripts/sync-version.cjs && npm run build:plugin"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
[
|
|
15
|
+
"@semantic-release/github",
|
|
16
|
+
{
|
|
17
|
+
"assets": [
|
|
18
|
+
{ "path": "dist/plugin/main.js", "label": "plugin-main.js" },
|
|
19
|
+
{ "path": "dist/plugin/manifest.json", "label": "plugin-manifest.json" }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
]
|
|
24
|
+
}
|
package/DEVELOPER.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Developer Guide
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
src/
|
|
7
|
+
cli/index.ts CLI entry — reads config, starts stdio transport
|
|
8
|
+
plugin/
|
|
9
|
+
main.ts Obsidian plugin entry (extends Plugin)
|
|
10
|
+
settings.ts Settings tab with vault picker + copy URL
|
|
11
|
+
mcp/
|
|
12
|
+
protocol.ts JSON-RPC 2.0 types + Content-Length framing
|
|
13
|
+
transport.ts Transport interface
|
|
14
|
+
stdio-transport.ts Stdio transport (for CLI)
|
|
15
|
+
http-transport.ts HTTP/SSE transport (for plugin)
|
|
16
|
+
server.ts Transport-agnostic server — creates handlers + routes requests
|
|
17
|
+
handlers/
|
|
18
|
+
tools.ts Tool implementations (9 tools)
|
|
19
|
+
prompts.ts Prompt directory reader + template parser
|
|
20
|
+
utils/
|
|
21
|
+
fs-utils.ts File I/O, frontmatter parsing, heading/block patching
|
|
22
|
+
search.ts Recursive text search across .md files
|
|
23
|
+
vaults.ts VaultRegistry — config from env, file, Obsidian auto-discovery
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Key Design Decisions
|
|
27
|
+
|
|
28
|
+
**Zero dependencies.** The MCP protocol (JSON-RPC 2.0 over stdio with Content-Length framing) is implemented from scratch. No npm packages needed at runtime — only Node.js stdlib.
|
|
29
|
+
|
|
30
|
+
**Node.js everywhere.** All I/O uses `fs` and `fs/promises` — runs on any platform Node.js supports. Obsidian plugin runs inside Obsidian's Electron. CLI runs standalone.
|
|
31
|
+
|
|
32
|
+
**Two transports, shared logic.** The `server.ts` exports a `createServer()` factory that returns tool definitions and a `handleRequest()` function. The stdio and HTTP/SSE transports both use the same factory — only the I/O layer differs.
|
|
33
|
+
|
|
34
|
+
**Direct filesystem access.** Unlike `obsidian-mcp-tools` which communicates via HTTP with the Local REST API plugin, this server reads/writes files directly.
|
|
35
|
+
|
|
36
|
+
**Multi-vault first.** The `VaultRegistry` supports env var, config file, and Obsidian auto-discovery.
|
|
37
|
+
|
|
38
|
+
## Development Workflow
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Install dependencies
|
|
42
|
+
npm install
|
|
43
|
+
|
|
44
|
+
# Run CLI in dev mode (watch + hot reload)
|
|
45
|
+
npm run dev
|
|
46
|
+
|
|
47
|
+
# Type-check
|
|
48
|
+
npm run check
|
|
49
|
+
|
|
50
|
+
# Lint + format
|
|
51
|
+
npm run lint
|
|
52
|
+
npm run format
|
|
53
|
+
|
|
54
|
+
# Build TS to dist/
|
|
55
|
+
npm run build
|
|
56
|
+
|
|
57
|
+
# Build plugin bundle for Obsidian
|
|
58
|
+
npm run build:plugin
|
|
59
|
+
|
|
60
|
+
# Run CLI from compiled JS
|
|
61
|
+
npm run start
|
|
62
|
+
|
|
63
|
+
# Test via env var
|
|
64
|
+
OBSIDIAN_VAULT_PATHS=/path/to/vault node dist/cli/index.js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Testing the CLI
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
OBSIDIAN_VAULT_PATHS=/path/to/vault node --input-type=module -e '
|
|
71
|
+
const msgs = [
|
|
72
|
+
{jsonrpc:"2.0",id:1,method:"initialize",params:{}},
|
|
73
|
+
{jsonrpc:"2.0",id:2,method:"tools/call",params:{name:"list_vaults",arguments:{}}},
|
|
74
|
+
{jsonrpc:"2.0",id:3,method:"tools/call",params:{name:"list_files",arguments:{vault:"default"}}},
|
|
75
|
+
];
|
|
76
|
+
let input = "";
|
|
77
|
+
for(const m of msgs){ const s = JSON.stringify(m); input += "Content-Length: "+s.length+"\r\n\r\n"+s; }
|
|
78
|
+
const {spawn} = await import("child_process");
|
|
79
|
+
const child = spawn("node", ["dist/cli/index.js"], {stdio:["pipe","pipe","pipe"]});
|
|
80
|
+
let buf = "";
|
|
81
|
+
child.stdout.on("data", c => buf += c.toString());
|
|
82
|
+
child.on("close", () => {
|
|
83
|
+
let remaining = buf;
|
|
84
|
+
while(true){
|
|
85
|
+
const m = remaining.match(/^Content-Length: (\d+)\r\n\r\n/);
|
|
86
|
+
if(!m) break;
|
|
87
|
+
const len = parseInt(m[1]), start = m[0].length;
|
|
88
|
+
if(remaining.length < start + len) break;
|
|
89
|
+
console.log(JSON.parse(remaining.slice(start, start + len)));
|
|
90
|
+
remaining = remaining.slice(start + len);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
child.stdin.write(input);
|
|
94
|
+
child.stdin.end();
|
|
95
|
+
setTimeout(() => process.exit(0), 2000);
|
|
96
|
+
'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Testing the Plugin
|
|
100
|
+
|
|
101
|
+
1. Build the plugin: `npm run build:plugin`
|
|
102
|
+
2. Copy `dist/plugin/` to `<your-vault>/.obsidian/plugins/obsidian-native-mcp/`
|
|
103
|
+
3. Reload Obsidian, enable the plugin in Community Plugins
|
|
104
|
+
4. Open plugin settings to see discovered vaults
|
|
105
|
+
|
|
106
|
+
## Adding a New Tool
|
|
107
|
+
|
|
108
|
+
1. Define the tool schema in `src/mcp/server.ts` — add to the `toolDefinitions` array
|
|
109
|
+
2. Implement the handler in `src/handlers/tools.ts` — add to `getHandlers()`
|
|
110
|
+
3. Implement the core logic in `src/utils/fs-utils.ts`, `search.ts`, or a new util
|
|
111
|
+
|
|
112
|
+
## Release Process
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 1. Update version in package.json and manifest.json
|
|
116
|
+
# 2. Build
|
|
117
|
+
npm run build
|
|
118
|
+
npm run build:plugin
|
|
119
|
+
|
|
120
|
+
# 3. Publish to npm
|
|
121
|
+
npm publish
|
|
122
|
+
|
|
123
|
+
# 4. Create GitHub release
|
|
124
|
+
gh release create v0.2.0 --title "v0.2.0" --generate-notes
|
|
125
|
+
|
|
126
|
+
# 5. Submit plugin to Obsidian community plugin list
|
|
127
|
+
# https://github.com/obsidianmd/obsidian-releases
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Protocol Reference
|
|
131
|
+
|
|
132
|
+
The server speaks the standard MCP protocol over stdio (CLI) or HTTP/SSE (plugin):
|
|
133
|
+
|
|
134
|
+
### Stdio
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Client → Server: Content-Length: <N>\r\n\r\n<JSON-RPC body>
|
|
138
|
+
Server → Client: Content-Length: <N>\r\n\r\n<JSON-RPC body>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### HTTP/SSE
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
Client → Server: GET /sse → SSE stream with endpoint event
|
|
145
|
+
Client → Server: POST /message?session_id=<id> → JSON-RPC request
|
|
146
|
+
Server → Client: SSE event "message" with JSON-RPC response
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Supported Methods
|
|
150
|
+
|
|
151
|
+
| Method | Purpose |
|
|
152
|
+
| --------------------------- | ------------------------- |
|
|
153
|
+
| `initialize` | Protocol handshake |
|
|
154
|
+
| `tools/list` | List available tools |
|
|
155
|
+
| `tools/call` | Execute a tool |
|
|
156
|
+
| `prompts/list` | List available prompts |
|
|
157
|
+
| `prompts/get` | Get prompt content |
|
|
158
|
+
| `notifications/initialized` | Client-ready notification |
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Utkarsh Srivastava
|
|
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,179 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Obsidian Native MCP
|
|
4
|
+
|
|
5
|
+
**Zero-dependency MCP server for Obsidian vaults**
|
|
6
|
+
Direct filesystem access — no Obsidian process, no REST API plugin required.
|
|
7
|
+
|
|
8
|
+
[](https://github.com/usrivastava92/obsidian-native-mcp/actions)
|
|
9
|
+
[](https://github.com/usrivastava92/obsidian-native-mcp/releases)
|
|
10
|
+
[](https://www.npmjs.com/package/obsidian-native-mcp)
|
|
11
|
+
[](https://www.npmjs.com/package/obsidian-native-mcp)
|
|
12
|
+
[](LICENSE)
|
|
13
|
+
[](CONTRIBUTING.md)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
Obsidian Native MCP is a [Model Context Protocol](https://modelcontextprotocol.io) server that gives AI assistants (Claude Desktop, etc.) direct, safe access to your Obsidian vaults.
|
|
18
|
+
|
|
19
|
+
**Two ways to use it:**
|
|
20
|
+
|
|
21
|
+
- **Obsidian plugin** (recommended) — 1-click install, auto-discovers vaults, settings UI, runs inside Obsidian
|
|
22
|
+
- **CLI** — standalone, works without Obsidian running, configured via env var or config file
|
|
23
|
+
|
|
24
|
+
**Why Obsidian Native MCP over other solutions?**
|
|
25
|
+
|
|
26
|
+
| Feature | Obsidian Native MCP | obsidian-mcp-tools (archived) |
|
|
27
|
+
| ------------------ | -------------------------------------- | ----------------------------------------- |
|
|
28
|
+
| Obsidian required? | **Plugin: no. CLI: no.** | Yes — must be running with Local REST API |
|
|
29
|
+
| Dependencies | **Zero** — uses only Node.js stdlib | MCP SDK, arktype, zod, radash, turndown… |
|
|
30
|
+
| Distribution | Obsidian plugin + npm CLI | Bun-compiled binary |
|
|
31
|
+
| Multi-vault | **Built-in** — one server, many vaults | No |
|
|
32
|
+
| Cross-platform | **1 codebase, runs everywhere (WORA)** | Platform-specific binaries |
|
|
33
|
+
| File patching | Headings, blocks, frontmatter | Via REST API |
|
|
34
|
+
| Setup effort | Plugin: 1 click. CLI: one command. | Manual download + config |
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
### Obsidian plugin (recommended)
|
|
39
|
+
|
|
40
|
+
1. Open Obsidian → Settings → Community Plugins → Browse
|
|
41
|
+
2. Search for "Obsidian Native MCP" and install
|
|
42
|
+
3. Enable the plugin in Community Plugins list
|
|
43
|
+
4. Go to plugin settings → toggle which vaults to expose → copy the MCP URL
|
|
44
|
+
|
|
45
|
+
### CLI (standalone)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g obsidian-native-mcp
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Build from source
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone https://github.com/usrivastava92/obsidian-native-mcp.git
|
|
55
|
+
cd obsidian-native-mcp
|
|
56
|
+
npm install
|
|
57
|
+
npm run build
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
### Plugin
|
|
63
|
+
|
|
64
|
+
Plugin auto-discovers all your Obsidian vaults from Obsidian's own config. Open plugin settings to select which vaults to expose.
|
|
65
|
+
|
|
66
|
+
### CLI
|
|
67
|
+
|
|
68
|
+
Configure vault(s) via environment variable or config file.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Single vault
|
|
72
|
+
export OBSIDIAN_VAULT_PATHS=/Users/me/my-obsidian-vault
|
|
73
|
+
|
|
74
|
+
# Multiple vaults (semicolons on all platforms)
|
|
75
|
+
export OBSIDIAN_VAULT_PATHS=/Users/me/personal;/Users/me/work
|
|
76
|
+
|
|
77
|
+
# Windows
|
|
78
|
+
set OBSIDIAN_VAULT_PATHS=C:\Users\me\personal;C:\Users\me\work
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Config file at `~/.config/obsidian-native-mcp/vaults.json`:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"vaults": {
|
|
86
|
+
"personal": "/Users/me/personal-notes",
|
|
87
|
+
"work": "/Users/me/work-vault"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Usage
|
|
93
|
+
|
|
94
|
+
### Obsidian plugin
|
|
95
|
+
|
|
96
|
+
After enabling the plugin, open its settings tab. You'll see a URL like `http://127.0.0.1:9789/sse`. Add it to your `claude_desktop_config.json`:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"obsidian-native-mcp": {
|
|
102
|
+
"url": "http://127.0.0.1:9789/sse"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### CLI
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"obsidian-native-mcp": {
|
|
114
|
+
"command": "obsidian-native-mcp",
|
|
115
|
+
"env": {
|
|
116
|
+
"OBSIDIAN_VAULT_PATHS": "/Users/me/my-obsidian-vault"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Tools
|
|
124
|
+
|
|
125
|
+
| Tool | Description |
|
|
126
|
+
| ---------------- | ------------------------------------------------------- |
|
|
127
|
+
| `list_vaults` | List all configured vaults with paths |
|
|
128
|
+
| `get_vault_info` | Stats per vault (file count, etc.) |
|
|
129
|
+
| `list_files` | List files/dirs in a vault directory |
|
|
130
|
+
| `get_file` | Read file content (markdown or json with frontmatter) |
|
|
131
|
+
| `create_file` | Create or overwrite a file |
|
|
132
|
+
| `append_to_file` | Append content to a file |
|
|
133
|
+
| `patch_file` | Patch by heading, block reference, or frontmatter field |
|
|
134
|
+
| `delete_file` | Delete a file |
|
|
135
|
+
| `search` | Full-text search across markdown files |
|
|
136
|
+
|
|
137
|
+
All file tools accept an optional `vault` parameter. When only one vault is configured, it's inferred automatically.
|
|
138
|
+
|
|
139
|
+
### Prompts
|
|
140
|
+
|
|
141
|
+
Place markdown files in a `Prompts/` folder in any vault, tagged with `mcp-tools-prompt` in frontmatter:
|
|
142
|
+
|
|
143
|
+
```markdown
|
|
144
|
+
---
|
|
145
|
+
tags: [mcp-tools-prompt]
|
|
146
|
+
description: Summarize the daily note
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
Summarize what happened on <% tp.mcpTools.prompt("date", "Date to summarize") %>.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Prompts appear automatically in your MCP client's prompt selector.
|
|
153
|
+
|
|
154
|
+
## Roadmap
|
|
155
|
+
|
|
156
|
+
- [x] Zero-dependency MCP protocol implementation
|
|
157
|
+
- [x] Full vault CRUD (list, read, create, append, patch, delete)
|
|
158
|
+
- [x] Full-text search across vault
|
|
159
|
+
- [x] Multi-vault support
|
|
160
|
+
- [x] Cross-platform (runs anywhere Node.js runs)
|
|
161
|
+
- [x] Prompt templates from vault's Prompts folder
|
|
162
|
+
- [x] Config file support (`~/.config/obsidian-native-mcp/vaults.json`)
|
|
163
|
+
- [x] Obsidian community plugin with settings UI
|
|
164
|
+
- [x] Vault auto-discovery from Obsidian config
|
|
165
|
+
- [x] HTTP/SSE transport for plugin
|
|
166
|
+
- [x] Two distribution methods (plugin + CLI)
|
|
167
|
+
- [ ] Smart Connections-like semantic search (local embeddings)
|
|
168
|
+
- [ ] Vault change watching (file system events)
|
|
169
|
+
- [ ] npm publish workflow
|
|
170
|
+
|
|
171
|
+
## Security
|
|
172
|
+
|
|
173
|
+
obsidian-native-mcp runs locally on your machine. The plugin exposes vaults over localhost only. The CLI communicates over local stdio. No data is sent to external services. Only vaults you explicitly select/configure are accessible.
|
|
174
|
+
|
|
175
|
+
For security concerns, please open an issue or see [SECURITY.md](SECURITY.md).
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const server_1 = require("../mcp/server");
|
|
4
|
+
const stdio_transport_1 = require("../mcp/stdio-transport");
|
|
5
|
+
const vaults_1 = require("../utils/vaults");
|
|
6
|
+
async function main() {
|
|
7
|
+
const registry = new vaults_1.VaultRegistry();
|
|
8
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
9
|
+
console.log("0.2.0");
|
|
10
|
+
process.exit(0);
|
|
11
|
+
}
|
|
12
|
+
console.error("obsidian-native-mcp server starting");
|
|
13
|
+
const server = (0, server_1.createServer)(registry);
|
|
14
|
+
const transport = new stdio_transport_1.StdioTransport();
|
|
15
|
+
transport.onRequest(async (msg) => server.handleRequest(msg));
|
|
16
|
+
transport.start();
|
|
17
|
+
}
|
|
18
|
+
main().catch((err) => {
|
|
19
|
+
console.error("Fatal error:", err);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;AAAA,0CAA6C;AAC7C,4DAAwD;AACxD,4CAAgD;AAEhD,KAAK,UAAU,IAAI;IACjB,MAAM,QAAQ,GAAG,IAAI,sBAAa,EAAE,CAAC;IAErC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAErD,MAAM,MAAM,GAAG,IAAA,qBAAY,EAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,gCAAc,EAAE,CAAC;IAEvC,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9D,SAAS,CAAC,KAAK,EAAE,CAAC;AACpB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptHandler = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const promises_1 = require("fs/promises");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
function parseFrontmatter(content) {
|
|
8
|
+
if (!content.startsWith("---"))
|
|
9
|
+
return null;
|
|
10
|
+
const endIndex = content.indexOf("---", 3);
|
|
11
|
+
if (endIndex === -1)
|
|
12
|
+
return null;
|
|
13
|
+
const raw = content.slice(3, endIndex).trim();
|
|
14
|
+
const body = content.slice(endIndex + 3).trim();
|
|
15
|
+
const frontmatter = {};
|
|
16
|
+
for (const line of raw.split("\n")) {
|
|
17
|
+
const colonIndex = line.indexOf(":");
|
|
18
|
+
if (colonIndex === -1)
|
|
19
|
+
continue;
|
|
20
|
+
const key = line.slice(0, colonIndex).trim();
|
|
21
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
22
|
+
if (value.startsWith('"') && value.endsWith('"'))
|
|
23
|
+
value = value.slice(1, -1);
|
|
24
|
+
else if (value.startsWith("[") && value.endsWith("]")) {
|
|
25
|
+
value = value
|
|
26
|
+
.slice(1, -1)
|
|
27
|
+
.split(",")
|
|
28
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ""))
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
frontmatter[key] = value;
|
|
32
|
+
}
|
|
33
|
+
return { frontmatter, body };
|
|
34
|
+
}
|
|
35
|
+
class PromptHandler {
|
|
36
|
+
registry;
|
|
37
|
+
promptDir = "Prompts";
|
|
38
|
+
constructor(registry) {
|
|
39
|
+
this.registry = registry;
|
|
40
|
+
}
|
|
41
|
+
async list(vaultName) {
|
|
42
|
+
const vaultPath = this.registry.resolve(vaultName);
|
|
43
|
+
const promptPath = (0, path_1.join)(vaultPath, this.promptDir);
|
|
44
|
+
let entries;
|
|
45
|
+
try {
|
|
46
|
+
entries = (0, fs_1.readdirSync)(promptPath);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const prompts = [];
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
if (!entry.endsWith(".md"))
|
|
54
|
+
continue;
|
|
55
|
+
const fullPath = (0, path_1.join)(promptPath, entry);
|
|
56
|
+
const stat = (0, fs_1.statSync)(fullPath);
|
|
57
|
+
if (!stat.isFile())
|
|
58
|
+
continue;
|
|
59
|
+
try {
|
|
60
|
+
const content = await (0, promises_1.readFile)(fullPath, "utf-8");
|
|
61
|
+
const parsed = parseFrontmatter(content);
|
|
62
|
+
const tags = parsed?.frontmatter?.tags || [];
|
|
63
|
+
if (!tags.includes("mcp-tools-prompt"))
|
|
64
|
+
continue;
|
|
65
|
+
const promptArgs = parsePromptParameters(parsed?.body || content);
|
|
66
|
+
prompts.push({
|
|
67
|
+
name: entry,
|
|
68
|
+
description: parsed?.frontmatter?.description || entry.replace(".md", ""),
|
|
69
|
+
arguments: promptArgs,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return prompts;
|
|
77
|
+
}
|
|
78
|
+
async get(name, vaultName) {
|
|
79
|
+
const vaultPath = this.registry.resolve(vaultName);
|
|
80
|
+
const promptPath = (0, path_1.join)(vaultPath, this.promptDir, name);
|
|
81
|
+
let content;
|
|
82
|
+
try {
|
|
83
|
+
content = await (0, promises_1.readFile)(promptPath, "utf-8");
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
throw new Error(`Prompt not found: ${name}`);
|
|
87
|
+
}
|
|
88
|
+
const parsed = parseFrontmatter(content);
|
|
89
|
+
const body = parsed?.body || content;
|
|
90
|
+
const withoutFrontmatter = body.trim();
|
|
91
|
+
return {
|
|
92
|
+
messages: [
|
|
93
|
+
{
|
|
94
|
+
role: "user",
|
|
95
|
+
content: {
|
|
96
|
+
type: "text",
|
|
97
|
+
text: withoutFrontmatter,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.PromptHandler = PromptHandler;
|
|
105
|
+
function parsePromptParameters(content) {
|
|
106
|
+
const regex = /<%[-_*]*\s*tp\.mcpTools\.prompt\(([^)]+)\)\s*[-_]*%>/g;
|
|
107
|
+
const params = [];
|
|
108
|
+
let match;
|
|
109
|
+
while ((match = regex.exec(content)) !== null) {
|
|
110
|
+
try {
|
|
111
|
+
const argsStr = match[1];
|
|
112
|
+
const args = argsStr.split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
113
|
+
if (args.length >= 1) {
|
|
114
|
+
params.push({
|
|
115
|
+
name: args[0],
|
|
116
|
+
description: args[1],
|
|
117
|
+
required: true,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return params;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/handlers/prompts.ts"],"names":[],"mappings":"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"}
|