Skip to main content
Glama
README.md
# Hub Pessoal MCP

Fase 1 do projeto: um app desktop (Electron + React + TypeScript) que guarda
notas do dia a dia organizadas por temas e subtemas — ao estilo pastas — com
links soltos entre notas ao estilo Obsidian, e um servidor MCP que expõe
tudo isso pra qualquer assistente de IA compatível (Claude Desktop, por
exemplo), tudo rodando 100% local, sem nuvem.

## Como rodar o app desktop

```bash
npm install
npm run dev
```

Isso abre a janela do Electron com hot-reload ligado. Na primeira vez já
vem um punhado de notas de exemplo linkadas entre si, pra você ver o app
funcionando sem precisar digitar nada.

- **Barra lateral esquerda**: a árvore de temas. `+ Novo tema` cria um tema
  de nível raiz (ex: "Pagamentos"); passa o mouse num tema e clica `+` pra
  criar um subtema dentro dele (ex: "Projetos" → "Hub Pessoal"). Duplo
  clique no nome renomeia; o `×` remove (com confirmação) — as notas de um
  tema removido não são apagadas, só voltam a ficar "sem tema". **Arrasta
  um tema ou subtema** pra reordenar a lista do teu jeito — a ordem fica
  salva. Só dá pra arrastar dentro do mesmo grupo (temas de raiz entre si,
  subtemas de um mesmo pai entre si), não pra virar filho de outro tema.
- **Clica** num tema (ou subtema) pra ver, no meio, tudo que está
  relacionado a ele. Clicar num tema raiz agrega as notas dos subtemas
  também. Sem nada selecionado, o meio mostra a visão geral com um cartão
  por tema e a contagem de notas.
- Na barra de captura (embaixo, centro), escreve algo e aperta `Enter` — a
  nota nova entra automaticamente no tema selecionado. Use
  `[[Nome de outra nota]]` no meio do texto pra criar um link — se a nota
  ainda não existir, ela é criada automaticamente (igual ao Obsidian).
- No painel de detalhe (direita), dá pra trocar o tema de uma nota a
  qualquer momento pelo seletor abaixo do título. O conteúdo mostra os
  trechos que viraram link como texto clicável (clica em qualquer outro
  lugar do conteúdo pra editar); "Linka para" e "Linkado por" listam as
  notas conectadas, com um `×` pra desvincular sem apagar nada. `[[Nome]]`
  funciona tanto na barra de captura quanto editando o conteúdo de uma nota
  já existente — nos dois casos cria a nota referenciada se ela ainda não
  existir. O `+` ao lado de "Linka para" abre um dropdown com busca pra
  linkar uma nota já existente sem precisar digitar `[[Nome]]` no texto.
  URLs `http(s)://` soltas no conteúdo também viram link clicável, abrindo
  no navegador padrão do sistema (nunca dentro da janela do app).
- Botão **Selecionar** no topo da grade de notas liga um modo de seleção
  múltipla — marca várias notas e usa o seletor "Mover para…" que aparece
  pra trocar o tema de todas de uma vez.
- Atalhos: `Ctrl+K` foca a busca, `Ctrl+N` foca a barra de captura, `Esc`
  fecha o painel de detalhe.

## Como rodar o servidor MCP

```bash
npm run mcp:server
```

Ele lê e escreve o mesmo arquivo que o app desktop usa
(`data/notas.json`), então o que você adiciona por um lado aparece no
outro. Pra conectar no Claude Desktop, adiciona isto no
`claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "hub-pessoal": {
      "command": "npx",
      "args": ["tsx", "/caminho/completo/para/hub-pessoal-mcp/mcp-server/index.ts"]
    }
  }
}
```

Ferramentas expostas:

**Temas**
- `listar_temas` — devolve a árvore de temas/subtemas com a contagem de
  notas de cada um (tema raiz já vem com o total agregado dos subtemas).
  Chama essa primeiro: é como a IA descobre a organização do usuário e o
  caminho exato (ex: `"Projetos/Hub Pessoal"`) pra usar nas outras
  ferramentas.
- `criar_tema` — cria um tema ou subtema sem precisar criar nota junto;
  idempotente (não duplica se já existir um com o mesmo nome no mesmo nível).
- `renomear_tema` — renomeia um tema/subtema existente, pelo caminho.
- `remover_tema` — remove um tema/subtema pelo caminho; as notas que
  estavam nele não são apagadas, só voltam a ficar sem tema. Chama sem
  `confirmar` primeiro pra ver o impacto (subtemas e quantas notas seriam
  afetadas); só remove de fato com `confirmar: true`.

**Notas**
- `buscar_contexto` — busca por texto livre no título/conteúdo, sem se
  importar com acento (título pesa mais no ranking); aceita `tema`
  opcional pra restringir a busca a um assunto e `limite` pra cortar o
  tamanho da resposta.
- `listar_notas` — lista notas, as mais recentes primeiro; sem `tema` lista
  tudo (use `limite` pra não estourar o contexto), com `tema` filtra só
  aquele assunto (um tema raiz traz também as notas dos subtemas).
- `notas_relacionadas` — dado o ID de uma nota, mostra pra quais notas ela
  linka e quais linkam pra ela (os dois lados do `[[Nome]]`), pra puxar o
  fio de um assunto.
- `adicionar_nota` — cria uma nota; o `tema` aceita o mesmo formato de
  caminho, mas aqui ele **cria** o tema/subtema automaticamente se ainda
  não existir.
- `atualizar_nota` — corrige/expande uma nota existente (título, conteúdo,
  tema ou links) sem precisar apagar e recriar; só os campos passados são
  alterados, e `tema: ""` tira a nota do tema atual.
- `remover_nota` — remove uma nota pelo ID. Chama sem `confirmar` primeiro
  pra ver uma prévia sem apagar nada; só remove de fato com
  `confirmar: true`.
- `buscar_semantico` — busca por similaridade de vocabulário (TF-IDF +
  cosseno, calculado localmente, sem IA/nuvem), pra achar notas do mesmo
  assunto mesmo sem a frase exata em comum. Complementa `buscar_contexto`.
- `historico_nota` — reconstrói como uma nota mudou ao longo do tempo, a
  partir dos backups automáticos em `data/backups/` (até 5 versões).
- `contexto_projeto` — empacota as notas de um tema/projeto (+ as ligadas
  por `[[Nome]]` que ficam fora dele) num bloco de texto único, pronto pra
  colar como contexto em outra sessão/agente.
- `listar_pendencias` — varre o conteúdo procurando `- [ ] algo` ou
  `TODO: algo` e devolve os itens em aberto com a nota de origem; aceita
  `tema` pra restringir (ex: `"agenda"`).
- `sugerir_organizacao` — aponta notas sem tema, temas quase vazios e
  pares de temas com nome parecido (possível duplicata por digitação).

Um caminho de tema que não existe (nas ferramentas que só leem, não criam)
retorna um aviso pedindo pra chamar `listar_temas` primeiro, em vez de
inventar resultado.

## Estrutura do projeto

```
hub-pessoal-mcp/
  src/
    main/        → processo principal do Electron (janela + IPC)
    preload/      → ponte segura entre o main e a interface
    renderer/     → a interface (React): árvore de temas, grade de notas, etc.
    shared/       → tipos e a camada de armazenamento, usados por todo mundo
  mcp-server/      → servidor MCP standalone (roda separado do app)
  data/notas.json  → onde tudo fica salvo (JSON simples, fácil de inspecionar)
  data/backups/    → cópias automáticas antes de cada escrita (as 5 mais recentes)
```

## Decisões da Fase 1 (e o que fica pra depois)

- **Armazenamento em JSON**, não SQLite ainda — dá pra ler com o olho e
  não exige compilar dependência nativa. Fase 2 troca por SQLite +
  `sqlite-vec` sem mudar a API de `src/shared/armazenamento.ts`.
- **Sem OCR nem captura de recibos ainda** — isso é a Fase 2.
- **Sem app mobile ainda** — isso é a Fase 3, com sync via rede local.
- **UI**: tema neumórfico escuro com roxo `#7c6cf0`, notas organizadas por
  temas/subtemas hierárquicos (2 níveis, reordenáveis por drag-and-drop) em
  vez de tags soltas.
- **Backup automático**: cada escrita em `data/notas.json` primeiro copia o
  estado atual pra `data/backups/`, mantendo as últimas 5 versões — recupera
  de uma migração com bug ou uma remoção em cascata que deu errado.
- **Busca sem acento**: tanto a busca da interface quanto `buscar_contexto`
  ignoram acentuação ("financas" acha "Finanças").

## Scripts

| Comando | O que faz |
|---|---|
| `npm run dev` | roda o app desktop com hot-reload |
| `npm run build` | gera o instalador (`.dmg`/`.exe`/`.AppImage`) |
| `npm run mcp:server` | roda só o servidor MCP, via stdio |
| `npm run typecheck` | checa os tipos do projeto inteiro |
| `npm test` | roda todos os testes: armazenamento/temas/migração/backup (`src/shared`), lógica do MCP (`mcp-server`) e componentes React (`src/renderer`, via Testing Library + jsdom) |
| `npm run test:watch` | mesma coisa, em modo watch |

## Licença

[MIT](LICENSE).