obsidian-mcp-server 3.6.1 → 3.7.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/AGENTS.md +40 -22
- package/CLAUDE.md +40 -22
- package/Dockerfile +3 -3
- package/README.md +29 -17
- package/changelog/3.7.x/3.7.0.md +34 -0
- package/changelog/3.7.x/3.7.1.md +40 -0
- package/dist/config/server-config.d.ts +1 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +5 -1
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +35 -35
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +20 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +40 -0
- package/dist/instructions.js.map +1 -0
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +7 -5
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +12 -8
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +69 -9
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +88 -7
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +256 -309
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/index.js +9 -3
- package/dist/mcp-server/tools/definitions/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +6 -0
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +12 -4
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +107 -10
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +237 -162
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +1 -0
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +70 -24
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +6 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +8 -10
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +7 -5
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +13 -7
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +6 -4
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +4 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +4 -1
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +15 -4
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +4 -2
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +26 -2
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +47 -15
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +5 -0
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +21 -4
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -1
- package/dist/services/obsidian/frontmatter-ops.d.ts +15 -1
- package/dist/services/obsidian/frontmatter-ops.d.ts.map +1 -1
- package/dist/services/obsidian/frontmatter-ops.js +55 -10
- package/dist/services/obsidian/frontmatter-ops.js.map +1 -1
- package/dist/services/obsidian/jsonlogic-evaluator.d.ts +83 -0
- package/dist/services/obsidian/jsonlogic-evaluator.d.ts.map +1 -0
- package/dist/services/obsidian/jsonlogic-evaluator.js +192 -0
- package/dist/services/obsidian/jsonlogic-evaluator.js.map +1 -0
- package/dist/services/obsidian/jsonlogic-worker.d.ts +53 -0
- package/dist/services/obsidian/jsonlogic-worker.d.ts.map +1 -0
- package/dist/services/obsidian/jsonlogic-worker.js +237 -0
- package/dist/services/obsidian/jsonlogic-worker.js.map +1 -0
- package/dist/services/obsidian/obsidian-service.d.ts +61 -6
- package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
- package/dist/services/obsidian/obsidian-service.js +301 -53
- package/dist/services/obsidian/obsidian-service.js.map +1 -1
- package/dist/services/obsidian/patch-instruction.d.ts +20 -1
- package/dist/services/obsidian/patch-instruction.d.ts.map +1 -1
- package/dist/services/obsidian/patch-instruction.js +30 -0
- package/dist/services/obsidian/patch-instruction.js.map +1 -1
- package/dist/services/obsidian/path-policy.d.ts +55 -5
- package/dist/services/obsidian/path-policy.d.ts.map +1 -1
- package/dist/services/obsidian/path-policy.js +143 -19
- package/dist/services/obsidian/path-policy.js.map +1 -1
- package/dist/services/obsidian/section-extractor.d.ts +24 -0
- package/dist/services/obsidian/section-extractor.d.ts.map +1 -1
- package/dist/services/obsidian/section-extractor.js +37 -0
- package/dist/services/obsidian/section-extractor.js.map +1 -1
- package/dist/services/obsidian/types.d.ts +12 -3
- package/dist/services/obsidian/types.d.ts.map +1 -1
- package/manifest.json +10 -2
- package/package.json +18 -14
- package/server.json +19 -5
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "obsidian_delete_note now deletes on the first call unless OBSIDIAN_DELETE_ELICITATION=true, and obsidian_list_tags and obsidian_list_notes no longer expose tag names or folder and note names outside OBSIDIAN_READ_PATHS."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 3.7.0 — 2026-10-05
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`OBSIDIAN_DELETE_ELICITATION`** (default `false`) turns the `obsidian_delete_note` confirmation round on ([#153](https://github.com/cyanheads/obsidian-mcp-server/issues/153)). Declared in `server.json`, `manifest.json`, and `.env.example`; the startup log reports the delete mode.
|
|
12
|
+
- **`requestedPath` and a `notice` on `obsidian_get_note` and `obsidian_open_in_ui`** when a path with no exact match reads or opens its single case-insensitive match instead ([#149](https://github.com/cyanheads/obsidian-mcp-server/issues/149)). Write and delete tools still match the exact path.
|
|
13
|
+
|
|
14
|
+
## Changed
|
|
15
|
+
|
|
16
|
+
- **`obsidian_delete_note` now deletes on the first call, with no confirmation round** ([#153](https://github.com/cyanheads/obsidian-mcp-server/issues/153)). Set `OBSIDIAN_DELETE_ELICITATION=true` to keep the confirmation; the stateful-session requirement over HTTP then applies only in that mode, and not under `OBSIDIAN_READ_ONLY=true` (which disables the tool), so `MCP_SESSION_MODE=stateless` starts otherwise. `OBSIDIAN_WRITE_PATHS` and `OBSIDIAN_READ_ONLY` bound what a delete reaches.
|
|
17
|
+
- **`obsidian_delete_note` checks write scope before it reads the note or builds a prompt**, so a readable note outside `OBSIDIAN_WRITE_PATHS` fails `path_forbidden` without asking ([#153](https://github.com/cyanheads/obsidian-mcp-server/issues/153)).
|
|
18
|
+
- **`obsidian_search_notes` backlink recipe** also matches `[[Target Note\|Alias]]`, the table-safe alias form.
|
|
19
|
+
|
|
20
|
+
## Fixed
|
|
21
|
+
|
|
22
|
+
- **`obsidian_get_note` `includeLinks` returns the target Obsidian resolves for `[[Note\|Alias]]`**, with no trailing backslash ([#126](https://github.com/cyanheads/obsidian-mcp-server/issues/126)). Wikilink and markdown-link scanning now follows Obsidian's own shapes (`[[Note|see [1]]]`, `Note(1).md`) and stays linear on unclosed brackets.
|
|
23
|
+
- **Heading writes on Local REST API v4.x keep their line breaks** ([#146](https://github.com/cyanheads/obsidian-mcp-server/issues/146)). An append, prepend, or replace no longer runs into the next heading or the line before. CRLF notes still land mid-line, a plugin limitation.
|
|
24
|
+
- **`obsidian_delete_note` no longer deletes a differently-cased note on a case-insensitive filesystem.** A wrong-case path fails `note_missing`, listing near matches in `suggestions`, and sends no DELETE.
|
|
25
|
+
|
|
26
|
+
## Security
|
|
27
|
+
|
|
28
|
+
- **`obsidian_list_tags` and `obsidian://tags` list only tags from readable notes when `OBSIDIAN_READ_PATHS` is set** ([#154](https://github.com/cyanheads/obsidian-mcp-server/issues/154)). Before, tag names and vault-wide counts from out-of-scope notes were returned. `count` is now the number of readable notes carrying the tag or a nested tag; write paths and read-only alone leave listings vault-wide.
|
|
29
|
+
- **`obsidian_list_notes` returns only readable entries and the folders leading to the read scope** ([#155](https://github.com/cyanheads/obsidian-mcp-server/issues/155)). Before, out-of-scope folder and root-level note names were listed. A nested scope can now be browsed to from the root, and `truncated` marks only depth-limited folders.
|
|
30
|
+
|
|
31
|
+
## Dependencies
|
|
32
|
+
|
|
33
|
+
- dev: `ignore` ^7.0.11 → ^7.0.12
|
|
34
|
+
- `marked` stays pinned at `17.0.6` (the Local REST API's markdown-patch engine depends on marked 17; marked 18 parses some blocks differently, which would split section targeting from the plugin's)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Read-scope fixes: under OBSIDIAN_READ_PATHS, JSONLogic search runs in the server over readable notes only, active and periodic denials name the target rather than the resolved note, read-then-write tools write to the note they read, and instructions state the effective scope. A case-variant write fails as case_variant_exists."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 3.7.1 — 2026-10-24
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **The `.mcpb` bundle carries only runtime files and package metadata** ([#166](https://github.com/cyanheads/obsidian-mcp-server/issues/166)). `src/`, `tests/`, `scripts/`, `docs/`, CI and editor config, and agent files are left out.
|
|
12
|
+
|
|
13
|
+
## Fixed
|
|
14
|
+
|
|
15
|
+
- **A whole-file `obsidian_write_note` or `obsidian_append_to_note` to a path that differs from an existing note only in letter case fails `case_variant_exists`** ([#159](https://github.com/cyanheads/obsidian-mcp-server/issues/159)). Conflict, with `existingPath` when the folder listing shows exactly one case match; it replaces a retried `ServiceUnavailable`. Nothing is written.
|
|
16
|
+
- **`obsidian_manage_tags` reads a wikilink's extent as Obsidian does** ([#160](https://github.com/cyanheads/obsidian-mcp-server/issues/160)). A `#word` inside `[[Note|a #b [1]]]` is link text, not an inline tag.
|
|
17
|
+
- **The server `instructions` state the effective scope** ([#158](https://github.com/cyanheads/obsidian-mcp-server/issues/158)): readable is the read paths plus the write paths, and writable is the write paths, or the read scope when only reads are restricted. Under `OBSIDIAN_READ_ONLY=true` with `OBSIDIAN_READ_PATHS`, the read scope is stated.
|
|
18
|
+
|
|
19
|
+
## Security
|
|
20
|
+
|
|
21
|
+
- **Under `OBSIDIAN_READ_PATHS`, `obsidian_search_notes` in `jsonlogic` mode evaluates the tree in the server over readable notes only** ([#156](https://github.com/cyanheads/obsidian-mcp-server/issues/156)). No hit, result, or error reads a note outside the scope, and `links`, `backlinks`, and `unresolvedLinks` hold readable entries. Which note a link resolves to is still Obsidian's vault-wide resolution ([#165](https://github.com/cyanheads/obsidian-mcp-server/issues/165)). Evaluation is bounded: a tree past a bound fails `logic_too_costly`, and a readable note whose data is nested too deep to hand to the evaluator fails `note_too_deep`. Without a read scope the tree goes to the plugin unchanged.
|
|
22
|
+
- **A `path_forbidden` denial on an `active` or `periodic` target names the target** ([#157](https://github.com/cyanheads/obsidian-mcp-server/issues/157)), such as `(active file)` or `daily note for 2026-10-01`, not the note it resolved to.
|
|
23
|
+
- **`obsidian_replace_in_note`, `obsidian_manage_frontmatter` (`delete`), and `obsidian_manage_tags` (`add`, `remove`) write to the path their read resolved** ([#162](https://github.com/cyanheads/obsidian-mcp-server/issues/162)), so a change of active note or period between the read and the write cannot redirect the write to another note. `obsidian_manage_tags` reads back that same path, and `obsidian_get_note` `format: "document-map"` on an `active` or `periodic` target resolves it once, so the map and the returned `path` describe the same note.
|
|
24
|
+
|
|
25
|
+
## Dependencies
|
|
26
|
+
|
|
27
|
+
- `@cyanheads/mcp-ts-core` ^0.13.12 → ^0.13.15
|
|
28
|
+
- `js-yaml` ^5.4.2 → ^5.5.0
|
|
29
|
+
- `pino-pretty` ^13.1.3 → ^13.2.0
|
|
30
|
+
- `undici` ^8.11.2 → ^8.12.3
|
|
31
|
+
- `zod` ^4.6.5 → ^4.6.7
|
|
32
|
+
- `json-logic-js` 2.0.5 (new; the plugin's own JSONLogic engine, evaluated in-server under a read scope)
|
|
33
|
+
- `glob-to-regexp` 0.4.1 (new; the plugin's `glob` operator)
|
|
34
|
+
- dev: `@biomejs/biome` ^2.5.15 → ^2.6.2
|
|
35
|
+
- dev: `@types/node` ^26.6.4 → ^26.6.5
|
|
36
|
+
- dev: `ignore` ^7.0.12 → ^7.0.13
|
|
37
|
+
- dev: `tsc-alias` ^1.9.7 → ^1.9.8
|
|
38
|
+
- dev: `vitest` ^5.0.3 → ^5.0.5
|
|
39
|
+
- dev: `@vitest/coverage-istanbul` ^5.0.5 (new, `test:coverage` script)
|
|
40
|
+
- toolchain: Bun 1.4.2 → 1.4.4 (`packageManager`, Dockerfile, README badge)
|
|
@@ -13,6 +13,7 @@ declare const ServerConfigSchema: z.ZodObject<{
|
|
|
13
13
|
readPaths: z.ZodPreprocess<z.ZodPipe<z.ZodOptional<z.ZodArray<z.ZodString>>, z.ZodTransform<string[] | undefined, string[] | undefined>>, unknown>;
|
|
14
14
|
writePaths: z.ZodPreprocess<z.ZodPipe<z.ZodOptional<z.ZodArray<z.ZodString>>, z.ZodTransform<string[] | undefined, string[] | undefined>>, unknown>;
|
|
15
15
|
readOnly: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
|
|
16
|
+
deleteElicitation: z.ZodDefault<z.ZodUnion<readonly [z.ZodBoolean, z.ZodCodec<z.ZodString, z.ZodBoolean>]>>;
|
|
16
17
|
omnisearchUrl: z.ZodOptional<z.ZodString>;
|
|
17
18
|
}, z.core.$strip>;
|
|
18
19
|
export type ServerConfig = z.infer<typeof ServerConfigSchema>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAgF3C,QAAA,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAgF3C,QAAA,MAAM,kBAAkB;;;;;;;;;;;iBA4DtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAc9C;AAED,iEAAiE;AACjE,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -112,10 +112,13 @@ const ServerConfigSchema = z.object({
|
|
|
112
112
|
.default(false)
|
|
113
113
|
.describe('Opt-in flag for the command-palette pair (`obsidian_list_commands` + `obsidian_execute_command`). Off by default — Obsidian commands are opaque and can be destructive.'),
|
|
114
114
|
readPaths: envPathList.describe('Optional vault-relative folder allowlist for read operations. Comma-separated; prefix-based with implicit recursion; case-insensitive; trailing slashes normalized. Unset = full vault.'),
|
|
115
|
-
writePaths: envPathList.describe('Optional vault-relative folder allowlist for write operations. Same syntax as OBSIDIAN_READ_PATHS. Write paths are implicitly readable. Unset = full vault.'),
|
|
115
|
+
writePaths: envPathList.describe('Optional vault-relative folder allowlist for write operations. Same syntax as OBSIDIAN_READ_PATHS. Write paths are implicitly readable. Unset = full vault, or the read scope when reads are restricted.'),
|
|
116
116
|
readOnly: envBoolean
|
|
117
117
|
.default(false)
|
|
118
118
|
.describe('Global kill switch. When true, denies every write regardless of OBSIDIAN_WRITE_PATHS, and suppresses the OBSIDIAN_ENABLE_COMMANDS pair (commands can mutate). Defaults to false.'),
|
|
119
|
+
deleteElicitation: envBoolean
|
|
120
|
+
.default(false)
|
|
121
|
+
.describe('When true, `obsidian_delete_note` asks the user to confirm each delete through an elicitation round, and HTTP startup requires a stateful session (MCP_SESSION_MODE=stateless is refused) unless OBSIDIAN_READ_ONLY=true, which disables the tool. Defaults to false: the delete runs on the first call, bounded by OBSIDIAN_WRITE_PATHS and OBSIDIAN_READ_ONLY.'),
|
|
119
122
|
omnisearchUrl: z
|
|
120
123
|
.string()
|
|
121
124
|
.url()
|
|
@@ -133,6 +136,7 @@ export function getServerConfig() {
|
|
|
133
136
|
readPaths: 'OBSIDIAN_READ_PATHS',
|
|
134
137
|
writePaths: 'OBSIDIAN_WRITE_PATHS',
|
|
135
138
|
readOnly: 'OBSIDIAN_READ_ONLY',
|
|
139
|
+
deleteElicitation: 'OBSIDIAN_DELETE_ELICITATION',
|
|
136
140
|
omnisearchUrl: 'OBSIDIAN_OMNISEARCH_URL',
|
|
137
141
|
});
|
|
138
142
|
return _config;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
import { createApp, disabledTool } from '@cyanheads/mcp-ts-core';
|
|
11
11
|
import { requestContextService } from '@cyanheads/mcp-ts-core/utils';
|
|
12
12
|
import { getServerConfig } from './config/server-config.js';
|
|
13
|
+
import { buildInstructions } from './instructions.js';
|
|
13
14
|
import { allPromptDefinitions } from './mcp-server/prompts/definitions/index.js';
|
|
14
15
|
import { allResourceDefinitions } from './mcp-server/resources/definitions/index.js';
|
|
15
|
-
import { buildSearchNotesTool, commandToolDefinitions, readToolDefinitions, writeToolDefinitions, } from './mcp-server/tools/definitions/index.js';
|
|
16
|
+
import { buildDeleteNoteTool, buildSearchNotesTool, commandToolDefinitions, readToolDefinitions, writeToolDefinitions, } from './mcp-server/tools/definitions/index.js';
|
|
16
17
|
import { getObsidianService, initObsidianService } from './services/obsidian/obsidian-service.js';
|
|
17
18
|
import { PathPolicy } from './services/obsidian/path-policy.js';
|
|
18
19
|
const config = getServerConfig();
|
|
@@ -27,38 +28,22 @@ initObsidianService(config);
|
|
|
27
28
|
const obsidian = getObsidianService();
|
|
28
29
|
const omnisearchReachable = await obsidian.probeOmnisearch();
|
|
29
30
|
const searchNotesTool = buildSearchNotesTool({ omnisearchReachable });
|
|
31
|
+
const writeToolSet = [
|
|
32
|
+
...writeToolDefinitions,
|
|
33
|
+
buildDeleteNoteTool({ elicitation: config.deleteElicitation }),
|
|
34
|
+
];
|
|
30
35
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* toggle, Omnisearch availability) when those flags are active.
|
|
36
|
+
* The delete confirmation can run only when `obsidian_delete_note` is
|
|
37
|
+
* registered: `OBSIDIAN_READ_ONLY=true` disables it below, so the gate — and
|
|
38
|
+
* the session it needs — exists only with elicitation on and writes allowed.
|
|
35
39
|
*/
|
|
36
|
-
|
|
37
|
-
const sections = [
|
|
38
|
-
'Use the `obsidian_*` tools to access the Obsidian vault via the Local REST API plugin: search, read, write, and patch notes, including targeted edits to headings, blocks, and YAML frontmatter. Notes are addressed by vault-relative path including the file extension (e.g. `Folder/Note.md`); tags support hierarchical `parent/child` notation, and counts roll up to parents.',
|
|
39
|
-
];
|
|
40
|
-
if (config.readOnly) {
|
|
41
|
-
sections.push('Read-only mode is active (OBSIDIAN_READ_ONLY=true): every write tool rejects every path with `path_forbidden` / `read_only_mode`. `obsidian_open_in_ui` still opens notes that exist, but rejects an open against a missing path — Obsidian would create the file.');
|
|
42
|
-
}
|
|
43
|
-
else if (!policy.isUnrestricted) {
|
|
44
|
-
const { readPaths, writePaths } = policy.describe();
|
|
45
|
-
const render = (scope) => typeof scope === 'string' ? scope : scope.map((p) => `'${p}'`).join(', ');
|
|
46
|
-
sections.push(`Vault path policy is enforced. Readable: ${render(readPaths)}. Writable: ${render(writePaths)}. Paths outside scope reject with \`path_forbidden\` — error data carries the active scope so you can self-correct.`);
|
|
47
|
-
}
|
|
48
|
-
if (config.enableCommands && !config.readOnly) {
|
|
49
|
-
sections.push('Command-palette tools (`obsidian_list_commands`, `obsidian_execute_command`) are enabled and can fire any Obsidian command. Commands are opaque and may be destructive — prefer dedicated tools when one fits.');
|
|
50
|
-
}
|
|
51
|
-
if (omnisearchReachable) {
|
|
52
|
-
sections.push('`obsidian_search_notes` includes an `omnisearch` mode: BM25-ranked, typo-tolerant, with PDF/OCR coverage (via the Text Extractor plugin). Results cap at 50 upstream — narrow the query (quoted phrases, `-exclusion`, `path:` / `ext:` filters) to surface more.');
|
|
53
|
-
}
|
|
54
|
-
return sections.join('\n\n');
|
|
55
|
-
}
|
|
40
|
+
const deleteGateActive = config.deleteElicitation && !config.readOnly;
|
|
56
41
|
const writeTools = config.readOnly
|
|
57
|
-
?
|
|
42
|
+
? writeToolSet.map((def) => disabledTool(def, {
|
|
58
43
|
reason: 'Disabled by OBSIDIAN_READ_ONLY=true.',
|
|
59
44
|
hint: 'Unset OBSIDIAN_READ_ONLY (or set it to false) to enable write tools.',
|
|
60
45
|
}))
|
|
61
|
-
:
|
|
46
|
+
: writeToolSet;
|
|
62
47
|
const commandTools = config.enableCommands && !config.readOnly
|
|
63
48
|
? commandToolDefinitions
|
|
64
49
|
: commandToolDefinitions.map((def) => disabledTool(def, {
|
|
@@ -76,30 +61,45 @@ const { services } = await createApp({
|
|
|
76
61
|
tools,
|
|
77
62
|
resources: allResourceDefinitions,
|
|
78
63
|
prompts: allPromptDefinitions,
|
|
79
|
-
instructions: buildInstructions(
|
|
64
|
+
instructions: buildInstructions({
|
|
65
|
+
enableCommands: config.enableCommands,
|
|
66
|
+
omnisearchReachable,
|
|
67
|
+
policy,
|
|
68
|
+
readOnly: config.readOnly,
|
|
69
|
+
}),
|
|
80
70
|
/**
|
|
71
|
+
* With `OBSIDIAN_DELETE_ELICITATION=true` (and writes allowed),
|
|
81
72
|
* `obsidian_delete_note` confirms through `ctx.requestInput`, which a
|
|
82
73
|
* 2025-era HTTP client can only answer on a stateful session — under
|
|
83
74
|
* stateless HTTP the tool would be unusable for them. Required, not just
|
|
84
|
-
* defaulted: over HTTP, a resolved `stateless` mode (an
|
|
85
|
-
* `MCP_SESSION_MODE=stateless`) fails startup with a
|
|
86
|
-
*
|
|
75
|
+
* defaulted, in that mode: over HTTP, a resolved `stateless` mode (an
|
|
76
|
+
* explicit `MCP_SESSION_MODE=stateless`) fails startup with a
|
|
77
|
+
* `ConfigurationError`. With the confirmation off, or the tool disabled by
|
|
78
|
+
* `OBSIDIAN_READ_ONLY=true`, nothing needs a session and `stateful` is only
|
|
79
|
+
* the default. stdio is never refused; `MCP_SESSION_MODE` has no effect there.
|
|
87
80
|
*/
|
|
88
|
-
sessionMode:
|
|
81
|
+
sessionMode: deleteGateActive
|
|
82
|
+
? { default: 'stateful', require: 'stateful' }
|
|
83
|
+
: { default: 'stateful' },
|
|
89
84
|
teardown: () => obsidian.close(),
|
|
90
85
|
});
|
|
91
86
|
/**
|
|
92
87
|
* Startup banner — emitted after createApp() returns so the framework's
|
|
93
88
|
* `logger.initialize()` has run; calls inside `setup()` happen pre-init and
|
|
94
|
-
* are dropped. Operators check this against their config to verify the
|
|
95
|
-
* path
|
|
96
|
-
*
|
|
89
|
+
* are dropped. Operators check this against their config to verify the
|
|
90
|
+
* configured path lists and whether `obsidian_delete_note` asks before deleting
|
|
91
|
+
* (`deleteElicitation`, false under read-only like `enableCommands`, since
|
|
92
|
+
* the tool is disabled there). The server `instructions` and the active scope
|
|
93
|
+
* on `path_forbidden` errors state the effective scope instead (write paths
|
|
94
|
+
* are readable; a write needs read access), so the LLM (or operator) can
|
|
95
|
+
* self-correct without scrolling the log.
|
|
97
96
|
*/
|
|
98
97
|
const bannerCtx = requestContextService.createRequestContext({
|
|
99
98
|
operation: 'startup',
|
|
100
99
|
additionalContext: {
|
|
101
100
|
...policy.describe(),
|
|
102
101
|
enableCommands: config.enableCommands && !config.readOnly,
|
|
102
|
+
deleteElicitation: deleteGateActive,
|
|
103
103
|
omnisearchUrl: obsidian.omnisearchUrl,
|
|
104
104
|
omnisearchReachable,
|
|
105
105
|
},
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAClG,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAEhE,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;AACjC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;AAEtC;;;;;GAKG;AACH,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;AACtC,MAAM,mBAAmB,GAAG,MAAM,QAAQ,CAAC,eAAe,EAAE,CAAC;AAE7D,MAAM,eAAe,GAAG,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,CAAC,CAAC;AAEtE
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The server-level `instructions` string sent on every
|
|
3
|
+
* `initialize`: baseline orientation, then deployment-specific lines for
|
|
4
|
+
* read-only mode, the effective path scope, the command-palette toggle, and
|
|
5
|
+
* Omnisearch availability.
|
|
6
|
+
* @module instructions
|
|
7
|
+
*/
|
|
8
|
+
import type { PathPolicy } from './services/obsidian/path-policy.js';
|
|
9
|
+
/**
|
|
10
|
+
* Build the instructions. The scope lines state what `path_forbidden`
|
|
11
|
+
* enforces (`PathPolicy.effectiveScope`), not the configured lists the
|
|
12
|
+
* startup banner reports.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildInstructions(opts: {
|
|
15
|
+
enableCommands: boolean;
|
|
16
|
+
omnisearchReachable: boolean;
|
|
17
|
+
policy: PathPolicy;
|
|
18
|
+
readOnly: boolean;
|
|
19
|
+
}): string;
|
|
20
|
+
//# sourceMappingURL=instructions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAarE;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB,GAAG,MAAM,CA6BT"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The server-level `instructions` string sent on every
|
|
3
|
+
* `initialize`: baseline orientation, then deployment-specific lines for
|
|
4
|
+
* read-only mode, the effective path scope, the command-palette toggle, and
|
|
5
|
+
* Omnisearch availability.
|
|
6
|
+
* @module instructions
|
|
7
|
+
*/
|
|
8
|
+
const BASELINE = 'Use the `obsidian_*` tools to access the Obsidian vault via the Local REST API plugin: search, read, write, and patch notes, including targeted edits to headings, blocks, and YAML frontmatter. Notes are addressed by vault-relative path including the file extension (e.g. `Folder/Note.md`); tags support hierarchical `parent/child` notation, and counts roll up to parents.';
|
|
9
|
+
const SCOPE_RECOVERY = 'Paths outside scope reject with `path_forbidden` — error data carries the active scope so you can self-correct.';
|
|
10
|
+
/** Prefixes as the instructions quote them; `undefined` is an unrestricted op. */
|
|
11
|
+
function renderScope(scope) {
|
|
12
|
+
return scope === undefined ? 'full vault' : scope.map((p) => `'${p}'`).join(', ');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the instructions. The scope lines state what `path_forbidden`
|
|
16
|
+
* enforces (`PathPolicy.effectiveScope`), not the configured lists the
|
|
17
|
+
* startup banner reports.
|
|
18
|
+
*/
|
|
19
|
+
export function buildInstructions(opts) {
|
|
20
|
+
const { enableCommands, omnisearchReachable, policy, readOnly } = opts;
|
|
21
|
+
const { readable, writable } = policy.effectiveScope();
|
|
22
|
+
const sections = [BASELINE];
|
|
23
|
+
if (readOnly) {
|
|
24
|
+
sections.push('Read-only mode is active (OBSIDIAN_READ_ONLY=true): write tools are unavailable. `obsidian_open_in_ui` still opens notes that exist, but rejects an open against a missing path — Obsidian would create the file.');
|
|
25
|
+
if (readable !== undefined) {
|
|
26
|
+
sections.push(`Vault path policy is enforced. Readable: ${renderScope(readable)}. ${SCOPE_RECOVERY}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (!policy.isUnrestricted) {
|
|
30
|
+
sections.push(`Vault path policy is enforced. Readable: ${renderScope(readable)}. Writable: ${renderScope(writable)}. ${SCOPE_RECOVERY}`);
|
|
31
|
+
}
|
|
32
|
+
if (enableCommands && !readOnly) {
|
|
33
|
+
sections.push('Command-palette tools (`obsidian_list_commands`, `obsidian_execute_command`) are enabled and can fire any Obsidian command. Commands are opaque and may be destructive — prefer dedicated tools when one fits.');
|
|
34
|
+
}
|
|
35
|
+
if (omnisearchReachable) {
|
|
36
|
+
sections.push('`obsidian_search_notes` includes an `omnisearch` mode: BM25-ranked, typo-tolerant, with PDF/OCR coverage (via the Text Extractor plugin). Results cap at 50 upstream — narrow the query (quoted phrases, `-exclusion`, `path:` / `ext:` filters) to surface more.');
|
|
37
|
+
}
|
|
38
|
+
return sections.join('\n\n');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=instructions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../src/instructions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,QAAQ,GACZ,qXAAqX,CAAC;AAExX,MAAM,cAAc,GAClB,iHAAiH,CAAC;AAEpH,kFAAkF;AAClF,SAAS,WAAW,CAAC,KAAoC;IACvD,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAKjC;IACC,MAAM,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IACvE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC;IACvD,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5B,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,IAAI,CACX,mNAAmN,CACpN,CAAC;QACF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CACX,4CAA4C,WAAW,CAAC,QAAQ,CAAC,KAAK,cAAc,EAAE,CACvF,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CACX,4CAA4C,WAAW,CAAC,QAAQ,CAAC,eAAe,WAAW,CAAC,QAAQ,CAAC,KAAK,cAAc,EAAE,CAC3H,CAAC;IACJ,CAAC;IACD,IAAI,cAAc,IAAI,CAAC,QAAQ,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CACX,gNAAgN,CACjN,CAAC;IACJ,CAAC;IACD,IAAI,mBAAmB,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CACX,mQAAmQ,CACpQ,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* @fileoverview obsidian://tags — vault tag listing with usage counts, for
|
|
3
3
|
* clients that prefer attaching resources.
|
|
4
4
|
*
|
|
5
|
-
* Not a mirror of `obsidian_list_tags`: this is a snapshot of
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Not a mirror of `obsidian_list_tags`: this is a snapshot of
|
|
6
|
+
* `ObsidianService.listTags`, returned whole and unsorted — the upstream
|
|
7
|
+
* `/tags/` payload in upstream order, or under OBSIDIAN_READ_PATHS the tags of
|
|
8
|
+
* readable notes in order of first appearance. The tool shapes the same
|
|
9
|
+
* payload for an LLM caller — count-descending, capped, filterable — and that
|
|
10
|
+
* shaping deliberately lives in the tool handler rather than the shared
|
|
11
|
+
* service call both make.
|
|
10
12
|
* @module mcp-server/resources/definitions/obsidian-tags.resource
|
|
11
13
|
*/
|
|
12
14
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obsidian-tags.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-tags.resource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"obsidian-tags.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-tags.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGrD,eAAO,MAAM,YAAY;;;;;6BA+BvB,CAAC"}
|
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
* @fileoverview obsidian://tags — vault tag listing with usage counts, for
|
|
3
3
|
* clients that prefer attaching resources.
|
|
4
4
|
*
|
|
5
|
-
* Not a mirror of `obsidian_list_tags`: this is a snapshot of
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* Not a mirror of `obsidian_list_tags`: this is a snapshot of
|
|
6
|
+
* `ObsidianService.listTags`, returned whole and unsorted — the upstream
|
|
7
|
+
* `/tags/` payload in upstream order, or under OBSIDIAN_READ_PATHS the tags of
|
|
8
|
+
* readable notes in order of first appearance. The tool shapes the same
|
|
9
|
+
* payload for an LLM caller — count-descending, capped, filterable — and that
|
|
10
|
+
* shaping deliberately lives in the tool handler rather than the shared
|
|
11
|
+
* service call both make.
|
|
10
12
|
* @module mcp-server/resources/definitions/obsidian-tags.resource
|
|
11
13
|
*/
|
|
12
14
|
import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
13
15
|
import { getObsidianService } from '../../../services/obsidian/obsidian-service.js';
|
|
14
16
|
export const obsidianTags = resource('obsidian://tags', {
|
|
15
17
|
name: 'obsidian-tags',
|
|
16
|
-
description: 'All tags found in the Obsidian vault, with usage counts,
|
|
18
|
+
description: 'All tags found in the Obsidian vault, with usage counts, unsorted and uncapped — a full snapshot. When OBSIDIAN_READ_PATHS is set, only tags carried by readable notes. Includes hierarchical parents (e.g. `work` for `work/tasks`). Use the `obsidian_list_tags` tool for a count-ranked, capped, filterable view.',
|
|
17
19
|
mimeType: 'application/json',
|
|
18
20
|
params: z.object({}),
|
|
19
21
|
output: z.object({
|
|
@@ -21,10 +23,12 @@ export const obsidianTags = resource('obsidian://tags', {
|
|
|
21
23
|
.array(z
|
|
22
24
|
.object({
|
|
23
25
|
name: z.string().describe('Tag name without the leading `#`.'),
|
|
24
|
-
count: z
|
|
26
|
+
count: z
|
|
27
|
+
.number()
|
|
28
|
+
.describe('Times the tag, or a tag nested under it, occurs across the vault. When OBSIDIAN_READ_PATHS is set: the number of readable notes carrying the tag or a tag nested under it.'),
|
|
25
29
|
})
|
|
26
30
|
.describe('A tag with its usage count.'))
|
|
27
|
-
.describe('Every tag in the vault, in
|
|
31
|
+
.describe('Every tag in the vault, in the order the Local REST API reports them. When OBSIDIAN_READ_PATHS is set: every tag carried by a readable note, in order of first appearance.'),
|
|
28
32
|
}),
|
|
29
33
|
auth: ['resource:obsidian-tags:read'],
|
|
30
34
|
async handler(_params, ctx) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obsidian-tags.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-tags.resource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"obsidian-tags.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/obsidian-tags.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAE7E,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC,iBAAiB,EAAE;IACtD,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,sTAAsT;IACxT,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC;aACJ,KAAK,CACJ,CAAC;aACE,MAAM,CAAC;YACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;YAC9D,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,CACP,4KAA4K,CAC7K;SACJ,CAAC;aACD,QAAQ,CAAC,6BAA6B,CAAC,CAC3C;aACA,QAAQ,CACP,4KAA4K,CAC7K;KACJ,CAAC;IACF,IAAI,EAAE,CAAC,6BAA6B,CAAC;IAErC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG;QACxB,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACrC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;IACvE,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -4,14 +4,23 @@
|
|
|
4
4
|
* 1. **Case-insensitive fallback.** If a path lookup 404s, list the parent
|
|
5
5
|
* directory and look for a single case-insensitive filename match. If
|
|
6
6
|
* found, retry against the canonical filesystem path (matches v2.x
|
|
7
|
-
* behavior)
|
|
8
|
-
*
|
|
7
|
+
* behavior) and report the path that was asked for as `requestedPath`, so
|
|
8
|
+
* the caller can disclose the substitution. The fallback is reachable on
|
|
9
|
+
* every host: the plugin's `note+json` and document-map reads resolve
|
|
10
|
+
* through Obsidian's case-sensitive vault index even on a case-insensitive
|
|
11
|
+
* filesystem (macOS, Windows). Only the raw-markdown read case-folds there,
|
|
12
|
+
* answering 200 for a wrong-case path before any fallback runs.
|
|
9
13
|
*
|
|
10
14
|
* 2. **"Did you mean" suggestions.** When no case match exists but the parent
|
|
11
15
|
* directory has near-matches (e.g., extension-stripped variants), re-throw
|
|
12
16
|
* NotFound enriched with the candidates in the message and
|
|
13
17
|
* `error.data.suggestions[]`.
|
|
14
18
|
*
|
|
19
|
+
* 3. **Case-variant naming on writes.** A whole-file write never substitutes
|
|
20
|
+
* a path, but on a case-insensitive filesystem a write to a case variant of
|
|
21
|
+
* an existing note is refused (`case_variant_exists`); the same listing
|
|
22
|
+
* names the note it collided with as `existingPath`.
|
|
23
|
+
*
|
|
15
24
|
* `obsidian_get_note` and `obsidian_open_in_ui` wrap their service calls with
|
|
16
25
|
* `withCaseFallback`; writes and deletes match the exact path.
|
|
17
26
|
* @module mcp-server/tools/definitions/_shared/suggest-paths
|
|
@@ -19,6 +28,16 @@
|
|
|
19
28
|
import type { Context } from '@cyanheads/mcp-ts-core';
|
|
20
29
|
import type { ObsidianService } from '../../../../services/obsidian/obsidian-service.js';
|
|
21
30
|
import type { NoteTarget } from '../../../../services/obsidian/types.js';
|
|
31
|
+
/** Outcome of {@link withCaseFallback}; `requestedPath` is present only when another path was read. */
|
|
32
|
+
export type CaseFallback<T> = {
|
|
33
|
+
result: T;
|
|
34
|
+
resolvedPath: string | undefined;
|
|
35
|
+
requestedPath?: never;
|
|
36
|
+
} | {
|
|
37
|
+
result: T;
|
|
38
|
+
resolvedPath: string;
|
|
39
|
+
requestedPath: string;
|
|
40
|
+
};
|
|
22
41
|
/**
|
|
23
42
|
* Wrap a service call with case-insensitive path fallback and "did you mean"
|
|
24
43
|
* enrichment. Non-path targets pass through — their paths are resolved
|
|
@@ -27,7 +46,7 @@ import type { NoteTarget } from '../../../../services/obsidian/types.js';
|
|
|
27
46
|
* For path targets:
|
|
28
47
|
* - **Exact match** → returns `{ result, resolvedPath: target.path }`.
|
|
29
48
|
* - **Single case match** → retries with the canonical path and returns
|
|
30
|
-
* `{ result, resolvedPath: <canonical
|
|
49
|
+
* `{ result, resolvedPath: <canonical>, requestedPath: target.path }`.
|
|
31
50
|
* - **Multiple case matches** → throws `Conflict` with the candidates so the
|
|
32
51
|
* agent can disambiguate.
|
|
33
52
|
* - **No case match, extension-stripped near-matches** → throws `NotFound`
|
|
@@ -36,17 +55,58 @@ import type { NoteTarget } from '../../../../services/obsidian/types.js';
|
|
|
36
55
|
*
|
|
37
56
|
* `resolvedPath` is `undefined` for non-path targets — callers derive the
|
|
38
57
|
* canonical path from the result itself (typically `NoteJson.path`).
|
|
58
|
+
* `requestedPath` is present only on the single-case-match branch.
|
|
39
59
|
*/
|
|
40
|
-
export declare function withCaseFallback<T>(ctx: Context, svc: ObsidianService, target: NoteTarget, fn: (target: NoteTarget) => Promise<T>): Promise<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
export declare function withCaseFallback<T>(ctx: Context, svc: ObsidianService, target: NoteTarget, fn: (target: NoteTarget) => Promise<T>): Promise<CaseFallback<T>>;
|
|
61
|
+
/**
|
|
62
|
+
* Disclose a `withCaseFallback` substitution on the caller's enrichment: the
|
|
63
|
+
* `requestedPath` field plus a one-line notice naming both paths. `action` is
|
|
64
|
+
* what the tool did with the canonical file. Returns the notice — `notice` is
|
|
65
|
+
* last-wins, so a caller that sets another one must fold this text into it —
|
|
66
|
+
* or `undefined` when nothing was substituted.
|
|
67
|
+
*/
|
|
68
|
+
export declare function discloseCaseSubstitution(ctx: Context, fallback: CaseFallback<unknown>, action: 'read' | 'opened'): string | undefined;
|
|
44
69
|
/**
|
|
45
70
|
* List the parent directory of `path` and return up to {@link MAX_SUGGESTIONS}
|
|
46
71
|
* close-match candidates. Match order: case-insensitive equality first, then
|
|
47
72
|
* extension-stripped equality. Returns `[]` on listing failure or empty
|
|
48
|
-
* basename. Used by callers that need suggestions without
|
|
49
|
-
*
|
|
73
|
+
* basename. Used by callers that need suggestions without substituting a
|
|
74
|
+
* match (e.g., `obsidian_delete_note`'s exact-case miss, which names the
|
|
75
|
+
* candidates but never deletes one in place of the requested path).
|
|
50
76
|
*/
|
|
51
77
|
export declare function findSimilarPaths(ctx: Context, svc: ObsidianService, path: string): Promise<string[]>;
|
|
78
|
+
/** What the parent folder listing shows about the spelling of a path. */
|
|
79
|
+
export interface CaseVariants {
|
|
80
|
+
/** The path itself is listed under exactly that name. */
|
|
81
|
+
exact: boolean;
|
|
82
|
+
/** Listed files whose names equal the path's ignoring case, but not exactly (full vault paths). */
|
|
83
|
+
variants: string[];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read the parent folder listing for files that differ from `path` only in
|
|
87
|
+
* letter case. A listing that fails, or that the read scope withholds, reads
|
|
88
|
+
* as empty, so no path outside the read scope is ever named; the caller's
|
|
89
|
+
* cancellation propagates as itself.
|
|
90
|
+
*/
|
|
91
|
+
export declare function findCaseVariants(ctx: Context, svc: ObsidianService, path: string): Promise<CaseVariants>;
|
|
92
|
+
/**
|
|
93
|
+
* Message and error data for a `case_variant_exists` refusal. `existingPath`
|
|
94
|
+
* is set only when exactly one variant is listed — never picked from several,
|
|
95
|
+
* and never inferred when none is: a file Obsidian does not index (a dot-file)
|
|
96
|
+
* refuses a write the same way and never appears in a listing.
|
|
97
|
+
*/
|
|
98
|
+
export declare function caseVariantError(path: string, variants: readonly string[]): {
|
|
99
|
+
message: string;
|
|
100
|
+
data: {
|
|
101
|
+
path: string;
|
|
102
|
+
existingPath?: string;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Run a whole-file write to `path`, and when the service refuses it as
|
|
107
|
+
* `case_variant_exists`, rethrow it naming the existing note from the parent
|
|
108
|
+
* listing. The listing is read only on that failure, so a write that succeeds
|
|
109
|
+
* costs no extra request.
|
|
110
|
+
*/
|
|
111
|
+
export declare function withCaseVariantNamed<T>(ctx: Context, svc: ObsidianService, path: string, write: () => Promise<T>): Promise<T>;
|
|
52
112
|
//# sourceMappingURL=suggest-paths.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suggest-paths.d.ts","sourceRoot":"","sources":["../../../../../src/mcp-server/tools/definitions/_shared/suggest-paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"suggest-paths.d.ts","sourceRoot":"","sources":["../../../../../src/mcp-server/tools/definitions/_shared/suggest-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAW/D,uGAAuG;AACvG,MAAM,MAAM,YAAY,CAAC,CAAC,IACtB;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,KAAK,CAAA;CAAE,GACtE;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,UAAU,EAClB,EAAE,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GACrC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAsC1B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,GAAG,QAAQ,GACxB,MAAM,GAAG,SAAS,CAOpB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,mGAAmG;IACnG,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAOvB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAYpE;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,CAAC,CAAC,CASZ"}
|