Skip to main content
Glama
50R1Paps

second-brain-worker

by 50R1Paps

Second Brain Worker

Un knowledge base personale serverless su Cloudflare Workers che indicizza la tua wiki Obsidian e file esterni, con ricerca ibrida (semantica + keyword) accessibile via MCP (Model Context Protocol) direttamente dal tuo IDE.

Cos'è

Second Brain Worker è un Cloudflare Worker che:

  • Indicizza file markdown dalla wiki Obsidian e file esterni (PDF, articoli, testo)

  • Chunka il contenuto rispettando la struttura delle sezioni (##/###), preservando frontmatter e wikilink

  • Genera embedding semantici via Workers AI (@cf/baai/bge-base-en-v1.5, 768 dim)

  • Memorizza tutto in R2 (raw), D1 (metadata + FTS5 keyword search), e Vectorize (embedding)

  • Espone 5 tool MCPretrieve, ingest, reindex, read, grep — accessibili dal tuo IDE via OAuth GitHub

  • Sincronizza automaticamente la wiki via GitHub webhook: quando fichi push su main, i file .md cambiati vengono re-indicizzati

Tutto entro il free tier di Cloudflare.


Related MCP server: obsidian-mcp

Architettura

                    ┌─────────────────────────────────────────────┐
                    │              Cloudflare Worker               │
                    │                                              │
   GitHub Webhook ──▶  /webhook/github  ──▶  fetch .md  ──▶  R2   │
                    │                                  │           │
   REST API ────────▶  /api/ingest      ──▶  chunker    │           │
                    │  /api/retrieve    ──▶  embedder   │           │
                    │  /api/reindex     ──▶  D1 + Vectorize        │
                    │  /api/health      │           │               │
                    │                   │           │               │
   MCP Client ─────▶  /mcp (OAuth)      │           │               │
                    │  /authorize       │           │               │
                    │  /callback        │           │               │
                    └─────────────────────────────────────────────┘
                           │         │           │
                    ┌──────▼──┐ ┌───▼────┐ ┌────▼──────┐
                    │   R2    │ │   D1   │ │ Vectorize │
                    │ (raw)   │ │(meta + │ │ (embedding│
                    │         │ │  FTS5) │ │  vectors) │
                    └─────────┘ └────────┘ └───────────┘

Componenti

  • R2 (RAW_BUCKET) — storage del contenuto raw dei file

  • D1 (DB) — database SQLite con tabelle files, chunks, e tabella virtuale FTS5 chunks_fts per keyword search

  • Vectorize (VECTORIZE) — indice di embedding semantici (768 dimensioni)

  • Workers AI (AI) — modello bge-base-en-v1.5 per generazione embedding

  • KV (OAUTH_KV) — storage di stato/CSRF token per il flow OAuth

  • Durable Object (SECOND_BRAIN_MCP) — McpAgent che mantiene la sessione MCP

  • OAuth Provider — integrazione @cloudflare/workers-oauth-provider con GitHub OAuth


MCP Tools

Il server MCP espone 5 tool accessibili dal tuo IDE:

retrieve

Ricerca ibrida nel knowledge base: query semantica via Vectorize + query keyword via D1 FTS5. I risultati vengono fusi (merge) con pesi 50/50, ordinati per score combinato, e arricchiti con metadata dal D1.

Parametri:

  • query (string, required) — query in linguaggio naturale o keyword

  • limit (number, default 10, max 50) — numero massimo di risultati

  • file_type ("wiki_page" | "ingested", optional) — filtra per tipo di file

  • section_prefix (string, optional) — filtra per prefisso sezione (es. ## Architecture)

  • file_key_prefix (string, optional) — filtra per prefisso file key (es. wiki/concepts/)

ingest

Carica un file nel knowledge base: il contenuto viene chunkato, embeddato, e salvato in R2 + D1 + Vectorize. Se il file esiste già, i vecchi chunk e vector vengono sostituiti.

Parametri:

  • file_key (string, required) — identificatore univoco. Per wiki page: path relativo (es. wiki/concepts/Tool Attention.md). Per file esterni: filename:uuid

  • content (string, required) — contenuto testuale completo

  • file_type ("wiki_page" | "ingested", required) — tipo di file

  • title (string, optional) — titolo del file

  • source (string, optional) — URL o path sorgente

reindex

Re-indicizza un file specifico o tutti i file. Legge il raw da R2, re-esegue chunking + embedding, aggiorna Vectorize e D1.

Parametri:

  • file_key (string, optional) — file da re-indicizzare. Se omesso, re-indicizza tutti i file

read

Legge il testo raw di un file indicizzato da R2 con offset/limite opzionali. Utile per leggere il contesto completo attorno a un chunk trovato via retrieve.

Parametri:

  • file_key (string, required) — file key del file da leggere

  • offset (number, optional, default 0) — offset in caratteri da cui iniziare

  • max_chars (number, optional, default 2000, max 10000) — numero massimo di caratteri da restituire

grep

Cerca un pattern regex nel testo raw di un file indicizzato. Restituisce i match con contesto opzionale. Utile per estrarre dati strutturati (date, importi, ID) dai documenti.

Parametri:

  • file_key (string, required) — file key del file in cui cercare

  • pattern (string, required) — pattern regex JavaScript

  • max_matches (number, optional, default 10, max 50) — numero massimo di match

  • context (number, optional, default 40, max 200) — caratteri di contesto attorno a ogni match


REST API

Oltre ai tool MCP, il Worker espone endpoint REST (non autenticati, utili per script e setup):

Metodo

Endpoint

Descrizione

GET

/api/health

Stato del Worker: file indicizzati, chunk totali

POST

/api/ingest

Ingest di un file (stesso formato del tool MCP)

POST

/api/retrieve

Retrieve ibrido (stesso formato del tool MCP)

POST

/api/reindex

Reindex di un file o di tutti

POST

/api/read

Leggi raw text da R2 con offset/limit

POST

/api/grep

Ricerca regex sul contenuto indicizzato

GET

/api/metrics

Metriche aggregate di retrieve (latency, score, zero-result rate)

POST

/webhook/github

Webhook GitHub per sync automatico


Prerequisiti

  • Node.js 22+

  • Account Cloudflare (free tier sufficiente)

  • GitHub OAuth App — per l'autenticazione MCP

  • Wiki Obsidian in un repo GitHub (se vuoi usare il sync automatico)


Setup completo

1. Clona e installa

git clone https://github.com/50R1Paps/second-brain-worker.git
cd second-brain-worker
npm install

2. Crea le risorse Cloudflare

Esegui questi comandi con wrangler per creare le risorse necessarie:

# Crea R2 bucket
npx wrangler r2 bucket create second-brain-raw

# Crea D1 database
npx wrangler d1 create second-brain
# Annota il database_id dal output

# Crea KV namespace per OAuth
npx wrangler kv namespace create OAUTH_KV
# Annota l'id dal output

# Crea Vectorize index (768 dim per bge-base-en-v1.5)
npx wrangler vectorize create second-brain-embeddings --dimensions 768 --metric cosine

3. Configura wrangler.toml

Copia il file di esempio e compila i valori reali:

cp wrangler.toml.example wrangler.toml

Modifica wrangler.toml sostituendo <your-d1-database-id> e <your-kv-namespace-id> con i valori ottenuti al passo 2.

4. Applica la migration D1

# Locale (per dev)
npm run db:migrate

# Remoto (per produzione)
npm run db:migrate:remote

5. Crea una GitHub OAuth App

  1. Vai su GitHub Settings > Developer settings > OAuth Apps > New OAuth App

  2. Compila:

    • Application name: Second Brain MCP

    • Homepage URL: https://second-brain.<tuo-subdomain>.workers.dev

    • Authorization callback URL: https://second-brain.<tuo-subdomain>.workers.dev/callback

  3. Annota il Client ID e genera un Client Secret

6. Imposta i segreti

# OAuth
npx wrangler secret put GITHUB_CLIENT_ID
npx wrangler secret put GITHUB_CLIENT_SECRET
npx wrangler secret put COOKIE_ENCRYPTION_KEY

# GitHub webhook sync (opzionale, solo se usi il sync automatico)
npx wrangler secret put WEBHOOK_SECRET
npx wrangler secret put GITHUB_TOKEN
npx wrangler secret put GITHUB_TOKEN_EXPIRY

Per COOKIE_ENCRYPTION_KEY puoi generare una stringa casuale con:

openssl rand -hex 32

Per GITHUB_TOKEN, crea un Personal Access Token con scope repo (per leggere i file .md via API).

Per WEBHOOK_SECRET, genera un'altra stringa casuale e usala anche come secret del webhook GitHub.

Per GITHUB_TOKEN_EXPIRY, inserisci la data di scadenza del PAT in formato ISO (es. 2026-09-25T00:00:00Z). Il Cron Trigger giornaliero controlla questa data e apre automaticamente una issue su second-brain-worker 2 giorni prima della scadenza come promemoria.

7. Deploy

npm run deploy

Annota l'URL del Worker (es. https://second-brain.<tuo-subdomain>.workers.dev).

8. Inizializza il knowledge base (setup script)

Se hai la wiki Obsidian in locale, puoi ingerire tutti i file .md con lo script di setup:

# Locale (durante dev)
npm run setup -- --wiki-dir /path/to/wiki --url http://localhost:8787

# Remoto (dopo deploy)
npm run setup -- --wiki-dir /path/to/wiki --url https://second-brain.<tuo-subdomain>.workers.dev

# Dry run (lista file senza ingerire)
npm run setup:dry -- --wiki-dir /path/to/wiki

9. Configura il webhook GitHub (opzionale)

Per sincronizzare automaticamente la wiki quando fichi push su main:

  1. Vai su GitHub > Your Repo > Settings > Webhooks > Add webhook

  2. Compila:

    • Payload URL: https://second-brain.<tuo-subdomain>.workers.dev/webhook/github

    • Content type: application/json

    • Secret: lo stesso valore di WEBHOOK_SECRET

    • Trigger: "Just the push event"

  3. Salva

Da ora, ogni push su main che modifica file .md in wiki/ triggera la re-indicizzazione automatica.


Gestione dei secret

I secret sono gestiti via wrangler secret put e stored nella dashboard Cloudflare (mai nel codice). Ecco quando aggiornarli:

Secret

Scade?

Quando aggiornare

GITHUB_TOKEN

— scade in base alla configurazione del PAT

Quando il Personal Access Token scade: npx wrangler secret put GITHUB_TOKEN e incolla il nuovo token

GITHUB_TOKEN_EXPIRY

— da aggiornare insieme al token

Quando aggiorni GITHUB_TOKEN: npx wrangler secret put GITHUB_TOKEN_EXPIRY con la nuova data di scadenza

GITHUB_CLIENT_ID

No

Solo se revochi/ricrei l'OAuth App su GitHub

GITHUB_CLIENT_SECRET

No

Solo se revochi/ricrei l'OAuth App su GitHub

COOKIE_ENCRYPTION_KEY

No

Mai (a meno che tu non voglia invalidare tutte le sessioni attive)

WEBHOOK_SECRET

No

Mai (deve coincidere con il secret configurato nelle impostazioni webhook su GitHub)

Rotazione del GITHUB_TOKEN

Il GITHUB_TOKEN (Personal Access Token con scope repo) è l'unico secret con scadenza. Un Cron Trigger giornaliero controlla GITHUB_TOKEN_EXPIRY e apre automaticamente una issue su second-brain-worker 2 giorni prima della scadenza come promemoria.

Quando ricevi la notifica (o quando il token è già scaduto):

  1. Crea un nuovo token su GitHub Settings > Tokens con scope repo

  2. Aggiorna i secret su Cloudflare:

npx wrangler secret put GITHUB_TOKEN
npx wrangler secret put GITHUB_TOKEN_EXPIRY
  1. Incolla il nuovo token e la nuova data di scadenza (formato ISO, es. 2026-09-25T00:00:00Z)

  2. Chiudi la issue di promemoria su GitHub

Nessun altro secret o configurazione su Cloudflare deve essere aggiornato.


Configurare il client MCP nel IDE

Windsurf / Cursor

Crea o modifica il file mcp_config.json nel tuo IDE (in Windsurf: Settings > MCP Servers):

{
  "mcpServers": {
    "second-brain": {
      "command": "npx",
      "args": [
        "workers-mcp",
        "proxy",
        "https://second-brain.<tuo-subdomain>.workers.dev/mcp"
      ]
    }
  }
}

Sostituisci <tuo-subdomain> con il tuo subdomain reale.

Al primo utilizzo, il IDE aprira il browser per l'autenticazione GitHub. Dopo il login, i 5 tool (retrieve, ingest, reindex, read, grep) saranno disponibili nell'AI assistant.

Claude Desktop

Aggiungi al file claude_desktop_config.json:

{
  "mcpServers": {
    "second-brain": {
      "command": "npx",
      "args": [
        "workers-mcp",
        "proxy",
        "https://second-brain.<tuo-subdomain>.workers.dev/mcp"
      ]
    }
  }
}

Sviluppo

Comandi disponibili

Comando

Descrizione

npm run dev

Avvia il Worker in locale con wrangler dev

npm run deploy

Deploy su Cloudflare

npm test

Esegue i test (vitest)

npm run test:watch

Test in watch mode

npm run typecheck

Type checking con tsc --noEmit

npm run db:migrate

Applica migration D1 in locale

npm run db:migrate:remote

Applica migration D1 in remoto

npm run setup

Script di ingest della wiki

npm run setup:dry

Dry run dello script di setup

Struttura del progetto

src/
├── worker.ts          # Entry point: OAuthProvider + routing
├── mcp.ts             # SecondBrainMCP (McpAgent) con i 5 tool
├── types.ts           # Tipi condivisi: Env, request/response interfaces
├── http.ts            # Utility HTTP: jsonResponse, handleCORS
├── health.ts          # Health check endpoint
├── ingest.ts          # Ingestion: chunking, embedding, R2+D1+Vectorize, GitHub push
├── retrieve.ts        # Retrieve ibrido: semantic + keyword search, merge, metrics
├── metrics.ts         # Metriche aggregate di retrieve (latency, score, zero-result)
├── reindex.ts         # Reindex singolo file o tutti i file
├── read.ts            # Read raw text da R2 con offset/limit
├── grep.ts            # Grep regex sul contenuto indicizzato
├── chunker.ts         # Markdown chunker (split su ##/###, overlap, wikilink-safe)
├── github-handler.ts  # Hono app: REST API + OAuth flow (/authorize, /callback)
├── oauth-utils.ts     # Utility OAuth: state, CSRF, cookie, approval dialog
├── webhook.ts         # GitHub webhook handler per sync automatico
├── cron.ts            # Cron Trigger: reminder scadenza GITHUB_TOKEN via GitHub Issue
└── setup.ts           # Logica del setup script (ingest bulk)

migrations/
├── 0001_initial_schema.sql       # Schema D1: files, chunks, chunks_fts + trigger
└── 0002_retrieve_metrics.sql     # Tabella retrieve_metrics per observability

scripts/
└── setup.ts           # CLI entry point per il setup script

test/                  # Test suite (vitest + @cloudflare/vitest-pool-workers)

Test

npm test           # tutti i test
npm run typecheck  # type checking

I test usano @cloudflare/vitest-pool-workers per simulare l'ambiente Workers con D1, R2, e AI bindings.


Operazioni di manutenzione

Deploy del Worker

Dopo aver modificato il codice:

npm run typecheck   # verifica tipi
npm test            # verifica test
npx wrangler deploy # deploy su Cloudflare

Non serve reindicizzare dopo un deploy che non cambia la logica di ingest/retrieve.

Applicare una nuova migration D1

Quando aggiungi una migration (es. 0002_retrieve_metrics.sql):

# Locale (per dev)
npm run db:migrate

# Remoto (per produzione)
npx wrangler d1 migrations apply second-brain --remote

La migration va applicata prima del deploy se il codice dipende dalla nuova tabella.

Reindicizzare i documenti (popolare Vectorize)

Se i vettori sono mancanti o vuoi rigenerare gli embeddings:

# Reindex di un singolo file
curl -X POST https://second-brain.<tuo-subdomain>.workers.dev/api/reindex \
  -H "Content-Type: application/json" \
  -d '{"file_key": "wiki/concepts/Esempio.md"}'

# Reindex di tutti i file (attenzione: può hit rate limit su Workers AI)
curl -X POST https://second-brain.<tuo-subdomain>.workers.dev/api/reindex \
  -H "Content-Type: application/json" \
  -d '{}'

Nota: il reindex di tutti i file (113+) può fallire per rate limiting di Workers AI. Se semantic_hits è 0 dopo un reindex completo, reindicizza i file singolarmente con una pausa di ~1 secondo tra ogni chiamata. Verifica lo stato con:

npx wrangler d1 execute second-brain --remote \
  --command "SELECT COUNT(*) as total, COUNT(vector_id) as with_vectors FROM chunks"

Verificare le metriche di retrieve

Dopo aver fatto qualche retrieve, controlla le metriche aggregate:

# Ultime 24 ore (default)
curl https://second-brain.<tuo-subdomain>.workers.dev/api/metrics

# Ultima ora
curl https://second-brain.<tuo-subdomain>.workers.dev/api/metrics?period=1h

# Ultimi 7 giorni
curl https://second-brain.<tuo-subdomain>.workers.dev/api/metrics?period=7d

# Ultimi 30 giorni
curl https://second-brain.<tuo-subdomain>.workers.dev/api/metrics?period=30d

Periodi validi: 1h, 24h, 7d, 30d.

Le metriche includono: total queries, zero-result rate, avg/p50/p95 latency, score distribution, e search type breakdown (semantic/keyword/hybrid).


Documenti


Limiti e note

  • Il chunker e ottimizzato per markdown con frontmatter YAML e wikilink [[Nome]] (formato Obsidian)

  • L'embedding model bge-base-en-v1.5 produce vettori a 768 dimensioni — assicurati che il Vectorize index sia creato con --dimensions 768

  • Il retrieve ibrido usa pesi 50% semantico + 50% keyword, con top-K = 20 per ciascuna modalita

  • Il content dei risultati viene troncato a 2000 caratteri per chunk

  • L'OAuth flow supporta solo GitHub come identity provider

  • Il webhook sync processa solo file .md nella cartella wiki/ su push a main

  • I commit automatici del Worker su GitHub includono [skip ci] per non triggerare la pipeline di lint

  • Le metriche di retrieve sono best-effort: se la persistenza su D1 fallisce, il retrieve non viene interrotto

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to interact with Obsidian vaults through vector search, vault indexing, and file monitoring. It provides a standardized interface for searching and managing markdown-based personal knowledge management data within the Model Context Protocol.
    6,218 npm
    1
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables semantic search and note management for Obsidian vaults via the Model Context Protocol, allowing LLMs to search, read, and index notes, PDFs, and web pages locally.
    -