Models the combined (protocol × bind × cert) state as a 9-cell table
with three verdicts (🟢 OK / 🟡 WARN / 🔴 JAIL). Server always starts;
🔴 surfaces a Notice + log + MCP `initialize` instructions warning so
the LLM client also sees it. New settings: `bindMode` discriminated
enum + `customBindHost`. Migration flips the implicit `0.0.0.0` to a
real `127.0.0.1` default, making ADR-103's "loopback by default"
premise actually true. Inspired by community PR #208 (fa1k3) —
preserves the security instinct while keeping the LAN/remote escape
hatch the hardcoded approach would have removed.
Refined through design dialogue:
- paginate/no-paginate boundary is a CHARACTER budget (READ_PAGE_CHARS,
default 50000), not a line count (line count is an invalid proxy for
context cost — 1500 short vs 1500 long lines differ by orders of magnitude)
- bookends stay line-based (lineStart/lineEnd/totalLines) so edit.at_line
on a large file still works; a page = longest whole-line run within the
char budget
- fits budget → whole file verbatim, one load (common case, no ceremony)
- exceeds → bookended page 1 (single contiguous block, not chunk array) +
nextPage hint; sequential paging via new `page` param
- returnFullFile:true RETAINED and repurposed as the explicit
whole-large-file override (not retired)
- hard invariant recorded: a default read must never hand the agent a
context-breaking raw dump
Alternatives added: no-guard full dump, line-count budget, chunk-array — all rejected with rationale.
Synthesizes #133's intent (machine-usable default for a machine protocol)
with the project instinct (concise formatted tool results, but unaltered
content for editing). Empirically grounded: default vault.read is lossy
(fragments + flattens newlines → broken read→edit round-trip), the raw
envelope double-encodes the body (~2x), and returnFullFile-without-raw
crashes the formatter.
Decision (two-bucket contract): content reads faithful/verbatim by default
(fragmentation = explicit opt-in via existing strategy/maxFragments);
action/result tools stay concise-formatted by default; stop double-encoding
the body; verbatim path must not route through the broken formatter.
#133's global-raw-flip remedy explicitly declined; its intent satisfied by
bucket 1. Status: Proposed (design of record; implementation tracked
separately, not in this ADR).
router.ts was a 2,228-line monolith (~2.8x the 800-line threshold). Extract
the largest unit — executeVaultOperation + its helpers — into
src/semantic/operations/vault.ts behind a RouterContext interface.
- operations/router-context.ts: RouterContext (api/app/fragmentRetriever/
validator); SemanticRouter implements it and passes itself, so shared
state propagates with no indirection
- operations/shared.ts: Params, SearchResultItem, paramStr/Num/Bool
(router-private → shared by all handlers)
- operations/vault.ts: executeVaultOperation + splitContent/sortFiles/
copyFile/copyDirectoryRecursive; mechanical this.→ctx. (tsc is the
safety net — a missed rewrite fails compile)
- router fields api/app/fragmentRetriever/validator → public readonly
- removed a fully unreachable pre-existing dead subtree (combineSearchResults,
isDirectory, performFileBasedSearch, indexVaultFiles, getSearchWorkflowHints,
extractContext, getFileType — zero call sites anywhere; eslint doesn't flag
unused class methods, which hid them). ~290 dead lines gone.
router.ts 2228→988; vault.ts 948. Behaviour-preserving: tsc clean, lint at
the exact pre-existing baseline (0 errors, 5 unrelated warnings), 235/235
tests unchanged.
Staged per the issue's "extract vault first in isolation" guidance: #199
stays open for the remaining handlers (edit/view/graph/system/bases →
router thin dispatcher <300 lines). ADR-202.
Stale/evicted MCP sessions were unrecoverable without a client restart
(#128): a non-initialize request bearing an evicted Mcp-Session-Id hit a
server-side synthetic `initialize` that cannot drive SDK 1.29's
web-standard transport to an initialized state (proven in #190 — non-stream
compatReq, hono RequestError→400, post-hoc accept header lost→406), "failed
open", and returned a non-spec 400 -32000 "Server not initialized" that no
client treats as session-expiry → infinite 400 loop.
Emit the Streamable HTTP spec's session-lifecycle signal and let the client
re-initialize itself:
- evicted session + non-initialize → HTTP 404 + Mcp-Session-Id (spec §3);
compliant client/bridge re-inits per §4, next request opens a fresh
session — no restart (fixes#128)
- no session id + non-initialize → HTTP 400 (spec §2)
- initialize (fresh or recreate) → unchanged, never short-circuited
Delete the synthetic compat-initialize block, requireInitializeNotice
machinery, and the createNullRes/NullResponse shim. New
sendSessionTerminated() helper centralises the spec response.
ADR-106 records the decision, the restated acceptance (404-signal +
2xx-reinit pair), and the known client caveat (spec §4 is the client's
obligation; mcp-remote reaches it via connectToRemoteServer recursion).
Regression harness tests/mcp-session-reinit.test.ts drives the request
pair through handleMCPRequest deterministically (paths return before any
SDK transport — the thing #190 showed cannot be driven synthetically).
ADR numbered 106 (not 105) to avoid collision with the #197 branch's
ADR-105; contiguous once both merge. make check green (build + lint 0
errors + 229/229 tests).
- eslint.config.mts: remove now-deleted build-worker.js from ignores (review nit)
- ADR-105: correct overreaching claim — the ConnectionPool pipeline itself
was also dormant (submitRequest never called; live path is MCPServerPool →
SDK setRequestHandler → tool.handler). Scope explicitly limited to the
worker-offload path; unused pool scaffolding left to a separate decision.
The worker-thread pool ratified/extended by ADR-104 has never executed a
single request since introduction (v0.5.8b, 22742bd): ConnectionPool's
action-level workerOps gate (tool.vault.search, …) never matched the
operation-level method strings the pool emits (tool.vault, tool.edit, …),
so processWithWorker was unreachable. Independently, the worker built to
dist/workers/workers/semantic-worker.js while WorkerManager loaded
dist/workers/semantic-worker.js — load would have failed anyway.
Verified dead against current main; no CPU-starvation field reports.
Fixing forward was rejected: it would ship an untested concurrency path
that widens the #139 parallel-edit race via a new TOCTOU window for an
unproven perf need (see #197 analysis).
- delete src/workers/semantic-worker.ts, src/utils/worker-manager.ts,
build-worker.js; drop build:worker + the build-worker.js build step
- strip worker wiring from ConnectionPool (retained as a main-thread
bounded queue — the only behaviour that ever ran) and the dead
prepareWorkerContext/workerScript from mcp-server.ts
- ADR-105 records the removal as a partial reversal of ADR-104; ADR-104
stays Accepted (SSE-route deconfliction half, shipped #196, still
governs) with a banner pointing to ADR-105
Behaviour-preserving by construction: the deleted path never ran.
make check green (build + lint 0 errors + 225/225 tests).
`claude mcp add --header "Authorization: Bearer <token>"` defeats
secret-at-rest protection: the CLI resolves and echoes the header value to
stdout (captured by any parent process, incl. AI agents) and on macOS the
spawned MCP child argv is written to the unified log. Editing the MCP config
file directly avoids both vectors.
- Settings UI: new shared `renderClaudeCodeConnection()` renders a
ready-to-paste JSON config + a security warning for the authenticated
path, and the safe plain CLI command only when auth is disabled. Used by
both the initial render and the live-refresh handler so they cannot drift
(the previous two copies had already diverged).
- README / SECURITY.md / troubleshooting.md / issue-32 response template:
drop `mcp-remote` + `NODE_TLS_REJECT_UNAUTHORIZED=0`, document native HTTP
transport and proper self-signed-cert trust (macOS Keychain +
NODE_EXTRA_CA_CERTS for Bun-based runtimes), add the `--header` warning.
- styles.css: `.mcp-security-warning`.
Reimplemented from PR #143 against current `main` (the settings UI had been
restructured since the PR was opened); supersedes and closes out #143.
ADR-100 left untouched — accepted ADRs are immutable records; the `--header`
deprecation is recorded in ADR-104's context instead.
Co-authored-by: Earl Plak <5597016+laplaque@users.noreply.github.com>
Ratifies the existing (undocumented, v0.5.8b) worker-thread pool as the
sanctioned execution model for CPU-bound semantic operations, and records
the decision to extend it to edit.window fuzzy matching plus deconflict the
GET /mcp SSE route. This ADR is the architectural gate for PR #126 (#125).
Status: Draft — pending decider review.
Refs: #125, #126
Records the decision to keep the per-instance, non-synced, auto-generated
self-signed cert/key and NOT move the private key into the synced vault,
and to dissolve the recurring "store the key in the vault?" question by
documenting the HTTPS scope (opt-in, loopback-only, asserts no identity;
zero-config already delivered by the HTTP-on-localhost default).
Rationale: localhost self-signed cert is low-sensitivity; the API key
(#135) is the real secret and already syncs; vault-shared key is a
defense-in-depth regression for shared/team vaults for marginal gain.
ADR only — no code change; certificate-manager.ts behaviour unchanged.
Independent of the accepted "Direct Filesystem Access" finding.
Refs #7
Closes#175. Records the decision to replace the arbitrary-JS new Function
in expression-evaluator.ts with a vetted no-eval expression library — driven
by the security reality (.base files are syncable/shareable, so the current
path is arbitrary code execution from untrusted files in a live plugin), not
the (non-gating) scanner finding.
Hard constraints recorded: no hand-rolled parser (its own risk class), and a
mandatory differential test corpus (new == old). Implementation tracked in
#180; rejected alternatives (accept-and-document, hand-rolled parser, heavy
sandbox) documented with rationale.
Obsidian moved community-plugin submission from the obsidian-releases PR
process to the community.obsidian.md developer portal. Replace the now-defunct
'I'm Not Dead Yet' PR-refresh procedure in CLAUDE.md with the portal-based
distribution reality (stable Latest release matching manifest, no v-prefix,
versions.json, make promote).
ADR-300 records the decision to keep a single main branch with the existing
prerelease/promote flow rather than add a dedicated Obsidian release branch:
for 'same code, slower cadence' the prerelease/promote gap already provides
the decoupling, and a release branch would only add permanent version-file
divergence. Rejected alternatives documented.
Three drift points caught in review:
- §4 (CI publishing): drop the promised "second workflow publishes
latest .mcpb on mcpb/ change" — it isn't implemented and isn't
needed. Release-time publishing is the actual flow. Also documents
the versioned + unversioned dual-emit and the stable-URL motivation.
- §2 (server.js behavior): the implementation surfaces JSON-RPC
-32000 on session expiry and lets the client reinitialize rather
than attempting transparent replay. ADR previously claimed replay;
text now matches code with the rationale (replay would require
re-issuing state-establishing notifications and that path is
fragile).
- Consequences: acknowledge that the maker script partially exposes
the bridge to advanced users, which slightly weakens the
"bridge-is-invisible" framing of ADR-100/102. The audience that
reads server.js is the same audience that would have written
custom mcpServers JSON anyway, so the abstraction holds for the
cohort that benefits from it.
Refs PR #155 review.
Records the decision to ship an MCPB bundle for one-click Claude
Desktop install, with the existing Claude Code CLI string and JSON
mcpServers block remaining as the second and third onboarding paths.
Key constraints documented:
- MCPB has no native remote-HTTP server type today, so the bundle
exposes a small stdio<->HTTP bridge that Claude Desktop's bundled
Node runtime executes.
- Bridge is hand-rolled (zero deps) rather than vendoring mcp-remote.
- Bundle is static and single-vault; advanced users get a maker
script (planned) to generate custom-named bundles for multi-vault
setups. Other MCP clients (Cline, Continue, etc.) continue using
the JSON path.
Reconciles with ADR-100 by clarifying that the deprecation of
mcp-remote applied to user-facing config, not to invisible bridges
inside an MCPB.
Users can now control which MCP tools are exposed to connecting agents
via a tri-state tree UI in the plugin settings panel. Disabled tools
are excluded from the MCP tools/list enumeration — agents cannot
discover or call them. Includes defense-in-depth runtime check.
Also adds ADR-101 (tool visibility gating) and ADR-200 (CLI parity gaps).
Always run in pooled/concurrent mode — the dual code path (single
MCPServer vs MCPServerPool) existed for backward compatibility with
clients that couldn't handle HTTP transport. All modern MCP clients
now support Streamable HTTP natively.
Changes:
- Remove enableConcurrentSessions toggle and maxConcurrentConnections
setting from interfaces, defaults, and settings UI
- Remove single-server code path (setupMCPHandlers, non-concurrent
request handling branch) — ~200 lines of dead code
- Simplify connection templates from 4 options to 2:
Claude Code command + standard JSON with Authorization header
- Drop mcp-remote and Windows workaround templates
- Fix auth format: use standard Authorization header instead of
URL-embedded credentials
- Remove unused imports (schema types, DataviewTool, FileSystemAdapter)
- Update README and troubleshooting docs
- Accept ADR-100
Net: -433 lines
Scaffolds Architecture Decision Records for the project with three
domains: core (100-199), tools (200-299), delivery (300-399).
ADR-100 proposes removing the concurrent mode toggle, dropping the
mcp-remote connection option, and simplifying to two config templates
(Claude Code command + standard JSON with header auth).
ADR-001: Adopting ADR format
- Documents decision to use Architecture Decision Records
- Acknowledges retroactive adoption after organic development
ADR-002: Search Facade Architecture (Proposed)
- Unified search interface composing multiple strategies
- Routes operator queries to fast search, natural language to ranked search
- Addresses Issue #63 search implementation gaps
- Aligns with "less tools, more details" principle
Fixes#31 - Documents the Node.js "Program Files" path issue on Windows
where Claude Desktop fails to spawn npx due to unquoted paths with spaces.
Also includes common troubleshooting sections for:
- Connection issues
- Authentication errors
- SSL certificate problems
- Server startup failures
- Dataview/Bases integration
- Performance optimization
- Replace complex technical README with cleaner, simpler version
- Focus on semantic agency and graph navigation as core value
- Move detailed documentation to docs/ subdirectory
- Create tool-specific documentation for vault and graph operations
- Emphasize why semantic MCP matters for AI knowledge access
- Switched from JSON to YAML format for .base files
- Implemented expression-based filter parsing (e.g., status == 'active')
- Added property prefix support (note.*, file.*, formula.*)
- Created ExpressionEvaluator for filter expressions
- Created FormulaEngine for formula calculations
- Fixed metadata cache usage for frontmatter properties
- Removed old object-based filter system
- Updated semantic router and tool descriptions
- Added comprehensive documentation
- Bump version to 0.9.5
- Added complete type definitions for Bases functionality
- Implemented BasesAPI class with full CRUD operations
- Integrated Bases into ObsidianAPI and semantic router
- Added 10 new MCP operations for Bases management
- Supports queries, views, templates, and CSV/JSON/Markdown export
- Graceful degradation when Bases plugin not available
- Bump version to 0.9.4
- Add CONTRIBUTING.md with development guidelines
- Add SECURITY.md with vulnerability reporting policy
- Create GitHub issue templates for bugs and features
- Add PR template with checklist
- Set up GitHub Actions for testing and security scanning
- Document all security findings from code audit
- Create detailed GitHub issues for all vulnerabilities
- Update CHANGELOG with security audit results
- Add project structure documentation
This establishes proper open-source project structure and documents
all critical security issues that need to be addressed.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Implement search-based graph traversal that applies queries at each node
- Return chains of relevant snippets along traversal paths
- Support score thresholds to filter low-relevance nodes
- Add advanced traversal with multiple strategies (breadth-first, best-first, beam-search)
- Integrate with existing MCP graph operations
- Add comprehensive test coverage for circular references and edge cases
This enables AI agents to explore knowledge graphs by following high-scoring semantic paths, returning contextual snippet chains in a single API call.