AgenticSecond Brain
Second
Brain
An Obsidian vault that Claude can read, query, and write to. 360+ atomic notes, one idea each.
Drag to turn. Point at a note to see its links; click to pin it.Open the demo to turn the network and explore the notes.
Colour shows the folder a note sits in. Position, links and dot size are drawn for this page; the cards are sample notes.
- notes
- 0+
- note types
- 0
- MCP tools
- 0
Plain Markdown files, one idea each. Claude reads and writes them through nine MCP tools.
Anatomy of a note
Frontmatter on top, the idea below. Point at a part on either side.Tap a part on either side.
Source
Reading view
Portfolio bilingual — EN + DE with toggle
- type
- decision
- project
- luccafaust-dev
- tags
- i18nnext-intltoggle
- date
- 2026-04-20
Portfolio ships bilingual (EN + DE), switchable via a top-nav toggle. The i18n pass happens once content is solid, not in parallel, so the two languages don't drift.
Why: … How to apply: …
Linked by topic
Every line is a wiki-link. Point at a note to light its links.Tap a note to light its links.
Folders that mirror the work
Inbox, projects, knowledge, daily, research, templates. Open a note to see its frontmatter.
01-projects/second-brain-bridge/
Zod Validation
- type
- learning
- project
- second-brain-bridge
- tags
- mcpvalidationzod
- date
- 2026-03-15
Runtime validation with Zod schemas guarantees tool inputs and outputs are type-safe.
Nine tools, three jobs
Three tools find notes, three read, three write. Pick one to see a sample call.
Findby meaning or by frontmattervault → Claude
Readnotes, project context, skillsvault → Claude
Writecreate, update, linkClaude → vault
vault_recall
Hybrid recall — semantic + keyword + project filter (default)
takestaskprojectlimit
Sample call
{
"task": "decisions about automation",
"limit": 2
}Sample result
{
"notes": [
{ "path": "01-projects/hipm/decisions/n8n-statt-make.md",
"title": "n8n over Make", "score": 0.94,
"type": "decision", "project": ["hipm"],
"excerpt": "n8n replaces Make.com as the central..." },
{ "path": "02-knowledge/skills/webhook-patterns.md",
"title": "Webhook Patterns", "score": 0.61,
"type": "skill", "project": [],
"excerpt": "Webhook-based integrations follow..." }
],
"recall_summary": "2 relevante Notes: 1 decision, 1 skill"
}Search by meaning, on this machine
Notes and question are embedded by the same local model, then ranked by cosine similarity. Switch Ollama off to see the keyword fallback; each step is explained below.
Question
Which decisions affect the automation stack?
Embed
nomic-embed-text in Ollama on localhost
Rank
Cosine similarity against every cached note vector
Keyword fallback
title ×3tags ×2text ×1
Top notes
- n8n over Make0.94
- Webhook Patterns0.87
- Agent Orchestration0.79
How the search works
Every note becomes a vector
Ollama runs nomic-embed-text on localhost and turns each note into a vector. Long notes are cut at a paragraph break where possible. Each cached vector keeps the name of its model.
prepareText(text: string): string {if (text.length <= this.maxChars) return text;// Truncate at paragraph boundary if possible to preserve semantic coherenceconst truncated = text.slice(0, this.maxChars);const lastPara = truncated.lastIndexOf('\n\n');return lastPara > this.maxChars * 0.7 ? truncated.slice(0, lastPara) : truncated;}async embed(text: string): Promise<Float32Array> {const prepared = this.prepareText(text);const res = await fetch(`${this.host}/api/embeddings`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ model: this.model, prompt: prepared }),});if (!res.ok) {throw new Error(`Ollama embedding failed: ${res.status} ${res.statusText}`);}const data = await res.json() as { embedding: number[] };return new Float32Array(data.embedding);}
The question takes the same path
The same model embeds the question. Cosine similarity compares it with every cached note vector; the closest notes come back first, ten by default, optionally filtered by project or type.
export function cosineSimilarity(a: Float32Array, b: Float32Array): number {let dot = 0, normA = 0, normB = 0;for (let i = 0; i < a.length; i++) {dot += a[i] * b[i];normA += a[i] * a[i];normB += b[i] * b[i];}const denom = Math.sqrt(normA) * Math.sqrt(normB);return denom === 0 ? 0 : dot / denom;}
When Ollama is not there
If Ollama is unreachable when the server starts, vault_search scores words instead: title hits count three times, tags twice, text once. Coarser ranking, but search keeps answering.
const limit = rawLimit ?? 10;let results;if (ollamaAvailable) {const queryEmbedding = await ollama.embed(query);results = findTopK(queryEmbedding, embeddingsCache, limit, { project, type });} else {// Keyword fallbackconst notes = listNotes(VAULT_PATH).map(p => {const n = readNote(VAULT_PATH, p);return { path: p, title: n.title, tags: n.frontmatter.tags ?? [], content: n.content };});results = keywordSearch(query, notes).slice(0, limit);}
Why it runs on this machine
No note is sent anywhere to be embedded, no query costs money, and search works offline. A new embedding model re-embeds only the stale vectors, on the next start.
This machine
- Markdown filesthe vault folder
- Ollamanomic-embed-text on localhost
- Vector cacheeach vector with its model
- MCP serverranks, filters, reads
How notes get written
Claude writes them itself
Standing instructions give each note type a trigger. When one fires, Claude writes the note unprompted, into the folder of its type.
- decisionA choice with effects for six months or more01-projects/‹project›/decisions/
- learningUnexpected behaviour, a workaround, an insight worth reusing01-projects/‹project›/learnings/
- skillA process used three times or more, or written down as steps02-knowledge/skills/
- referenceA tool, source or system useful in more than one context01-projects/‹project›/references/
Through the server, with a schema
vault_write refuses a note without a type and embeds it right away. vault_update merges frontmatter and re-embeds. vault_link adds a wiki-link under a chosen heading and skips one that already exists.
1
vault_writecreates the filelearnings/error-boundaries.md
---type: learningtags: [mcp, errors]---MCP tools should catch and wrap…
returns
"embedded": true2
vault_updatemerges frontmatterlearnings/error-boundaries.md
---type: learningtags: [mcp, errors]status: active---MCP tools should catch and wrap…
returns
"re_embedded": true3
vault_linkadds a link under a headinglearnings/error-boundaries.md
---type: learning…---MCP tools should catch and wrap…## Related[[mcp-server-design]]
returns
"linked": true
The vault in daily use
Part of every session
A hook loads vault context with every prompt; another asks at the end of a turn whether a decision is worth a note. Before planning, Claude checks the project's earlier decisions so a new plan does not contradict them.
- Every prompt
hookloads vault context - Morning
/daily-briefthe day's briefing - Starting on a project
/brain-contextloads the project's context - Capturing
/braindump/url-dump/auto-researchthoughts, links and research become notes - End of a turn
hookis a decision worth a note? - Review
/weekly-checkin/knowledge-consolidationthe weekly check-in, consolidating what is known
Six decisions behind it
Plain Markdown files
Readable, git-trackable, editable without the server. The vault stays portable.
One idea per note
No long documents. Each note can be found and linked on its own.
Frontmatter as schema
Filter by type, project, status or date, not by full text.
Embeddings stay local
Ollama on localhost: no note leaves the machine to be embedded.
Vectors know their model
Each vector records its model; a model swap re-embeds only stale ones.
Keyword fallback
No Ollama at start? Search answers by keywords. Ranking suffers, the vault keeps working.