Commit graph

34 commits

Author SHA1 Message Date
Aaron Bockelie
5884ac492d docs(adr): accept ADR-107 — network exposure as classified state machine
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.
2026-05-24 16:44:35 -05:00
Aaron Bockelie
150cf5c071 docs(adr): amend ADR-203 — char-budget pagination with line bookends
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.
2026-05-19 00:24:56 -05:00
Aaron Bockelie
d4c005f1a6 docs(adr): accept ADR-203 (reviewed & approved) 2026-05-19 00:16:11 -05:00
Aaron Bockelie
2be1998e12 docs(adr): ADR-203 — faithful-by-default content reads; concise tool results (#133)
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).
2026-05-19 00:14:29 -05:00
Aaron Bockelie
c2d6f8b325 refactor(router): extract vault operation into operations/ module (ADR-202, #199 stage 1)
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.
2026-05-18 23:07:40 -05:00
Aaron Bockelie
9b348015d0 Merge main into fix/190; harden sessionManager type (PR #201 review)
- INDEX.md regenerated authoritatively (ADR-105 from #197 + ADR-106 here)
- mcp-server.ts auto-merged cleanly (#197 worker removal + #190 session
  re-init touched disjoint regions)
- sessionManager: drop optional '?' — assigned unconditionally in ctor;
  makes the ADR-106 'initialize never short-circuited' invariant
  type-guaranteed (PR #201 review nit)

make check green (229/229).
2026-05-18 22:48:26 -05:00
Aaron Bockelie
ddb13508a1 fix(mcp): client-driven session re-init via spec 404 (ADR-106, closes #190)
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).
2026-05-18 22:37:23 -05:00
Aaron Bockelie
cce503edbf chore(worker): address PR #200 review — drop dead eslint ignore, tighten ADR-105 scope note
- 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.
2026-05-18 22:31:47 -05:00
Aaron Bockelie
bf1aa6319a chore(worker): remove dormant worker-offload path (ADR-105, closes #197)
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).
2026-05-18 22:26:14 -05:00
Aaron Bockelie
75b29be6e1
Merge pull request #192 from aaronsb/security/safe-mcp-config-docs
security(docs): safe MCP config approach (reimplements #143)
2026-05-18 16:27:51 -05:00
Aaron Bockelie
e92173fa10 docs(adr): accept ADR-104 (decider review — gate for #126 cleared) 2026-05-18 16:27:38 -05:00
Aaron Bockelie
0bd8020804 security(docs): replace claude mcp add --header with safe config-file approach
`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>
2026-05-18 08:10:00 -05:00
Aaron Bockelie
8f882e6bfe docs(adr): ADR-104 — worker-thread offload for CPU-bound ops + SSE route deconfliction
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
2026-05-18 07:59:24 -05:00
Aaron Bockelie
92bd69013f docs(adr): ADR-103 — TLS cert/key storage strategy for localhost HTTPS (#7)
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
2026-05-16 23:34:39 -05:00
Aaron Bockelie
638ae4bedb docs(adr): ADR-201 replace new Function in Bases evaluator with sandboxed lib
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.
2026-05-16 13:58:18 -05:00
Aaron Bockelie
3c19cf5d2f docs: record single-branch release strategy (ADR-300), retire defunct PR submission guidance
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.
2026-05-16 11:39:27 -05:00
Aaron Bockelie
b451331989 docs(adr-102): Align with implementation
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.
2026-05-15 11:46:19 -05:00
Aaron Bockelie
5d6e9a4c41 docs: Add ADR-102 (MCPB bundle as primary Claude Desktop onboarding)
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.
2026-05-15 11:28:12 -05:00
Aaron Bockelie
26d82bac19 feat: Add tree-based MCP tool visibility gating (ADR-101)
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).
2026-03-26 11:08:27 -05:00
Aaron Bockelie
ae03b2e5f9 refactor: Remove concurrent mode toggle, simplify connection setup (ADR-100)
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
2026-03-14 23:13:35 -05:00
Aaron Bockelie
04715c1dfd chore: Add ADR tooling and ADR-100 (simplify connection setup)
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).
2026-03-14 22:53:46 -05:00
Aaron Bockelie
8eaea5ea30 docs: Add ADR-003 for presentation facade pattern 2025-12-15 00:09:06 -06:00
Aaron Bockelie
757f7d58ef docs: Add ADR-001 and ADR-002 for search facade architecture
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
2025-12-14 23:30:16 -06:00
Aaron Bockelie
2572e97b37 docs: Add n8n integration troubleshooting section 2025-12-14 23:23:01 -06:00
Aaron Bockelie
f5d0662e44
docs: Add troubleshooting guide with Windows path workaround (#66)
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
2025-12-14 23:21:50 -06:00
Aaron Bockelie
7fedcd9bb6 docs: Simplify README with semantic value proposition focus
- 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
2025-08-31 00:54:07 -05:00
Aaron Bockelie
e9d156ae68
fix: Improve metadata cache integration for Bases frontmatter parsing
- Add fallback frontmatter parsing when metadata cache is empty
- Parse YAML frontmatter directly from file content when needed
- Add debug logging to understand cache behavior
- Fix Debug.isDebugMode() method calls
2025-08-19 23:04:06 -05:00
Aaron Bockelie
2e5ddae672
refactor: Reimplement Bases support with proper YAML and expression parsing
- 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
2025-08-19 22:48:51 -05:00
Aaron Bockelie
73e8608a23
feat: Add Obsidian Bases support to MCP server
- 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
2025-08-19 22:21:09 -05:00
Aaron Bockelie
0dbb19c8fd
docs: Update plugin settings screenshot to show .mcpignore feature
- Shows new Path Exclusions toggle
- Shows .mcpignore File Management section
- Displays current exclusions status (1 patterns active)
- Includes context menu toggle
- Updated to reflect v0.7.3 interface
2025-07-21 17:19:17 -05:00
Aaron Bockelie
c2ba3840bf feat: Implement comprehensive security architecture
- Add 7-layer OWASP-compliant path validation system
- Implement granular CRUD + special operation permissions
- Add security audit logging with debug mode integration
- Create security presets (readOnly, safeMode, fullAccess)
- Add sandbox mode for restricted directory access
- Implement path allow/block lists with wildcard support
- Add comprehensive test coverage (39 security tests)
- Fix TypeScript compilation issues in SecureObsidianAPI

Fixes #10 (path traversal vulnerability)
Addresses #15 (operation permissions)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-16 09:16:24 -05:00
Aaron Bockelie
36301118a9 docs: Add comprehensive project documentation and security audit
- 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>
2025-07-12 00:14:45 -05:00
Aaron Bockelie
ee35f87b6f docs: Add plugin UI screenshot file 2025-07-08 16:19:14 -05:00
Aaron Bockelie
7f9ab6edcb feat: Add graph search traversal tool for semantic navigation
- 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.
2025-06-29 18:20:44 -05:00