obsidian-mcp-pro 2.1.0 → 4.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.
Files changed (102) hide show
  1. package/README.md +73 -42
  2. package/build/config.d.ts.map +1 -1
  3. package/build/config.js +44 -9
  4. package/build/config.js.map +1 -1
  5. package/build/http-server.d.ts +4 -2
  6. package/build/http-server.d.ts.map +1 -1
  7. package/build/http-server.js +52 -20
  8. package/build/http-server.js.map +1 -1
  9. package/build/index.d.ts.map +1 -1
  10. package/build/index.js +36 -38
  11. package/build/index.js.map +1 -1
  12. package/build/install.d.ts.map +1 -1
  13. package/build/install.js +1 -1
  14. package/build/install.js.map +1 -1
  15. package/build/lib/bases.d.ts +11 -4
  16. package/build/lib/bases.d.ts.map +1 -1
  17. package/build/lib/bases.js +21 -19
  18. package/build/lib/bases.js.map +1 -1
  19. package/build/lib/chunker.js +81 -2
  20. package/build/lib/chunker.js.map +1 -1
  21. package/build/lib/confirmation.d.ts +21 -0
  22. package/build/lib/confirmation.d.ts.map +1 -0
  23. package/build/lib/confirmation.js +37 -0
  24. package/build/lib/confirmation.js.map +1 -0
  25. package/build/lib/embedding-providers.d.ts.map +1 -1
  26. package/build/lib/embedding-providers.js +5 -15
  27. package/build/lib/embedding-providers.js.map +1 -1
  28. package/build/lib/embedding-store.d.ts +4 -0
  29. package/build/lib/embedding-store.d.ts.map +1 -1
  30. package/build/lib/embedding-store.js +100 -22
  31. package/build/lib/embedding-store.js.map +1 -1
  32. package/build/lib/errors.d.ts +13 -5
  33. package/build/lib/errors.d.ts.map +1 -1
  34. package/build/lib/errors.js +53 -19
  35. package/build/lib/errors.js.map +1 -1
  36. package/build/lib/fs-ops.d.ts +3 -0
  37. package/build/lib/fs-ops.d.ts.map +1 -0
  38. package/build/lib/fs-ops.js +32 -0
  39. package/build/lib/fs-ops.js.map +1 -0
  40. package/build/lib/index-cache.d.ts +13 -7
  41. package/build/lib/index-cache.d.ts.map +1 -1
  42. package/build/lib/index-cache.js +49 -205
  43. package/build/lib/index-cache.js.map +1 -1
  44. package/build/lib/link-rewriter.d.ts.map +1 -1
  45. package/build/lib/link-rewriter.js +23 -6
  46. package/build/lib/link-rewriter.js.map +1 -1
  47. package/build/lib/logger.d.ts.map +1 -1
  48. package/build/lib/logger.js +143 -23
  49. package/build/lib/logger.js.map +1 -1
  50. package/build/lib/markdown.d.ts +16 -5
  51. package/build/lib/markdown.d.ts.map +1 -1
  52. package/build/lib/markdown.js +143 -24
  53. package/build/lib/markdown.js.map +1 -1
  54. package/build/lib/permissions.d.ts.map +1 -1
  55. package/build/lib/permissions.js +13 -7
  56. package/build/lib/permissions.js.map +1 -1
  57. package/build/lib/tag-rewriter.d.ts.map +1 -1
  58. package/build/lib/tag-rewriter.js +12 -43
  59. package/build/lib/tag-rewriter.js.map +1 -1
  60. package/build/lib/tool-output.d.ts +11 -0
  61. package/build/lib/tool-output.d.ts.map +1 -0
  62. package/build/lib/tool-output.js +40 -0
  63. package/build/lib/tool-output.js.map +1 -0
  64. package/build/lib/vault.d.ts +61 -3
  65. package/build/lib/vault.d.ts.map +1 -1
  66. package/build/lib/vault.js +566 -160
  67. package/build/lib/vault.js.map +1 -1
  68. package/build/lib/yaml.d.ts +2 -0
  69. package/build/lib/yaml.d.ts.map +1 -0
  70. package/build/lib/yaml.js +66 -0
  71. package/build/lib/yaml.js.map +1 -0
  72. package/build/tools/attachments.d.ts.map +1 -1
  73. package/build/tools/attachments.js +213 -60
  74. package/build/tools/attachments.js.map +1 -1
  75. package/build/tools/bases.d.ts.map +1 -1
  76. package/build/tools/bases.js +50 -57
  77. package/build/tools/bases.js.map +1 -1
  78. package/build/tools/canvas.d.ts.map +1 -1
  79. package/build/tools/canvas.js +190 -66
  80. package/build/tools/canvas.js.map +1 -1
  81. package/build/tools/links.d.ts.map +1 -1
  82. package/build/tools/links.js +253 -163
  83. package/build/tools/links.js.map +1 -1
  84. package/build/tools/prompts.d.ts.map +1 -1
  85. package/build/tools/prompts.js +11 -9
  86. package/build/tools/prompts.js.map +1 -1
  87. package/build/tools/read.d.ts.map +1 -1
  88. package/build/tools/read.js +200 -123
  89. package/build/tools/read.js.map +1 -1
  90. package/build/tools/sections.d.ts.map +1 -1
  91. package/build/tools/sections.js +427 -33
  92. package/build/tools/sections.js.map +1 -1
  93. package/build/tools/semantic.d.ts.map +1 -1
  94. package/build/tools/semantic.js +173 -53
  95. package/build/tools/semantic.js.map +1 -1
  96. package/build/tools/tags.d.ts.map +1 -1
  97. package/build/tools/tags.js +160 -59
  98. package/build/tools/tags.js.map +1 -1
  99. package/build/tools/write.d.ts.map +1 -1
  100. package/build/tools/write.js +65 -25
  101. package/build/tools/write.js.map +1 -1
  102. package/package.json +20 -11
package/README.md CHANGED
@@ -18,9 +18,9 @@
18
18
  [![npm downloads](https://img.shields.io/npm/dm/obsidian-mcp-pro.svg)](https://www.npmjs.com/package/obsidian-mcp-pro)
19
19
  [![GitHub stars](https://img.shields.io/github/stars/rps321321/obsidian-mcp-pro?style=flat&logo=github)](https://github.com/rps321321/obsidian-mcp-pro)
20
20
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
21
- [![Node >= 18](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org/)
22
- [![Tests](https://img.shields.io/badge/tests-449_passing-brightgreen.svg)](https://github.com/rps321321/obsidian-mcp-pro)
23
- [![Tool Quality](https://img.shields.io/badge/Glama-23_tools_A--grade-success)](https://glama.ai/mcp/servers/rps321321/obsidian-mcp-pro)
21
+ [![Node >= 24](https://img.shields.io/badge/node-%3E%3D24-brightgreen.svg)](https://nodejs.org/)
22
+ [![Tests](https://img.shields.io/badge/tests-664_passing-brightgreen.svg)](https://github.com/rps321321/obsidian-mcp-pro)
23
+ [![Tool Quality](https://img.shields.io/badge/Glama-41_tools_A--grade-success)](https://glama.ai/mcp/servers/rps321321/obsidian-mcp-pro)
24
24
 
25
25
  Give AI assistants deep, structured access to your Obsidian knowledge base. Read, write, search, tag, analyze links, traverse graphs, manipulate canvases, query Bases, edit by heading or block reference, run semantic search, and pull binary attachments. All through the [Model Context Protocol](https://modelcontextprotocol.io/).
26
26
 
@@ -50,7 +50,7 @@ Give AI assistants deep, structured access to your Obsidian knowledge base. Read
50
50
  ## Features
51
51
 
52
52
  ### Read & Search
53
- - Full-text search across all vault notes (cached: re-runs only re-read changed files)
53
+ - Focus-ranked full-text search across all vault notes, with query-centered snippets (cached: re-runs only re-read changed files)
54
54
  - Read individual notes whole, or as a fragment by heading path, block id, or line range
55
55
  - List and filter notes by folder, date, or pattern
56
56
  - Search by frontmatter fields and values
@@ -86,7 +86,7 @@ Give AI assistants deep, structured access to your Obsidian knowledge base. Read
86
86
  ### Bases (Obsidian 1.10+)
87
87
  - `list_bases` enumerates `.base` files
88
88
  - `read_base` returns the parsed YAML (filters, properties, views)
89
- - `query_base` runs the filter DSL against the vault and returns matching notes; supports `taggedWith()`, `file.hasTag()`, `file.inFolder()`, comparison operators, and `and`/`or`/`not` combinators
89
+ - `query_base` runs the filter DSL against the vault and returns matching notes; supports `taggedWith()`, `file.hasTag()`, `file.inFolder()`, comparison operators, and `and`/`or`/`not` combinators. Unsupported filters warn and fail closed instead of broadening results.
90
90
 
91
91
  ### Attachments
92
92
  - `list_attachments` enumerates every non-md/canvas/base file with a per-extension count summary
@@ -95,8 +95,10 @@ Give AI assistants deep, structured access to your Obsidian knowledge base. Read
95
95
 
96
96
  ### Semantic Search (optional, Ollama or OpenAI)
97
97
  - `index_vault` chunks each note (heading-aware), embeds via the configured provider, persists vectors to `<vault>/.obsidian/cache/`, and incrementally re-embeds only changed notes
98
+ - Because indexing sends readable note chunks to the configured embedding provider, `index_vault` requires `confirm: "send-vault-text-to-embedding-provider"` on each call
98
99
  - `search_semantic` ranks notes by cosine similarity against an embedded query
99
- - `find_similar_notes` reuses an existing note's embeddings to surface neighbors without a live API call
100
+ - `find_similar_notes` reuses an existing note's embeddings, with source-topic anchoring, to surface neighbors without a live API call
101
+ - Stored snippets are returned only while the current note content hash still matches the indexed text; stale chunks are pruned on search
100
102
 
101
103
  ### MCP Resources
102
104
  - `obsidian://note/{path}` reads any note by its vault-relative path
@@ -114,9 +116,9 @@ The server exposes five starter prompts that clients (Claude Desktop, Cursor) su
114
116
 
115
117
  ### Operational features
116
118
  - **Folder-scoped permissions**: `OBSIDIAN_READ_PATHS` / `OBSIDIAN_WRITE_PATHS` allowlists gate every tool at the path-resolution choke point
117
- - **Persistent mtime cache** at `<vault>/.obsidian/cache/mcp-pro-index-cache.json` survives restarts; subsequent vault scans serve from cache after one stat-pass
119
+ - **In-memory mtime cache** speeds repeated vault scans within a running process without persisting note bodies to disk
118
120
  - **Progress notifications** (`notifications/progress`) on `rename_tag`, `find_unused_attachments`, and `index_vault` when the client subscribes via `_meta.progressToken`
119
- - **Elicitation** prompts the user to retype the note path on `delete_note(permanent: true)` when the client supports it
121
+ - **Hard bulk-write latches** require `confirmPath` for default `move_note` reference rewrites, `confirmTag` for non-dry-run `rename_tag`, and `confirm=true` for permanent deletion; clients with elicitation support also get typed prompts
120
122
 
121
123
  ---
122
124
 
@@ -178,22 +180,22 @@ claude mcp add obsidian-mcp-pro -- npx -y obsidian-mcp-pro
178
180
  ### HTTP Transport (Remote Clients, Cursor, ChatGPT, Web)
179
181
 
180
182
  ```bash
181
- npx -y obsidian-mcp-pro --transport=http --port=3333
183
+ MCP_HTTP_TOKEN=your-secret npx -y obsidian-mcp-pro --transport=http --port=3333
182
184
  ```
183
185
 
184
- Endpoint: `http://127.0.0.1:3333/mcp` (Streamable HTTP). Protect with a bearer token:
186
+ Endpoint: `http://127.0.0.1:3333/mcp` (Streamable HTTP). HTTP transport requires a bearer token:
185
187
 
186
188
  ```bash
187
- npx -y obsidian-mcp-pro --transport=http --token=your-secret
188
- # or: MCP_HTTP_TOKEN=your-secret npx -y obsidian-mcp-pro --transport=http
189
+ MCP_HTTP_TOKEN=your-secret npx -y obsidian-mcp-pro --transport=http
189
190
  ```
190
191
 
191
192
  The HTTP server binds to `127.0.0.1` by default with DNS rebinding protection enabled.
193
+ Startup always requires `MCP_HTTP_TOKEN`, including loopback-only local servers. The old `--token` flag was removed because command-line secrets can be exposed through OS process listings.
192
194
 
193
195
  > [!WARNING]
194
- > **Never bind `--host=0.0.0.0` directly to the public internet.** Doing so exposes your entire Obsidian vault to anyone who can reach the port. If you need remote access:
196
+ > **Never bind `--host=0.0.0.0` directly to the public internet.** Doing so exposes your entire Obsidian vault to anyone who can reach the port. The server refuses HTTP startup without a bearer token, but if you need remote access:
195
197
  > - Put the server behind a reverse proxy (nginx, Caddy, Cloudflare Tunnel) that terminates TLS, **and**
196
- > - Require `--token=<secret>` (or `MCP_HTTP_TOKEN`), **and**
198
+ > - Set `MCP_HTTP_TOKEN`, **and**
197
199
  > - Restrict `--allow-origin` to the specific origins you trust, **and**
198
200
  > - Set `--rate-limit` to cap request volume per IP.
199
201
  >
@@ -203,17 +205,17 @@ Additional hardening flags:
203
205
 
204
206
  | Flag | Purpose |
205
207
  |------|---------|
206
- | `--allow-origin=<csv>` | Restrict CORS to an allowlist (e.g. `https://claude.ai,https://chat.openai.com`). Default is `*`. |
208
+ | `--allow-origin=<csv>` | Restrict CORS to an allowlist (e.g. `https://claude.ai,https://chat.openai.com`). Default is localhost-only; `*` requires bearer auth. |
207
209
  | `--rate-limit=<n>` | Cap requests per minute per client IP. `/health` and `/version` are exempt. Default is unlimited. |
208
210
 
209
211
  Operational endpoints (no auth required):
210
212
 
211
213
  | Endpoint | Returns |
212
214
  |----------|---------|
213
- | `GET /health` | `{ status: "ok", sessions: <n>, version: <string> }` — liveness + session count. |
215
+ | `GET /health` | `{ status: "ok", version: <string> }` — liveness for monitoring. |
214
216
  | `GET /version` | `{ version: <string> }` — package version, for rollout auditing. |
215
217
 
216
- Structured logging is controlled by `LOG_LEVEL` (`debug`/`info`/`warn`/`error`/`silent`, default `info`) and `LOG_FORMAT` (`text`/`json`, default `text`). All logs go to stderr so the stdio transport on stdout is never polluted.
218
+ Structured logging is controlled by `LOG_LEVEL` (`debug`/`info`/`warn`/`error`/`silent`, default `info`) and `LOG_FORMAT` (`text`/`json`, default `text`). All logs go to stderr so the stdio transport on stdout is never polluted, with absolute paths, vault-relative path fields, secret-bearing URLs, and control characters redacted before write.
217
219
 
218
220
  ---
219
221
 
@@ -303,18 +305,22 @@ Restrict the tools' read/write surface to specific folders without exposing the
303
305
  | `OBSIDIAN_READ_PATHS` | Comma- or colon-separated list of folders that read tools may access. Unset means unrestricted. Use `.` to mean the vault root. |
304
306
  | `OBSIDIAN_WRITE_PATHS` | Same shape, but for mutations (create / append / update / delete / move / surgical edits). |
305
307
 
306
- Read and write are independent, so an audit account can be read-only on most of the vault but write-only to a `Drafts/` folder. The startup log line and `--help` advertise the active scope. The allowlist is enforced at a single path-resolution choke point so every tool inherits it.
308
+ Read and write are independent, so an audit account can be read-only on most of the vault but write-only to a `Drafts/` folder. Moving a note requires read access to the source and write access to the destination, because the move carries the source content into its new folder. Surgical edits (`replace_in_note`, `update_section`, `insert_at_section`, and `edit_block`) require read access to the target plus write access because they inspect existing content before writing. In-vault symlinks are checked against their real target before the allowlist decision is finalized. The startup log line and `--help` advertise the active scope. The allowlist is enforced at a single path-resolution choke point so every tool inherits it.
307
309
 
308
- ### Persistent Caches
310
+ ### Caches
309
311
 
310
- Two caches live under `<vault>/.obsidian/cache/`:
312
+ The note-content mtime cache is in-memory only. It speeds repeated vault-wide scans
313
+ inside the running process, but it does not persist note bodies or absolute paths to
314
+ disk. Any legacy `<vault>/.obsidian/cache/mcp-pro-index-cache.json` snapshot is
315
+ ignored and removed on first cache use.
316
+
317
+ Semantic embeddings live under `<vault>/.obsidian/cache/`:
311
318
 
312
319
  | File | Purpose |
313
320
  |------|---------|
314
- | `mcp-pro-index-cache.json` | mtime-keyed snapshot of recently read notes. The next process start hydrates from this and stat-passes against the live filesystem; only changed notes are re-read. |
315
- | `mcp-pro-embeddings.json` | Persisted embeddings for semantic search (only present once `index_vault` has run). Vault-relocation safe via an embedded `vaultRoot` check; switching providers / models invalidates entries automatically. |
321
+ | `mcp-pro-embeddings.json` | Persisted embeddings for semantic search (only present once `index_vault` has run). Vault-relocation safe via an embedded `vaultRoot` check; switching providers/models or stale note content invalidates entries automatically. |
316
322
 
317
- Both are vault-local, are excluded from vault scans (`.obsidian/` is pruned), and can be deleted at any time. Persistence can be turned off entirely with `OBSIDIAN_CACHE_DISABLED=1`.
323
+ The embedding cache is vault-local, excluded from vault scans (`.obsidian/` is pruned), and can be deleted at any time. Persistence can be turned off with `OBSIDIAN_CACHE_DISABLED=1`.
318
324
 
319
325
  ### Semantic Search Provider
320
326
 
@@ -329,9 +335,17 @@ The semantic-search tools (`index_vault`, `search_semantic`, `find_similar_notes
329
335
 
330
336
  For local Ollama: install [Ollama](https://ollama.com/), then `ollama pull nomic-embed-text`. The semantic tools register even when no provider is configured, so they're discoverable; calls return a configuration hint until set up.
331
337
 
338
+ `index_vault` sends readable note chunks to that provider. Calls must include:
339
+
340
+ ```json
341
+ { "confirm": "send-vault-text-to-embedding-provider" }
342
+ ```
343
+
344
+ This latch is per call so scripted index refreshes have to keep the privacy decision visible.
345
+
332
346
  ### Observability
333
347
 
334
- Logs stream to stderr as either plain text (default) or single-line JSON, controlled by `LOG_LEVEL` (`debug`/`info`/`warn`/`error`/`silent`) and `LOG_FORMAT` (`text`/`json`).
348
+ Logs stream to stderr as either plain text (default) or single-line JSON, controlled by `LOG_LEVEL` (`debug`/`info`/`warn`/`error`/`silent`) and `LOG_FORMAT` (`text`/`json`). Local stderr and MCP-forwarded log payloads both redact absolute paths, vault-relative path fields, secret-bearing URLs, and control characters.
335
349
 
336
350
  The server also declares the MCP [`logging` capability](https://modelcontextprotocol.io/specification), so every log line is forwarded to the connected client as a `notifications/message` frame alongside tool responses. Clients that honor `logging/setLevel` can filter server-side logs at runtime without restarting. Claude Desktop surfaces these in its MCP DevTools pane; most other clients currently ignore them, so this is useful primarily for self-hosters and tooling authors.
337
351
 
@@ -339,12 +353,22 @@ The server also declares the MCP [`logging` capability](https://modelcontextprot
339
353
 
340
354
  ## Security
341
355
 
342
- - **Vault boundary** — every tool and resource routes through a single path resolver that rejects `..` traversal, null-byte injection, and symlinks pointing outside the vault (ancestor-realpath check).
356
+ - **Vault boundary** — every tool and resource routes through a single path resolver that rejects `..` traversal, null-byte injection, and symlinks pointing outside the vault (ancestor-realpath check). On Windows it also rejects reserved device names, alternate data stream syntax, and trailing-dot/space filename aliases.
357
+ - **Note/file boundary** — note read and edit surfaces only target `.md` files; attachments, Canvas files, and Bases stay on their dedicated tools with their own caps and parsers.
358
+ - **Full-note cap** — full `.md` note reads and read-modify-write helpers refuse notes over 5 MiB before materializing them; `get_note` line fragments still stream from large notes for targeted inspection.
343
359
  - **Excluded directories** — `.obsidian`, `.git`, and `.trash` are pruned at traversal time and at resolution time, so nested occurrences never leak back to clients.
344
- - **HTTP transport** — binds to `127.0.0.1` by default with DNS rebinding protection (host-header allowlist). Optional `--token=<secret>` requires `Authorization: Bearer <secret>` on every `/mcp` request; compared in constant time.
345
- - **Error sanitization** — filesystem error messages are stripped of absolute host paths before being returned to MCP clients. Uncaught HTTP errors respond with a generic `Internal server error` body; full detail stays in the server log.
360
+ - **HTTP transport** — requires a bearer token at startup and binds to `127.0.0.1` by default with DNS rebinding protection (host-header allowlist). `/mcp` requests must send `Authorization: Bearer <secret>`, compared in constant time. Malformed request URL or Host data is rejected before routing.
361
+ - **Attachment safety** — executable extensions are blocked, SVGs are returned as `text/plain`, and active text formats such as HTML/XML/CSS are served with `text/plain` resource metadata.
362
+ - **Canvas link safety** — Canvas link nodes added through the server accept only absolute `http://` and `https://` URLs, preventing local file and application-protocol links from being persisted by tool calls.
363
+ - **Regex edit safety** — `replace_in_note` caps regex pattern/input size and rejects backtracking-prone repeated groups before matching.
364
+ - **Error and log sanitization** — filesystem error messages are stripped of absolute host paths before being returned to MCP clients, local stderr and MCP-forwarded logs redact paths and secret-bearing URLs, ASCII control bytes and Unicode bidi controls are escaped in displayed values, and note-derived duplicate-alias text is not copied into graph warnings. Uncaught HTTP errors respond with a generic `Internal server error` body while server logs keep sanitized diagnostics.
365
+ - **Untrusted vault text boundaries** — tool outputs that include note bodies, read, search result, semantic result, semantic index failure, write rewrite-warning, link-graph, attachment, Base, and Canvas path summaries, attachment extension summaries, search snippets, semantic snippets and heading paths, tag lists, tag search result paths and previews, tag rename skipped-note rows, frontmatter values, Base data, wikilink targets in link-analysis output, SVG attachment text, section headings, Canvas node identities, colors, previews, edge endpoints, and edge labels, or backlink context wrap those vault-authored portions in `[BEGIN UNTRUSTED VAULT CONTENT: ...]` / `[END UNTRUSTED VAULT CONTENT: ...]` markers before they enter an MCP client's model context. Note and daily resources use the same visible boundaries for markdown bodies and carry `_meta["obsidian-mcp-pro/contentTrust"] = "untrusted-vault-content"`.
366
+ - **YAML parser boundaries** — Obsidian properties are parsed only from `---` YAML delimiter lines; non-YAML gray-matter language blocks stay as body text, oversized frontmatter is skipped on reads and refused for metadata updates, and note/Base YAML containing anchors or aliases is not parsed.
367
+ - **Semantic index freshness** — `search_semantic` and `find_similar_notes` re-check current note content hashes before returning stored snippets or source-note embeddings, pruning stale cache entries instead of surfacing old note text.
368
+ - **Semantic indexing confirmation** — `index_vault` refuses to read and embed vault notes unless the call includes `confirm: "send-vault-text-to-embedding-provider"`, making provider-bound note transfer explicit.
369
+ - **Bulk-write confirmations** — `move_note` reference rewrites require `confirmPath` to match the destination path, and non-dry-run `rename_tag` requires `confirmTag` to match the new tag. Clients that support MCP elicitation are also asked to re-type the destination path or new tag before the rewrite runs.
346
370
  - **Atomic writes** — every note write (`create_note`, `append`, `prepend`, `update_frontmatter`, canvas mutations) stages content to a sibling temp file then renames onto the target, so a crash or kill mid-write never leaves a truncated file. Combined with per-path locks for the full read-modify-write cycle, concurrent callers can't lose each other's updates. The `install` subcommand uses the same pattern and keeps a backup of the previous config.
347
- - **Rate limiting + CORS allowlist** — optional `--rate-limit` caps per-IP request volume; `--allow-origin` restricts browser-facing CORS. `/health` and `/version` stay reachable under load for monitoring.
371
+ - **Rate limiting + CORS allowlist** — optional `--rate-limit` caps per-IP request volume; `--allow-origin` restricts browser-facing CORS and refuses `*` unless bearer auth is enabled. `/health` and `/version` stay reachable under load for monitoring.
348
372
  - **Request timeout** — HTTP POST requests are capped at 2 minutes of wall-clock time. Long-lived SSE GET streams are exempt so idle clients aren't reaped.
349
373
  - **Process supervision** — `uncaughtException` exits cleanly so systemd/Docker/npx supervisors can restart; `unhandledRejection` logs but doesn't kill the process.
350
374
 
@@ -369,11 +393,11 @@ Tag extraction is similarly case-tolerant: `tags`, `Tags`, `TAGS`, `tag`, and `T
369
393
 
370
394
  | Tool | Description | Key Parameters |
371
395
  |------|-------------|----------------|
372
- | `search_notes` | Full-text search across all notes (cached) | `query`, `caseSensitive`, `maxResults`, `folder` |
396
+ | `search_notes` | Focus-ranked full-text search with query-centered snippets (cached) | `query`, `caseSensitive`, `maxResults`, `folder` |
373
397
  | `get_note` | Read a note whole, or by `section` / `block` / `lines` | `path`, `section`, `block`, `lines` |
374
398
  | `list_notes` | List notes in the vault or a folder | `folder`, `limit` |
375
399
  | `get_daily_note` | Get today's (or a specific date's) daily note | `date` |
376
- | `search_by_frontmatter` | Find notes by frontmatter property values | `property`, `value`, `folder` |
400
+ | `search_by_frontmatter` | Find notes by frontmatter property values with case-insensitive key/value matching | `property`, `value`, `folder` |
377
401
  | `get_recent_notes` | Notes sorted by mtime; optional ISO-or-relative `since` filter | `limit`, `since`, `folder` |
378
402
  | `get_vault_stats` | Vault counts, bytes, words, tag coverage, most-recent note | `folder` |
379
403
  | `resolve_alias` | Translate frontmatter alias (or basename) to note path | `name`, `includeBasename` |
@@ -387,7 +411,7 @@ Tag extraction is similarly case-tolerant: `tags`, `Tags`, `TAGS`, `tag`, and `T
387
411
  | `prepend_to_note` | Prepend content after frontmatter | `path`, `content` |
388
412
  | `update_frontmatter` | Update frontmatter properties on a note | `path`, `properties` |
389
413
  | `create_daily_note` | Create today's daily note from template | `date`, `content`, `templatePath` |
390
- | `move_note` | Move or rename a note; rewrites references across the vault | `oldPath`, `newPath`, `updateLinks` |
414
+ | `move_note` | Move or rename a note; rewrites references across the vault | `oldPath`, `newPath`, `updateLinks`, `confirmPath` |
391
415
  | `delete_note` | Delete a note (trash by default); optional elicitation on permanent | `path`, `permanent`, `removeReferences` |
392
416
 
393
417
  ### Section-level edits
@@ -406,7 +430,7 @@ Tag extraction is similarly case-tolerant: `tags`, `Tags`, `TAGS`, `tag`, and `T
406
430
  |------|-------------|----------------|
407
431
  | `get_tags` | Get all tags and their usage counts | `sortBy` |
408
432
  | `search_by_tag` | Find all notes with a specific tag | `tag`, `includeContent` |
409
- | `rename_tag` | Rewrite inline + frontmatter occurrences vault-wide; hierarchical | `oldName`, `newName`, `hierarchical`, `dryRun` |
433
+ | `rename_tag` | Rewrite inline + frontmatter occurrences vault-wide; hierarchical | `oldName`, `newName`, `hierarchical`, `dryRun`, `confirmTag` |
410
434
 
411
435
  ### Links & graph
412
436
 
@@ -423,7 +447,7 @@ Tag extraction is similarly case-tolerant: `tags`, `Tags`, `TAGS`, `tag`, and `T
423
447
  | Tool | Description | Key Parameters |
424
448
  |------|-------------|----------------|
425
449
  | `list_canvases` | List all `.canvas` files in the vault | (none) |
426
- | `read_canvas` | Read a `.canvas` file's nodes and edges | `path` |
450
+ | `read_canvas` | Read a bounded `.canvas` node/edge summary | `path` |
427
451
  | `add_canvas_node` | Add a node to a canvas | `canvasPath`, `type`, `content`, `x`, `y` |
428
452
  | `add_canvas_edge` | Add an edge between two canvas nodes | `canvasPath`, `fromNode`, `toNode` |
429
453
 
@@ -433,7 +457,7 @@ Tag extraction is similarly case-tolerant: `tags`, `Tags`, `TAGS`, `tag`, and `T
433
457
  |------|-------------|----------------|
434
458
  | `list_bases` | Enumerate `.base` files in the vault | (none) |
435
459
  | `read_base` | Parse a Base file (filters, properties, views) | `path` |
436
- | `query_base` | Run a Base's filter DSL against the vault | `path`, `view`, `limit`, `includeFrontmatter` |
460
+ | `query_base` | Run a Base's filter DSL against the vault; unsupported filters warn and fail closed | `path`, `view`, `limit`, `includeFrontmatter` |
437
461
 
438
462
  ### Attachments
439
463
 
@@ -447,9 +471,9 @@ Tag extraction is similarly case-tolerant: `tags`, `Tags`, `TAGS`, `tag`, and `T
447
471
 
448
472
  | Tool | Description | Key Parameters |
449
473
  |------|-------------|----------------|
450
- | `index_vault` | Build / refresh the embedding index (incremental, progress events) | `force`, `folder` |
474
+ | `index_vault` | Build / refresh the embedding index (incremental, progress events) | `force`, `folder`, `confirm` |
451
475
  | `search_semantic` | Cosine search the embedding index for a natural-language query | `query`, `limit`, `folder`, `includeSnippet` |
452
- | `find_similar_notes` | Surface notes most similar to a source note (no live API call) | `path`, `limit` |
476
+ | `find_similar_notes` | Surface notes most similar to a source note, anchored to its opening topic (no live API call) | `path`, `limit` |
453
477
 
454
478
  ---
455
479
 
@@ -487,7 +511,7 @@ All tool paths must be **vault-relative** (e.g. `notes/hello.md`), never absolut
487
511
 
488
512
  ### HTTP Transport Returns `401 Unauthorized`
489
513
 
490
- The server was started with `--token=<secret>` (or `MCP_HTTP_TOKEN` is set in the environment) but the client isn't sending a matching `Authorization: Bearer <secret>` header. Verify the token value and that the header is present — comparison is case-sensitive and constant-time.
514
+ The server was started with `MCP_HTTP_TOKEN` but the client isn't sending a matching `Authorization: Bearer <secret>` header. Verify the token value and that the header is present — comparison is case-sensitive and constant-time.
491
515
 
492
516
  ### HTTP Transport Returns `429 Too Many Requests`
493
517
 
@@ -520,6 +544,9 @@ npm install
520
544
  # Build
521
545
  npm run build
522
546
 
547
+ # Run the full local maintenance gate
548
+ npm run verify
549
+
523
550
  # Run in development (watch mode)
524
551
  npm run dev
525
552
 
@@ -527,6 +554,10 @@ npm run dev
527
554
  OBSIDIAN_VAULT_PATH=/path/to/vault npm start
528
555
  ```
529
556
 
557
+ For the company-grade maintenance and R&D operating plan, see [docs/DEVELOPMENT_GOAL.md](./docs/DEVELOPMENT_GOAL.md).
558
+
559
+ GitHub Actions workflows are manual-only in this repository. Use `npm run verify` as the required local gate before merging or publishing.
560
+
530
561
  ### Project Structure
531
562
 
532
563
  ```
@@ -548,7 +579,7 @@ src/
548
579
  chunker.ts # Heading-aware chunking for embeddings
549
580
  embedding-providers.ts# Ollama + OpenAI providers
550
581
  embedding-store.ts # Persistent vector index, cosine search
551
- index-cache.ts # mtime-keyed content cache (in-memory + on-disk)
582
+ index-cache.ts # mtime-keyed content cache (in-memory)
552
583
  progress.ts # MCP progress-notification helper
553
584
  mime.ts # extension -> MIME map for attachments
554
585
  dates.ts # Moment-style date format for daily-note filenames
@@ -621,8 +652,8 @@ npm run lint:fix # auto-fix
621
652
  block (and don't expose subsequent content to wikilink rewriting).
622
653
  - **`/health` no longer leaks the live session count when a Bearer
623
654
  token is configured.** Status + version stay public for monitoring;
624
- `sessions` is dropped in authenticated deployments. Local-only
625
- setups still see it.
655
+ `sessions` is dropped in authenticated deployments. In 4.0.0 and
656
+ later, HTTP always starts with bearer auth, so the field stays hidden.
626
657
  - **`constantTimeEqual` is now fully length-safe** (pads both inputs
627
658
  to a fixed width before comparing), and the regex / parser hardening
628
659
  list also closed: `resolveWikilink` proximity tie-break for
@@ -665,11 +696,11 @@ npm run lint:fix # auto-fix
665
696
  - **Attachments.** `list_attachments`, `find_unused_attachments`, `get_attachment` (returns image / audio / blob bytes inline).
666
697
  - **Tag renames vault-wide.** `rename_tag` rewrites both inline `#tag` and frontmatter `tags:` (hierarchical mode rebases nested sub-tags).
667
698
  - **Folder-scoped permissions.** `OBSIDIAN_READ_PATHS` / `OBSIDIAN_WRITE_PATHS` allowlists.
668
- - **Persistent mtime cache.** Vault-wide scans (`get_tags`, `search_notes`, `search_by_tag`) hydrate from `<vault>/.obsidian/cache/mcp-pro-index-cache.json` after a restart and stat-pass against current state, only re-reading changed notes.
699
+ - **In-memory mtime cache.** Vault-wide scans (`get_tags`, `search_notes`, `search_by_tag`) reuse unchanged note content within the running process without persisting note bodies to disk.
669
700
  - **Quick wins.** `get_recent_notes`, `get_vault_stats`, `resolve_alias`.
670
701
  - **MCP prompts.** `daily-review`, `weekly-rollup`, `find-stale-notes`, `extract-action-items`, `build-moc`.
671
702
  - **Progress notifications** on `rename_tag`, `find_unused_attachments`, `index_vault`.
672
- - **Elicitation** on `delete_note(permanent: true)` for clients that support it.
703
+ - **Bulk-write confirmation latches** on `delete_note(permanent: true)`, `move_note` reference rewrites, and non-dry-run `rename_tag`, plus elicitation prompts for clients that support them.
673
704
  - **eslint** wired up with typescript-eslint flat config; `npm run lint` and `lint:fix`.
674
705
 
675
706
  **v1.7.0** — `delete_note` reference handling:
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAwK/D,wBAAgB,cAAc,IAAI,WAAW,CAsB5C;AAED,wBAAsB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAwCrF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAuL/D,wBAAgB,cAAc,IAAI,WAAW,CAsB5C;AAED,wBAAsB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAwDrF"}
package/build/config.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import * as fs from "fs";
2
- import * as fsp from "fs/promises";
3
2
  import * as path from "path";
4
3
  import * as os from "os";
5
4
  import { log } from "./lib/logger.js";
5
+ import { openVaultInternalFileForRead } from "./lib/vault.js";
6
+ const DAILY_NOTES_CONFIG_REL_PATH = ".obsidian/daily-notes.json";
7
+ const MAX_DAILY_NOTES_CONFIG_BYTES = 64 * 1024;
8
+ const MAX_DAILY_NOTES_CONFIG_FIELD_CHARS = 500;
6
9
  function getObsidianConfigPath() {
7
10
  const platform = os.platform();
8
11
  if (platform === "win32") {
@@ -35,6 +38,18 @@ function isObsidianConfig(value) {
35
38
  }
36
39
  return true;
37
40
  }
41
+ function isPlainObject(value) {
42
+ return value !== null && typeof value === "object" && !Array.isArray(value);
43
+ }
44
+ function boundedDailyConfigString(value) {
45
+ if (typeof value !== "string")
46
+ return undefined;
47
+ if (value.length > MAX_DAILY_NOTES_CONFIG_FIELD_CHARS)
48
+ return undefined;
49
+ if (value.includes("\0"))
50
+ return undefined;
51
+ return value;
52
+ }
38
53
  function isValidVaultPath(vaultPath) {
39
54
  try {
40
55
  const obsidianDir = path.join(vaultPath, ".obsidian");
@@ -54,9 +69,8 @@ function resolveVaultFromEnv() {
54
69
  }
55
70
  const resolved = path.resolve(envPath);
56
71
  if (!isValidVaultPath(resolved)) {
57
- // Log the configured path (not sanitized) because this is an operator-
58
- // facing diagnostic they already know the path, and hiding it would
59
- // make the error useless. This never goes into a tool response.
72
+ // The logger redacts the path before writing to stderr or forwarding to
73
+ // MCP clients, so this stays useful without leaking host layout.
60
74
  log.warn("OBSIDIAN_VAULT_PATH is not a valid vault (missing .obsidian dir)", {
61
75
  vaultPath: resolved,
62
76
  });
@@ -160,10 +174,26 @@ export async function getDailyNoteConfig(vaultPath) {
160
174
  format: "YYYY-MM-DD",
161
175
  };
162
176
  const resolvedVaultPath = vaultPath ?? getVaultConfig().vaultPath;
163
- const dailyNotesConfigPath = path.join(resolvedVaultPath, ".obsidian", "daily-notes.json");
177
+ let dailyNotesConfigPath = "";
164
178
  let raw;
165
179
  try {
166
- raw = await fsp.readFile(dailyNotesConfigPath, "utf-8");
180
+ const opened = await openVaultInternalFileForRead(resolvedVaultPath, DAILY_NOTES_CONFIG_REL_PATH);
181
+ dailyNotesConfigPath = opened.fullPath;
182
+ const stats = opened.stats;
183
+ if (stats.size > MAX_DAILY_NOTES_CONFIG_BYTES) {
184
+ await opened.handle.close();
185
+ log.warn("Daily notes config exceeds size cap; using defaults", {
186
+ bytes: stats.size,
187
+ max: MAX_DAILY_NOTES_CONFIG_BYTES,
188
+ });
189
+ return defaults;
190
+ }
191
+ try {
192
+ raw = await opened.handle.readFile("utf-8");
193
+ }
194
+ finally {
195
+ await opened.handle.close();
196
+ }
167
197
  }
168
198
  catch (err) {
169
199
  if (err.code === "ENOENT")
@@ -176,10 +206,15 @@ export async function getDailyNoteConfig(vaultPath) {
176
206
  }
177
207
  try {
178
208
  const parsed = JSON.parse(raw);
209
+ if (!isPlainObject(parsed))
210
+ return defaults;
211
+ const folder = boundedDailyConfigString(parsed.folder);
212
+ const format = boundedDailyConfigString(parsed.format);
213
+ const template = boundedDailyConfigString(parsed.template);
179
214
  return {
180
- folder: typeof parsed.folder === "string" ? parsed.folder : defaults.folder,
181
- format: typeof parsed.format === "string" ? parsed.format : defaults.format,
182
- template: typeof parsed.template === "string" ? parsed.template : undefined,
215
+ folder: folder ?? defaults.folder,
216
+ format: format ?? defaults.format,
217
+ template,
183
218
  };
184
219
  }
185
220
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  export interface HttpServerOptions {
3
3
  host: string;
4
4
  port: number;
5
- bearerToken?: string;
5
+ /** Required for HTTP transport. Use MCP_HTTP_TOKEN in the CLI path. */
6
+ bearerToken: string;
6
7
  buildMcpServer: () => McpServer;
7
8
  /** Install SIGINT/SIGTERM handlers + exit the process on shutdown. Default
8
9
  * `true` for CLI use. Set `false` when embedding (e.g. inside an Obsidian
@@ -13,7 +14,8 @@ export interface HttpServerOptions {
13
14
  /** Allowed CORS origins. Defaults to localhost-only patterns
14
15
  * (`["http://localhost:*", "http://127.0.0.1:*", "http://[::1]:*"]`).
15
16
  * Use an explicit list (e.g. `["https://claude.ai"]`) for browser-facing
16
- * deployments. Pass `["*"]` to allow all origins (a warning is logged).
17
+ * deployments. Pass `["*"]` to allow all origins only when Bearer auth is
18
+ * configured.
17
19
  * Requests from other origins still succeed (CORS is a browser-only
18
20
  * restriction) but the browser will reject the response. */
19
21
  allowedOrigins?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,SAAS,CAAC;IAChC;;wEAEoE;IACpE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;iEAK6D;IAC7D,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AA4LD,wBAAsB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA8UxF"}
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,SAAS,CAAC;IAChC;;wEAEoE;IACpE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;iEAM6D;IAC7D,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,sEAAsE;IACtE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAkND,wBAAsB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyVxF"}
@@ -21,29 +21,42 @@ class BodyTooLargeError extends Error {
21
21
  this.name = "BodyTooLargeError";
22
22
  }
23
23
  }
24
+ function declaredBodyTooLarge(req) {
25
+ const contentLength = req.headers["content-length"];
26
+ if (typeof contentLength !== "string" || !/^\d+$/.test(contentLength))
27
+ return false;
28
+ return BigInt(contentLength) > BigInt(MAX_BODY_BYTES);
29
+ }
24
30
  function readBody(req) {
25
31
  return new Promise((resolve, reject) => {
26
32
  let size = 0;
27
- let exceeded = false;
33
+ let settled = false;
28
34
  const chunks = [];
35
+ const fail = (err) => {
36
+ if (settled)
37
+ return;
38
+ settled = true;
39
+ chunks.length = 0;
40
+ reject(err);
41
+ };
29
42
  req.on("data", (chunk) => {
30
- if (exceeded)
43
+ if (settled)
31
44
  return;
32
45
  size += chunk.length;
33
46
  if (size > MAX_BODY_BYTES) {
34
- // Stop buffering but keep reading + discarding so the request stream
35
- // drains cleanly. Destroying the socket here races with the 413
36
- // response write and produces noisy `write after end` errors.
37
- exceeded = true;
38
- chunks.length = 0;
47
+ // Stop buffering and let the caller return 413 immediately; keep the
48
+ // request stream flowing so the socket can drain without a hard reset.
49
+ fail(new BodyTooLargeError());
50
+ req.resume();
39
51
  }
40
52
  else {
41
53
  chunks.push(chunk);
42
54
  }
43
55
  });
44
56
  req.on("end", () => {
45
- if (exceeded)
46
- return reject(new BodyTooLargeError());
57
+ if (settled)
58
+ return;
59
+ settled = true;
47
60
  const raw = Buffer.concat(chunks).toString("utf-8");
48
61
  if (!raw)
49
62
  return resolve(undefined);
@@ -54,7 +67,7 @@ function readBody(req) {
54
67
  reject(err instanceof Error ? err : new Error("Invalid JSON body"));
55
68
  }
56
69
  });
57
- req.on("error", reject);
70
+ req.on("error", (err) => fail(err));
58
71
  });
59
72
  }
60
73
  function setSecurityHeaders(req, res) {
@@ -73,6 +86,14 @@ function sendJson(res, status, body) {
73
86
  res.writeHead(status, { "Content-Type": "application/json" });
74
87
  res.end(JSON.stringify(body));
75
88
  }
89
+ function parseRequestUrl(req) {
90
+ try {
91
+ return new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ }
76
97
  // Constant-time string compare that does not leak the expected token's
77
98
  // length. Both inputs are padded to a fixed comparison width (the longer
78
99
  // of the two) before `timingSafeEqual`, so the compare time is the same
@@ -189,6 +210,12 @@ export async function startHttpServer(opts) {
189
210
  if (opts.bearerToken !== undefined && !bearerToken) {
190
211
  throw new Error("HTTP bearer token cannot be empty");
191
212
  }
213
+ if (!bearerToken) {
214
+ throw new Error("HTTP bearer token is required. Set MCP_HTTP_TOKEN in the CLI or pass bearerToken when embedding.");
215
+ }
216
+ const allowedOrigins = opts.allowedOrigins && opts.allowedOrigins.length > 0
217
+ ? opts.allowedOrigins
218
+ : ["http://localhost:*", "http://127.0.0.1:*", "http://[::1]:*"];
192
219
  const transports = new Map();
193
220
  const lastActivity = new Map();
194
221
  const touch = (sid) => { lastActivity.set(sid, Date.now()); };
@@ -198,9 +225,6 @@ export async function startHttpServer(opts) {
198
225
  // `initialize` builds a fresh server below; GC reclaims it once the
199
226
  // transport closes (Protocol._onclose clears the transport reference).
200
227
  // See https://github.com/rps321321/obsidian-mcp-pro/issues/8.
201
- const allowedOrigins = opts.allowedOrigins && opts.allowedOrigins.length > 0
202
- ? opts.allowedOrigins
203
- : ["http://localhost:*", "http://127.0.0.1:*", "http://[::1]:*"];
204
228
  if (allowedOrigins.includes("*")) {
205
229
  log.warn("CORS configured with wildcard origin '*'. Consider restricting to specific origins for production deployments.");
206
230
  }
@@ -254,6 +278,16 @@ export async function startHttpServer(opts) {
254
278
  }
255
279
  setCors(req, res, allowedOrigins);
256
280
  setSecurityHeaders(req, res);
281
+ const url = parseRequestUrl(req);
282
+ if (!url) {
283
+ log.warn("Rejected malformed HTTP request URL", {
284
+ method: req.method,
285
+ path: req.url ?? "",
286
+ ip: clientIp(req),
287
+ });
288
+ sendJson(res, 400, { error: "Malformed request URL" });
289
+ return;
290
+ }
257
291
  const requestOrigin = req.headers.origin;
258
292
  if (typeof requestOrigin === "string" && !originAllowed(requestOrigin, allowedOrigins)) {
259
293
  log.warn("Rejected request from disallowed Origin", {
@@ -275,7 +309,6 @@ export async function startHttpServer(opts) {
275
309
  // need to stay reachable for monitoring even under load.
276
310
  if (rateLimiter) {
277
311
  const ip = clientIp(req);
278
- const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
279
312
  const exempt = url.pathname === "/health" || url.pathname === "/version";
280
313
  if (!exempt && !rateLimiter.check(ip)) {
281
314
  res.setHeader("Retry-After", "60");
@@ -283,7 +316,6 @@ export async function startHttpServer(opts) {
283
316
  return;
284
317
  }
285
318
  }
286
- const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
287
319
  if (url.pathname !== "/mcp") {
288
320
  if (url.pathname === "/health") {
289
321
  // When a Bearer token is configured (production deployments),
@@ -294,8 +326,6 @@ export async function startHttpServer(opts) {
294
326
  status: "ok",
295
327
  version: opts.version ?? "",
296
328
  };
297
- if (!bearerToken)
298
- body.sessions = transports.size;
299
329
  sendJson(res, 200, body);
300
330
  return;
301
331
  }
@@ -352,6 +382,11 @@ export async function startHttpServer(opts) {
352
382
  sendJson(res, 415, { error: "Unsupported Media Type: expected application/json" });
353
383
  return;
354
384
  }
385
+ if (declaredBodyTooLarge(req)) {
386
+ req.resume();
387
+ sendJson(res, 413, { error: "Request body too large" });
388
+ return;
389
+ }
355
390
  let body;
356
391
  try {
357
392
  body = await readBody(req);
@@ -450,9 +485,6 @@ export async function startHttpServer(opts) {
450
485
  allowedOrigins: allowedOrigins.join(","),
451
486
  rateLimitPerMinute: opts.rateLimitPerMinute ?? 0,
452
487
  });
453
- if (!bearerToken) {
454
- log.warn("WARNING: HTTP server starting without authentication. Set MCP_HTTP_TOKEN to enable bearer token auth.");
455
- }
456
488
  const installSignals = opts.installSignalHandlers ?? true;
457
489
  const onSignal = () => {
458
490
  void stop().finally(() => {