From 272c003f736b8eb5c2d1123805e950510b9bf983 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 14:42:39 +0000 Subject: [PATCH 1/3] Stop sending every session to the superseded plan CLAUDE.md told anyone designing here to first read `docs/plan.md` in the admin repo. That file became `docs/archive/plan.md` on 2026-08-07, and its index entry now says "do not plan from it": 31KB of CouchDB, git remotes and per-workspace vault storage, none of which is the plan. The instruction kept resolving, kept being followed, and kept handing out the architecture we replaced. So this file describes this package and nothing else. No pointer into the private repo, no hosted roadmap, no CouchDB in principle 1. Two rules take their place: do not design this package around the hosted layer, and do not put anything private in a repo meant to go public at v0.1.0. README loses the same stale sentence -- there are no git remotes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01N7TgsCgsXW9g75gGDpYCy8 --- CLAUDE.md | 46 +++++++++++++++++++++++++++------------------- README.md | 6 +++--- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 93383aa..3ee8af8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,31 +13,39 @@ forbids a name that suggests a first-party product, and "Obsidian Pro" reads exactly like a paid tier of Obsidian itself. Say "Knap, for Obsidian" -- the vault app is what we work on, not what we are called. -This is the **public** package. The hosted, multi-tenant SaaS (admin panel, -Zitadel login, PostgreSQL, per-workspace vault storage, git remotes, Stripe, -PostHog, Hetzner deploy) will live in the private repo -`pantalytics/knap-mcp-admin`, which imports this package as a tag-pinned -dependency. Same open-core split as `odoo-mcp-pro` / `odoo-mcp-pro-admin` and -`squirrel-mcp` / `squirrel-mcp-admin`. This is the third product on those -patterns and it reuses them deliberately -- read `docs/plan.md` in the admin -repo before designing anything new here, because most of it is already decided -by those two. - -**Phase 1 is built and green**: the `VaultProvider` protocol, the filesystem -backend, all nineteen `vault_*` tools, stdio and streamable-http transports, 309 -tests, an MCP handshake smoke test that drives the server as a subprocess, and a -container that CI builds and then proves serves a mounted vault. -The phase list and what is still ahead (the hosted layer, CouchDB sync, -metering) live with the hosted layer, in `knap-mcp-admin/docs/plan.md`. +**This file describes this package and nothing else.** `knap-mcp` is the public +half of an open-core split, the same one as `odoo-mcp-pro` / +`odoo-mcp-pro-admin` and `squirrel-mcp` / `squirrel-mcp-admin`. A private +package imports this one as a tag-pinned dependency and adds the hosted, +multi-tenant service on top of it, through the seams listed below and no others. + +Two consequences, and they are the whole reason this section exists: + +- **Do not design this package around the hosted layer.** Its architecture is + not described here, it changes on its own schedule, and a decision taken there + is not a decision here. If a change needs something from it, that is a change + to the seam contract below, and it gets agreed on both sides before it is + built. +- **Do not put anything private in this repo.** No customer names, no + infrastructure hostnames, no roadmap for the hosted service. This package is + meant to be public at v0.1.0 and everything in it should already read as if it + were. + +**It is built and green**: the `VaultProvider` protocol, the filesystem backend, +all nineteen `vault_*` tools, stdio and streamable-http transports, 309 tests, +an MCP handshake smoke test that drives the server as a subprocess, and a +container that CI builds and then proves serves a mounted vault. What is left +here is maintenance and the occasional tool. The build ahead is in the private +package. ## Design principles 1. **The vault is the boss.** Notes are files. We do not own a database of content, we do not cache a copy, and Obsidian remains free to edit every byte under us. The server is a stateless view over a directory. Plain markdown on - disk stays the source of truth even in the hosted deployment, where CouchDB - and git are transports projecting onto it and not stores in their own right -- - that invariant is what keeps search cheap and the phone possible. + disk stays the source of truth wherever this runs: anything that syncs a + vault is a transport projecting onto those files, never a store in its own + right. That invariant is what keeps search cheap and the phone possible. 2. **Swappable backends.** Tools only ever touch the `VaultProvider` protocol, never a concrete filesystem call. The filesystem backend satisfies it today; a git-object or object-storage backend can satisfy it later without the tools diff --git a/README.md b/README.md index 3b3fdd0..2ffa0cf 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ create, append and patch do not. ## Open core This is the public package: one vault, from environment variables, over stdio or -HTTP. The hosted multi-tenant service -- vaults on Hetzner, Zitadel login, git -remotes, per-workspace isolation, usage diagnostics -- lives in the private -`knap-mcp-admin` repo and extends this one through documented seams only. +HTTP. A private package adds the hosted, multi-tenant service on top of it, and +extends this one through documented seams only. It never forks this code, and +nothing here depends on it. Same split as [odoo-mcp-pro](https://github.com/pantalytics/odoo-mcp-pro) and [squirrel-mcp](https://github.com/pantalytics/squirrel-mcp). From 57382f98393c8a7f540a26579fab88965d2a05c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 14:47:19 +0000 Subject: [PATCH 2/3] Property search is the retrieval mechanism, and the repo never said so OKF appears nowhere in this package. Not in CLAUDE.md, not in knowledge.py, not in a docstring. Meanwhile the vault this is built for keeps an OKF frontmatter discipline precisely so an AI can ask for the notes whose `type` is `meeting` instead of grepping for the word, and property search is how that question gets asked. The only thing the docs said about property search was that it exists because we do not evaluate Dataview. True, and it reads as a consolation prize, which is how a load-bearing surface gets treated as a corner of the API by someone deciding what to optimise. Named now, with what it is: an open Google Cloud spec, v0.1 dated 2026-06-12, one hard rule -- parseable frontmatter with a non-empty `type` -- and five optional keys worth being good at. We do not implement it and we do not validate it. What we owe it is that querying those keys is fast, paginated and honest about what it missed. Per ADR-0018 the bullet says where those facts came from, because they came from the announcement rather than the normative spec. The first draft of this commit cited a github.com URL that does not exist. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01N7TgsCgsXW9g75gGDpYCy8 --- CLAUDE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 3ee8af8..ace6e2d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,6 +98,26 @@ package. needs its plugin. `vault_search` offers frontmatter properties instead, which covers most of what people ask Dataview for, and `knowledge.py` says so at the handshake rather than letting a client invent a query it cannot run. +- **Frontmatter property search is the retrieval mechanism, not a consolation + prize.** The Dataview bullet above says why property search exists instead of + a query language. This says why it is worth more than "instead" makes it + sound: a vault that keeps a structured frontmatter discipline is one an AI can + ask precise questions of. Retrieve the notes whose `type` is `meeting`, rather + than grepping for the word "meeting" and hoping. + [OKF, the Open Knowledge Format](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing), + is that discipline written down: an open Google Cloud spec, v0.1 dated + 2026-06-12, whose one hard rule is that every note carries parseable + frontmatter with a non-empty `type`, plus optional `title`, `description`, + `resource`, `tags` and `timestamp`. Those six keys are the ones worth being + good at. (Read off the spec announcement on 2026-08-07, not from the + normative document; if a detail matters, check the spec.) + + So treat `property` and `property_value` on `vault_search`, and the property + fields on `vault_read`, as load-bearing surface rather than a corner of the + API. Free-text search is the fallback, not the plan. We do not implement OKF + and we do not validate it: a vault either keeps the discipline or it does not, + and the tools work either way. What we owe it is that querying those keys is + fast, paginated and honest about what it did not match. - Blocking filesystem calls run off the event loop via `tools/_common.run_blocking` (per-provider `asyncio.Lock`). - Single-tenant: one vault from env vars (stdio or HTTP). The hosted From b166ffdff695b1d717a1f241d4419f9680104f7c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 15:13:05 +0000 Subject: [PATCH 3/3] "We do not cache a copy" was too strong, and contradicted the other half Design principle 1 said the vault is the boss and, as evidence, that we keep no copy. True of this package. False as a universal, because a VaultProvider backed by a remote store has to keep a local one, and the private package's whole reading path is exactly that. Two documents, both read as ground truth, disagreeing on whether caching is allowed. Rewritten so the rule says what it actually protects: this package owns no database of content and holds no copy of its own, and what it forbids is becoming the system of record -- a store of note content Obsidian gets no vote in. A backend that keeps a local copy of a remote store still honours it, as long as plain markdown on disk is what the tools read. The invariant is unchanged. Only the evidence offered for it was wrong. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01N7TgsCgsXW9g75gGDpYCy8 --- CLAUDE.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ace6e2d..ec49e3e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,12 +40,18 @@ package. ## Design principles -1. **The vault is the boss.** Notes are files. We do not own a database of - content, we do not cache a copy, and Obsidian remains free to edit every byte - under us. The server is a stateless view over a directory. Plain markdown on - disk stays the source of truth wherever this runs: anything that syncs a - vault is a transport projecting onto those files, never a store in its own - right. That invariant is what keeps search cheap and the phone possible. +1. **The vault is the boss.** Notes are files. Obsidian remains free to edit + every byte under us, so this package owns no database of content and keeps no + copy of its own: the server is a stateless view over a directory, and + whatever is on disk when a call arrives is the answer. + + Read that as a rule about **this package**, not a ban on caching anywhere. + A `VaultProvider` is free to be backed by something that maintains a local + copy of a remote store, and one that does is still honouring this principle + so long as plain markdown on disk is what the tools read and the remote is + what the world agrees on. What the rule forbids is *us* becoming the system + of record: a store of note content that Obsidian does not get a vote in. + That invariant is what keeps search cheap and the phone possible. 2. **Swappable backends.** Tools only ever touch the `VaultProvider` protocol, never a concrete filesystem call. The filesystem backend satisfies it today; a git-object or object-storage backend can satisfy it later without the tools