Skip to content

Commit c99bc1d

Browse files
kelsi-bizerclaude
andauthored
Add sources/wiki split, truth-timeline page structure, and MCP server (#14)
Redo of closed PR #11 with BizerBrain naming throughout. Brings two cross-cutting patterns into BizerBrain that have been proven elsewhere (Karpathy's LLM Wiki pattern, GBrain's compiled-truth structure) and adds a Model Context Protocol server so the brain works with any MCP-capable agent. 1. Folder convention: sources/, pages/, daily/ The SKILL.md now teaches three distinct folders: - sources/ raw, immutable inputs (transcripts, articles, email dumps). Agent treats as append-only. - pages/ the wiki: synthesized topical/per-entity notes the agent owns and maintains. - daily/ the user's journal, mutable by the human. A new "Ingesting a source" procedure files raw input under sources/ and links it back to pages/. The boundary: sources/ is the receipts, pages/ is the synthesis, daily/ is the human's voice. 2. Page structure: compiled truth + timeline Every pages/<Entity>.md now has a stable, rewritable "compiled truth" section above a "---" separator and an append-only "## Timeline" below, with dated entries that back-link to [[daily/...]] or [[sources/...]]. The truth section is overwritten when facts change; timeline entries are never edited or removed. Pitfalls added: never rewrite sources/, never delete timeline entries, never edit existing timeline entries. 3. MCP server (packages/mcp-server) New package exposing the same four tools (list_notes, search_notes, read_note, write_note) over the Model Context Protocol via stdio. Agents that speak MCP natively (Claude Code, Cursor, any MCP client) can use BizerBrain by adding one line to their MCP config: { "mcpServers": { "bizerbrain": { "command": "node", "args": ["/opt/bizerbrain/packages/mcp-server/src/server.js"], "env": { "BRAIN_DIR": "/srv/bizerbrain/brain" } } } } Implementation: - src/brain.js filesystem ops with the same safety contract as brain_tools.py: path traversal blocked, atomic writes (tmp + rename), consistent {ok, ...} shape. - src/server.js MCP Server wired to StdioServerTransport. Tool schemas include the truth-timeline guidance in the write_note description so MCP-only agents (which don't load SKILL.md) get the same convention. Tests (17 passing, node:test): - brain.test.js round-trip, ensureMd, traversal blocked, null byte rejected, search snippet correctness, list sorted. - server.test.js list_tools exposes all four with schemas, call_tool round-trips, unknown tool name handled, traversal rejected at the protocol layer. No code changes outside .agents/skills/bizerbrain/SKILL.md and packages/mcp-server/. The web UI (packages/notes-app), file-api (packages/file-api), and docker image are unaffected. Co-authored-by: Claude <noreply@anthropic.com>
1 parent 92e8050 commit c99bc1d

8 files changed

Lines changed: 1368 additions & 33 deletions

File tree

.agents/skills/bizerbrain/SKILL.md

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ metadata:
2222
Use this skill any time the user:
2323

2424
- **Captures** something they want to remember (a meeting, a decision, a reading note, a thought)
25+
- **Drops in a raw source** (a transcript, an article, an email thread) and wants it filed into the brain
2526
- **Asks about prior context** ("what did we decide about X?", "what's the status of Y?", "who did I talk to about Z?")
2627
- **Mentions a person, project, or topic recurrently** — even casually — and would benefit from those references being linked together
2728
- **Asks for a summary** of recent activity, a person, a project, or a topic
@@ -42,73 +43,123 @@ The brain lives at `$BRAIN_DIR` (default `/srv/bizerbrain/brain`). Four tools op
4243

4344
### Folder conventions
4445

45-
- `daily/YYYY-MM-DD.md` — daily journal-style capture (one note per day)
46-
- `pages/<Topic>.md` — topical or per-entity notes; one note per person, project, place, or recurring idea (e.g., `pages/Project Aurora.md`, `pages/Sarah Johnson.md`)
46+
Three folders, three jobs:
4747

48-
Use exact human-friendly filenames. `pages/Project Aurora.md`, not `pages/project-aurora.md`. The user reads these in a UI that links by exact name.
48+
- **`sources/`****raw, immutable inputs.** Transcripts, articles, email dumps, dictated notes, anything the user drops in for you to process. **Treat as append-only.** Never rewrite the body of a source file. You may add a small `<!-- ingested: YYYY-MM-DD -->` HTML comment at the top, but do not edit the content.
49+
- **`pages/`****the wiki: synthesized topical or per-entity notes.** One note per person, project, place, recurring idea (e.g., `pages/Project Aurora.md`, `pages/Sarah Johnson.md`). You own these — create them, maintain them, merge as needed.
50+
- **`daily/`****the human's journal.** One note per day, named `daily/YYYY-MM-DD.md`. The user may write here directly. You may append, but be conservative — preserve their voice.
51+
52+
The boundary that matters most: **`sources/` is read-only for you; `pages/` is yours to maintain; `daily/` is the user's home turf.**
53+
54+
Use exact human-friendly filenames in `pages/`. `pages/Project Aurora.md`, not `pages/project-aurora.md`. The user reads these in a UI that links by exact name.
4955

5056
### Wiki links
5157

52-
Cross-link related notes with `[[Page Name]]`. The first time you reference a person, project, or recurring topic in a daily note, link it to its topical page. The link target is the basename of the file without the `.md` extension. The web UI auto-creates a stub note if the target doesn't exist yet.
58+
Cross-link related notes with `[[Page Name]]`. The first time you reference a person, project, or recurring topic in a daily note or a source, link it to its topical page in `pages/`. The link target is the basename of the file without the `.md` extension. The web UI auto-creates a stub note if the target doesn't exist yet.
5359

5460
Optional alias form: `[[Sarah Johnson|Sarah]]` displays as "Sarah" but links to `pages/Sarah Johnson.md`.
5561

62+
### Page structure: compiled truth + timeline
63+
64+
Every note in `pages/` follows this structure:
65+
66+
```markdown
67+
# <Entity name>
68+
69+
<Compiled truth — a concise, current, rewritable summary of what is
70+
known. 1–6 paragraphs or a few bullet points. This is what you'd
71+
tell someone who asked "who/what is X?" in a sentence or two.>
72+
73+
---
74+
75+
## Timeline
76+
77+
- YYYY-MM-DD — <event or fact> ([[daily/YYYY-MM-DD]] or [[sources/...]])
78+
- YYYY-MM-DD — <earlier event>
79+
- ...
80+
```
81+
82+
The **compiled truth** above the `---` separator is rewritable: you keep it current as new information arrives. The **timeline** below is **append-only**: every meaningful piece of evidence gets a new dated entry with a back-link to where it came from. Never delete or rewrite timeline entries; only add to them.
83+
84+
This gives the user two things in one file: the current understanding (truth), and the provenance trail (timeline) they can audit if they ever doubt something you wrote.
85+
86+
`sources/` files do not follow this structure — they're raw input. `daily/` files do not follow this structure — they're the user's journal.
87+
5688
## Procedure
5789

58-
### Capturing from a conversation or event
90+
### Capturing from a conversation or event (daily note)
5991

60-
1. Compute today's path: `daily/YYYY-MM-DD.md` (UTC or user's local; pick one and be consistent).
92+
1. Compute today's path: `daily/YYYY-MM-DD.md`.
6193
2. `read_note(today_path)` — if it returns `ok: false`, treat as empty.
6294
3. **Identify entities** mentioned: people, projects, decisions, recurring topics.
6395
4. Append a new section to the day's note with what was captured. Use `[[Page Name]]` for every entity.
6496
5. `write_note(today_path, merged_content)` — pass the full file contents, never just the new section.
65-
6. **For each new or significantly-updated entity**: ensure `pages/<Entity>.md` exists. If it doesn't, write a stub:
97+
6. **For each entity referenced**: update or stub the topical page in `pages/` using the "Update a topical page" procedure below.
6698

67-
```
68-
# <Entity>
99+
### Ingesting a source (transcript, article, email)
69100

70-
First mentioned in [[YYYY-MM-DD]].
71-
```
101+
1. Pick a clear filename under `sources/`, dated and descriptive: `sources/2026-05-09 product review transcript.md`.
102+
2. `read_note(path)` to confirm it's not already filed. If the user pasted content but didn't pick a path, choose one and tell them.
103+
3. `write_note(path, content)` — write the raw source verbatim. Optionally add a one-line HTML comment with the date you ingested it.
104+
4. **Extract entities** from the source: people, projects, decisions, dates, claims.
105+
5. For each entity, **update its topical page** (see below). Link the new timeline entries back to `[[sources/<filename>]]`.
106+
6. **Do not** synthesize the source into `pages/` so heavily that you replace it. The source is the receipt; the wiki is the synthesis.
72107

73-
If it already exists, optionally update it with what's new (use the "Updating" procedure below).
108+
### Updating a topical page (the truth + timeline pattern)
74109

75-
### Updating a topical page
110+
1. `read_note('pages/<Entity>.md')`. If `ok: false`, create with a stub (see "Stubbing a new entity" below).
111+
2. **Decide if the compiled truth needs to change.** New information may:
112+
- Add a fact ("Sarah is now the launch lead") → update truth
113+
- Correct a fact ("Sarah moved to Berlin in March, not February") → update truth
114+
- Add a one-time event with no lasting effect ("Sarah was on PTO last week") → timeline only
115+
3. Update the compiled-truth section above the `---` separator to reflect the current state.
116+
4. **Append** a new timeline entry below the `---` with the date and a back-link. Never edit or remove existing timeline entries.
117+
5. `write_note('pages/<Entity>.md', merged_content)` with the full file contents.
76118

77-
1. `read_note('pages/<Entity>.md')` — preserve existing structure and content; do not paraphrase or rewrite what's already there unless the user asked.
78-
2. Merge new context into the existing structure: extend lists, add subsections, update status lines, add a new dated entry under a "## History" or "## Updates" section.
79-
3. `write_note('pages/<Entity>.md', merged_content)` — full content, never partial.
119+
### Stubbing a new entity
80120

81-
### Recalling for the user
121+
When you reference `[[Name]]` for the first time and the file doesn't exist yet, create:
82122

83-
1. `search_notes(query)` — start with the user's words. Try variants if the first miss returns nothing useful.
84-
2. For the top 3-5 hits, `read_note(path)` to get the actual content.
85-
3. **Synthesize the answer from the read contents** — quote or summarize what's actually in the user's notes, do not invent or fill in from your training data. If the brain is silent on the question, say so.
86-
4. When citing, use the path: "From `daily/2026-04-12.md`: …"
123+
```markdown
124+
# Name
125+
126+
First mentioned in [[daily/YYYY-MM-DD]].
127+
128+
---
129+
130+
## Timeline
131+
132+
- YYYY-MM-DD — First mention in [[daily/YYYY-MM-DD]]
133+
```
87134

88-
### Cleaning up or restructuring
135+
You can flesh out the compiled truth later as more arrives.
89136

90-
When the user asks you to merge duplicate notes, rename a topic, or reorganize:
137+
### Recalling for the user
91138

92-
1. `list_notes()` to see the current structure.
93-
2. Use `read_note` to confirm what's in each affected file.
94-
3. Plan the merge or move; explain it to the user before executing if it touches more than ~5 files.
95-
4. Execute with `write_note` (and use the file-api `move` endpoint via the user if a true rename is needed — this skill does not include a delete or move tool by design).
139+
1. `search_notes(query)` — start with the user's words. Try variants if the first miss returns nothing useful.
140+
2. For the top 3-5 hits, `read_note(path)` to get the actual content.
141+
3. **Synthesize the answer from the read contents** — quote or summarize what's actually in the user's notes, do not invent or fill in from your training data. The compiled-truth sections of `pages/` are the best starting point; consult the timeline or the underlying `sources/` files when the user asks "how do we know?" or "when did that happen?"
142+
4. When citing, use the path: "From `pages/Sarah Johnson.md`: …" or "Per the 2026-04-22 source: …"
143+
5. If the brain is silent on the question, say so.
96144

97145
## Pitfalls
98146

99-
- **`write_note` replaces the entire file.** Always `read_note` first when updating; never call `write_note` with only the new content. This is the #1 source of data loss.
147+
- **`write_note` replaces the entire file.** Always `read_note` first when updating. This is the #1 source of data loss.
148+
- **Never rewrite a `sources/` file.** They are receipts. If a source contains errors, note the correction on the relevant topical page's timeline; don't edit the source.
149+
- **Never delete timeline entries.** Even a fact you later contradict belongs in the timeline — it's the audit trail.
100150
- **Don't invent paths or filenames.** When unsure whether a note exists, `search_notes` or `list_notes` first. Creating `pages/sarah.md` when `pages/Sarah Johnson.md` already exists fragments the brain.
101-
- **Preserve the user's words.** When updating an existing note, extend or annotate; don't rewrite. The user reads these in a web UI and notices when their phrasing changes.
151+
- **Preserve the user's words in daily notes.** When extending an existing daily entry, append rather than rewrite. The user reads these in a web UI and notices when their phrasing changes.
102152
- **The user may also be editing.** If they were typing in the UI when you write, the UI surfaces a conflict banner. Avoid spurious writes — only write when there's a real change to commit.
103-
- **Markdown only.** Stick to plain markdown. No HTML unless the user has been using HTML in the brain already.
153+
- **Markdown only.** Stick to plain markdown. No HTML unless the user has been using HTML in the brain already (the dated `<!-- ingested -->` comment in sources is the one exception).
104154
- **One topic, one note.** Don't create `pages/Aurora Launch.md` if `pages/Project Aurora.md` already covers it. Update the existing note.
105155
- **Wiki-link the basename, not the path.** `[[Sarah Johnson]]`, not `[[pages/Sarah Johnson.md]]` or `[[Sarah Johnson.md]]`.
106-
- **Date format.** Always `YYYY-MM-DD` for daily notes — sorts chronologically and matches the UI's "Today" button.
156+
- **Date format.** Always `YYYY-MM-DD` for daily notes and timeline entries.
107157

108158
## Verification
109159

110160
After a capture or update, verify:
111161

112162
1. `read_note(path)` and confirm the content is what you intended (especially after a `write_note` that merged old + new).
113-
2. For wiki links: each `[[Page]]` reference in a daily note should resolve to either an existing `pages/<Page>.md` or a freshly-created stub. `list_notes()` after the operation should show all referenced pages.
114-
3. For recall: the answer to the user should be supportable by quoting from the notes you read. If you can't quote it, you're hallucinating — re-read or admit the gap.
163+
2. For wiki links: each `[[Page]]` reference in a daily note or source should resolve to either an existing `pages/<Page>.md` or a freshly-created stub. `list_notes()` after the operation should show all referenced pages.
164+
3. For pages, confirm the structure is intact: a compiled-truth section, a `---` separator, a `## Timeline` heading, and dated entries with back-links.
165+
4. For recall: the answer to the user should be supportable by quoting from the notes you read. If you can't quote it, you're hallucinating — re-read or admit the gap.

packages/mcp-server/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
*.log

packages/mcp-server/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@bizerbrain/mcp-server",
3+
"version": "0.1.0",
4+
"private": true,
5+
"type": "module",
6+
"packageManager": "pnpm@10.33.0",
7+
"main": "src/server.js",
8+
"bin": {
9+
"bizerbrain-mcp": "src/server.js"
10+
},
11+
"scripts": {
12+
"start": "node src/server.js",
13+
"test": "node --test test/*.test.js"
14+
},
15+
"dependencies": {
16+
"@modelcontextprotocol/sdk": "^1.27.1"
17+
}
18+
}

0 commit comments

Comments
 (0)