th-memory-mcp
Servidor MCP de memória de longo prazo para OpenCode e outros harnesses — armazena preferências, lições e histórico de uso em um único arquivo SQLite local (100% local, sem API externa) para que a IA possa "lembrar e se adaptar" ao usuário por meio de aprendizado baseado em contexto.
Documentação
th-memory-mcp
Status: v2.3.0 — um mecanismo de memória temporal, ciente de conflitos e com recuperação híbrida. 16 ferramentas MCP, 25 suítes de teste aprovadas. Migração de esquema não destrutiva desde a v1 (todos os dados da v1 preservados). Novo na v2.2: estados de ciclo de vida, validade temporal, resolução de conflitos/deduplicação com escopo USER/SESSION/PROJECT/GLOBAL, recuperação híbrida FTS+vetorial (RRF), grafo de memória, montagem de get_context, consolidação periódica e link_memory / merge_memory / update_memory / import_memory / extract_memories. Novo na v2.2.3: recuperação com escopo aplicado, isolamento de escopo no grafo, ida e volta de exportação/importação, caminho de importação endurecido (realpath), validação estrita de importação, eliminação de consultas N+1, benchmark frio/ablação e interruptor MEMORY_RETRIEVAL_MODE. Novo na v2.2.7: filtro de segredos sincronizado entre o hook do Claude e o capture-core (redação de 6 padrões em vez de descarte de linha), correção do err() para retornar isError:true conforme a especificação MCP, correção da rotação de backup (backup apenas quando há migrações pendentes + poda para 5 arquivos) e adição de registro de erros de hook para o destilado do SessionEnd. Novo na v2.2.8: correção da contaminação de escopo 0,75→0 (crítico) e falso conflito 0→1 (vazamento GLOBAL), correção do salto 1 do grafo 0,52→1,0 via includeGraph e reescalonamento dos perfis de benchmark para 5K/20K/100K/500K/1M (pré-commit agora rápido 5K + normal 20K apenas). Novo na v2.2.9: extensão da exportação para entidades/usuários/relações, esquecimento agora remove links associados, migração com falha fechada com helper ISO compartilhado, eliminação do N+1 do FTS, inclusão de smoke no script de teste e visualizador sincronizado com perfis reescalonados 5K/20K/100K/500K/1M. Novo na v2.3.0: CLI th-memory + sublinhado de destaque.
Requisitos
- Node.js >= 20 — o servidor usa APIs exclusivas do Node (a compilação nativa
better-sqlite3e a resoluçãoimport.meta.url) e o SDK MCP exige um runtime moderno. Testes de CI no Node 20.x e 22.x. - npm — para instalar dependências e executar os scripts de build/teste (
npm install,npm run build,npm test). - OpenCode — o host que carrega este servidor MCP e o plugin de captura automática. Qualquer build que suporte MCP via stdio + plugins funciona; o plugin roda no runtime Bun integrado do OpenCode.
- SO: Windows / macOS / Linux — o servidor é multiplataforma (Node). O plugin de captura automática roda onde quer que o runtime Bun do OpenCode rode. Nota para Windows:
MEMORY_DB_PATHé mais fácil de definir comsetx; no macOS/Linux useexportno seu perfil de shell.
Nenhum serviço externo, conta ou chave de API é necessário — tudo vive em um único arquivo SQLite local.
Início Rápido
Caminho mais rápido: após clonar, execute npm run quickstart — ele compila, configura opencode.json, implanta o plugin e define MEMORY_DB_PATH para você em um único comando. As etapas abaixo mostram exatamente o que ele faz (use-as se preferir controle manual).
Instalação via npm (alternativa): instale o servidor globalmente com npm install -g th-memory-mcp (ou execute-o sob demanda com npx th-memory-mcp), depois aponte o mcp command em opencode.json para th-memory-mcp em vez do dist/index.js compilado. O plugin de captura automática ainda vem deste repositório (copie src/plugin/learning-capture.ts conforme descrito na etapa 4 abaixo).
Nota: não é mais publicado no GitHub Packages — instale a partir do npmjs com
npm i -g th-memory-mcp.
# 1. Clone and build
git clone https://github.com/worakorn-prince/th-memory-mcp.git
cd th-memory-mcp
npm install
npm run build
# 2. Share one DB between the server and the plugin
# Windows (PowerShell):
setx MEMORY_DB_PATH "$PWD/data/memory.db"
# macOS / Linux (add to your shell profile, e.g. ~/.zshrc):
# export MEMORY_DB_PATH="$PWD/data/memory.db"
- Mescle isto no seu
~/.config/opencode/opencode.json(substitua<REPO>pelo caminho absoluto do clone):
{
"instructions": ["<REPO>/AGENTS.memory.example.md"],
"mcp": {
"memory": {
"type": "local",
"command": ["node", "<REPO>/dist/index.js"],
"enabled": true,
"environment": { "MEMORY_DB_PATH": "<REPO>/data/memory.db" }
}
}
}
- (Opcional) Captura automática: copie
src/plugin/learning-capture.ts→~/.config/opencode/plugins/ - Reinicie o OpenCode
- Experimente: "Lembre-se de que prefiro pnpm" → nova sessão → "Qual gerenciador de pacotes eu prefiro?"
Arquitetura
OpenCode ──┬─ Plugin learning-capture (Bun) ── auto-captures prompts/tool/error into DB
│ └─ injects profile back into context on compaction
└─ MCP th-memory-mcp (Node.js stdio) ── 16 tools read/write the same SQLite DB
▲
Global instructions (memory-protocol.md) teach the AI to use the tools
Veja ARCHITECTURE_v2.md para a especificação completa da arquitetura.
Por que th-memory-mcp?
LLMs não se lembram de você entre sessões — cada novo chat começa em branco. O th-memory-mcp dá à sua IA uma memória privada e local de longo prazo:
- Aprendizado baseado em contexto, não fine-tuning — ele captura suas preferências, correções e hábitos, e os recupera no contexto na próxima vez. Mesmo mecanismo dos recursos de memória dos principais produtos de IA, sem enviar nenhum dado para fora da sua máquina.
- 100% local e privado — um único arquivo SQLite, sem nuvem, sem API externa. Segredos são filtrados antes de qualquer coisa ser armazenada.
- Baixa sobrecarga — cada chamada de ferramenta é limitada (latência < 10 ms, tamanho de saída limitado) e a IA só consulta a memória quando é realmente útil, então nunca infla seu contexto.
- Resiliente — toda ferramenta degrada graciosamente; se o banco de dados estiver indisponível, a IA continua funcionando em vez de travar.
- Aberto e extensível — licença MIT, 16 ferramentas documentadas, um destilado baseado em regras e um plugin de captura automática que você pode adaptar.
Funciona com outros harnesses
O th-memory-mcp é um servidor MCP padrão, então as 9 ferramentas rodam em qualquer lugar onde MCP-over-stdio seja suportado. A captura automática completa (captura em segundo plano de prompts/ferramentas/erros + injeção de perfil) precisa de um runtime de hooks — o OpenCode tem isso integrado; o Claude Code obtém via nossa ponte de hooks; Codex e Cursor usam as ferramentas manualmente (sem runtime de hooks ainda).
| Recurso | OpenCode | Claude Code | Qwen Code | Codex | Cursor |
|---|---|---|---|---|---|
| 16 ferramentas MCP | ✅ | ✅ | ✅ | ✅ | ✅ |
| Captura automática (segundo plano) | ✅ plugin | ✅ hooks | ⚠️ adaptador | ❌ manual | ❌ Rules |
| Injeção de perfil | ✅ compactação | ✅ UserPromptSubmit | ❌ get_profile | ❌ get_profile | ❌ get_profile |
| Correspondência difusa lexical | ✅ (v2.0) | ✅ (v2.0) | ✅ (v2.0) | ✅ (v2.0) | ✅ (v2.0) |
- Claude Code: veja CLAUDE_CODE_HOOKS.md — hooks prontos para uso replicam o plugin do OpenCode (captura + injeção de perfil em
UserPromptSubmit/PreCompact, destilado baseado em regras emSessionEnd). - Qwen Code: veja QWEN_SETUP.md — MCP funciona totalmente; hooks usam o esquema Gemini-CLI, então a captura automática precisa de um pequeno adaptador.
- Codex: veja CODEX_SETUP.md
- Cursor: veja CURSOR_SETUP.md
Todos os harnesses compartilham um único arquivo SQLite via MEMORY_DB_PATH, então a memória capturada
em qualquer lugar é legível em todos os lugares.
Destaques
- Memória estruturada — preferências com pontuação de confiança, além de registros
dedicados de
lesson(situação → erro → correção) para capturar correções, não apenas fatos planos. - Ciclo de vida e temporal — toda memória tem um estado de ciclo de vida (ativa/obsoleta/substituída/arquivada), pontuação de confiança/importância/saliência, decaimento por tipo e intervalos de validade para que a IA possa raciocinar sobre a verdade em um ponto no tempo e cadeias de substituição.
- Ciente de conflitos — detecção de duplicatas, detecção de contradições e resolução de atualização/substituição preservam ambos os lados de evidências ambíguas em vez de sobrescrever silenciosamente.
- Recuperação híbrida —
get_contextcombina busca por palavras-chave FTS5 com correspondência difusa lexical sem dependências (similaridade de n-gramas com hash, FNV-1a de 512 dims) (fusão RRF + pontuação) e depois monta um contexto com orçamento de tokens com expansão opcional do grafo de memória. - Consolidação — agrupamento periódico de memórias semelhantes em memórias
derivadas com proveniência completa (links
derived_from). - Tailandês de primeira classe / i18n — tokenização ciente de tailandês no destilado; a IA aceita tailandês e inglês de forma intercambiável.
- Privado por padrão — um único arquivo SQLite local, sem nuvem, sem chaves de API,
com linhas de segredos (
api_key=,password:,token) filtradas antes do armazenamento. - Multi-harness — roda no OpenCode, Claude Code, Codex e Cursor compartilhando um único banco de dados; captura automática + injeção de perfil via plugin do OpenCode ou hooks do Claude.
- Leve e resiliente — Node +
better-sqlite3, sem extensões nativas extras; toda ferramenta degrada graciosamente para que a IA continue funcionando se o banco de dados estiver indisponível.
Scripts
| Comando | Descrição |
|---|---|
npm run build | compila TypeScript → dist/ |
npm start | executa o servidor MCP (stdio) a partir de dist/index.js |
npm run distill | destilado baseado em regras: interações → seções de perfil + poda de dados antigos (env RETENTION_DAYS padrão 30) |
npm test | suíte completa: captura, destilado, ciclo de vida, temporal, conflito, recuperação, grafo, contexto, consolidação, benchmark, segurança, tools_v21, smoke, e2e_transport, retrieval_benchmark, recall_regression, escopo, perfil, extração de entidades, conflict_benchmark, security_regression, export_import_roundtrip |
node test/capture.test.mjs | testa capture-core (filtra segredos, deduplica, trunca, insere SQL) |
node test/distill.test.mjs | testa distill-core (tokenização tailandesa, estatísticas, seções de perfil, poda) |
node test/lifecycle.test.mjs | testa o mecanismo de ciclo de vida (estados, decaimento, substituição) |
node test/temporal.test.mjs | testa o modelo temporal (validade, recuperação histórica) |
node test/conflict.test.mjs | testa a resolução de conflitos e deduplicação |
node test/retrieval.test.mjs | testa a recuperação híbrida FTS+vetorial+RRF |
node test/graph.test.mjs | testa o grafo de memória (entidades, relações, travessia) |
node test/context.test.mjs | testa a montagem de contexto + orçamento de tokens |
node test/consolidation.test.mjs | testa agrupamento + memórias derivadas |
node test/benchmark.test.mjs | benchmark de latência sobre 300 memórias |
node test/security.test.mjs | verificações de injeção / segurança |
node test/smoke.mjs | teste smoke de ponta a ponta via JSON-RPC (16 ferramentas) |
Ferramentas (16)
| Ferramenta | Descrição |
|---|---|
remember | upsert de preferência (categoria+chave) — salvar novamente a mesma chave aumenta a confiança em 0,1 (teto 1,0) |
recall | busca de preferências + lições (FTS5) + interações correspondentes recentes. Use antes de iniciar uma nova tarefa |
get_profile | visão geral do perfil do usuário: seções de perfil + principais preferências + 5 lições mais recentes |
save_lesson | registra uma lição aprendida com uma correção (situação / erro / correção) |
search_history | busca prompts passados do usuário por palavra-chave (trechos de 200 caracteres por linha) |
forget | exclui uma linha de memória (preferência/lição/interação) por id (+tipo evita conflito de id entre tabelas) |
memory_stats | estatísticas de memória: contagens por tipo, tamanho do banco, interação mais antiga/mais recente, seções de perfil |
get_recent_interactions | lista interações brutas recentes (filtra por tipo) — matéria-prima para o Smart Distill |
export_memory | exporta memória para JSON sob data/exports/ apenas (nome de arquivo auto-saneado) |
get_context | monta memórias relevantes para a tarefa atual via recuperação híbrida (+ expansão opcional do grafo) com orçamento de tokens |
consolidate | agrupa memórias semelhantes via similaridade de embeddings; opcionalmente cria memórias derivadas/consolidadas vinculadas via derived_from |
link_memory | cria uma relação tipada entre duas memórias no grafo (supports/contradicts/supersedes/derived_from/related_to/caused_by/depends_on) |
merge_memory | mescla uma duplicata/quase duplicata em uma memória canônica (a fonte se torna substituída, proveniência em metadata.merged_from) |
update_memory | atualiza campos mutáveis no lugar, ou cria uma memória substituta quando content muda (defina supersede=false para editar no lugar) |
import_memory | importa memórias de JSON (valida tipo, deduplica contra existentes, nunca sobrescreve cegamente); dry-run por padrão, apply=true para inserir |
extract_memories | escaneia interações capturadas recentes em busca de frases com intenção de memória e propõe candidatos de memória (determinístico, sem LLM); dry-run por padrão, apply=true para criar (fonte=capturada) |
CLI (th-memory)
Dois binários são enviados em package.json (bin):
| Binário | Entrada | Propósito |
|---|---|---|
th-memory-mcp | dist/index.js | servidor MCP (stdio) — as 16 ferramentas acima |
th-memory | dist/cli.js | CLI de memória local (zero dependências, compartilha o mesmo banco via MEMORY_DB_PATH) |
Uso: th-memory [--db <path>] [--json] [--plain] <command> [options] — ajuda por comando via th-memory <command> --help.
Flags globais (todo comando):
| Flag | Efeito |
|---|---|
--db <path> | usar este arquivo SQLite (define MEMORY_DB_PATH) |
--json | imprimir JSON {ok,data} em vez de texto simples |
--plain / --no-color | desativar cores |
-h, --help | mostrar ajuda (global ou por comando) |
-V, --version | imprimir versão |
Comandos:
| Comando | Uso |
|---|---|
remember | remember --category <c> --key <k> --value <v|-> — salvar uma preferência (--value - lê da entrada padrão) |
recall | recall <topic> [--limit <n>] [--highlight] — buscar memória |
forget | forget <id> [--type <t>] — excluir por id (t: memory|preference|lesson|interaction) |
export | export [--include-interactions] [--filename <n>] — exportar para data/exports/*.json |
import | import (--file <p>|--json <s>) [--apply] [--user-id <id>] — importar backup (simulação por padrão) |
stats | stats — estatísticas de memória |
profile | profile — perfil de usuário destilado |
history | history [--query <q>] [--limit <n>] — buscar prompts anteriores (sem consulta = prompts recentes) |
recent | recent [--limit <n>] [--kind <k>] — interações recentes (k: prompt|tool_call|error) |
highlight | highlight [text...] -q <topic> [--limit <n>] — destacar correspondências de tópicos (texto vazio = pipe da entrada padrão) |
Comportamento de destaque (comando highlight e recall --highlight): correspondências são envolvidas com sublinhado (ESC[4m…ESC[24m) quando a saída padrão é um TTY com cores ativadas; quando canalizado, com --json, ou com --plain/--no-color, correspondências são envolvidas com marcadores [mem]…[/mem].
Exemplos:
th-memory remember --category coding_pref --key package_manager --value pnpm
th-memory recall pnpm --limit 5
th-memory recall pnpm --highlight
echo "I prefer pnpm for installs" | th-memory highlight -q pnpm
Nota: --value - lê o valor da entrada padrão (ex.: echo -n "pnpm" | th-memory remember --category coding_pref --key package_manager --value -).
Instalação com OpenCode
- Mescle a seção
mcpdeopencode.example.jsonno seuopencode.json(global ou nível de projeto)- Importante: defina
MEMORY_DB_PATHpara o MESMO arquivo de banco de dados tanto para o servidor quanto para o plugin (o exemplo usa<ABSOLUTE_PATH>/th-memory-mcp/data/memory.db), caso contrário o plugin de captura automática grava em um banco diferente daquele que a IA lê - Como definir (escolha um):
- defina-o no
environmentdo mcp (veja o exemplo) — cobre apenas o servidor MCP - ou defina-o como variável de ambiente de nível de sistema/usuário (ex.:
setx MEMORY_DB_PATH "D:/path/to/memory.db"no Windows) — cobre servidor e plugin, já que o plugin roda no mesmo processo do OpenCode
- defina-o no
- Importante: defina
- Anexe as regras globais de memória — adicione a
opencode.json:
(o conteúdo de exemplo da regra está em"instructions": ["C:/Users/<user>/.config/opencode/memory-protocol.md"]AGENTS.memory.example.md— pode ser anexado no nível do projeto) - (Opcional) Implante o plugin de captura automática: copie
src/plugin/learning-capture.ts→~/.config/opencode/plugins/learning-capture.ts - Reinicie o OpenCode (a configuração é carregada apenas na inicialização)
- Teste: "Lembre que eu prefiro pnpm" → abra uma nova sessão e pergunte de volta
Uso diário
A IA aceita tailandês e inglês de forma intercambiável — você pode alternar de idioma a qualquer momento sem aviso.
| Exemplo de comando | Ferramenta / efeito |
|---|---|
| "Lembre que..." | remember — salvar uma preferência |
| "Resuma memória" / "destile memória" | Smart Distill — a IA lê get_recent_interactions, encontra padrões e salva insights por conta própria |
| "Como está minha memória?" / "status da memória" | memory_stats |
| "Exportar memória" / "backup de memória" | export_memory |
| "Buscar histórico..." | search_history |
| "Esquecer..." | forget |
Cuidado de longo prazo: execute npm run distill ocasionalmente para resumir estatísticas e podar interações mais antigas que 30 dias.
Estrutura data/
data/
├── memory.db # SQLite (WAL mode) — main DB (+ .db-wal, .db-shm)
└── exports/ # JSON files from export_memory (writeable only in this dir)
- O caminho do banco pode ser substituído pela variável de ambiente
MEMORY_DB_PATH - tudo em
data/é ignorado pelo git
Benchmark — auto-relatado internamente (não de terceiros)
⚠️ Benchmark auto-relatado internamente — não é benchmark de terceiros
- interno N-pequeno: 180 registros/30 tópicos (B.recuperação: 30 tópicos × 5 relevantes + 30 distratores = 180; execução completa também usa subconjuntos de armazenamento/temporal/contexto N-pequeno)
- auto-execução em máquina única: máquina de desenvolvimento única, sistema operacional/Node/build better-sqlite3 únicos — não é entre máquinas, não verificado de forma independente
- não é benchmark de terceiros: auto-relatado, não verificado de forma independente; não compare como se fosse de um avaliador externo
- O conjunto de dados e o harness estão em
repro/(comitável) ebenchmark/(framework completo, vejaTH_MEMORY_MCP_BENCHMARK_SPEC.mdebenchmark/README.md).
Dois modos
| Modo | Comando | Dados | Suítes | Caso de uso |
|---|---|---|---|---|
| Normal | npm run benchmark | 180 registros / 30 tópicos | recuperação | verificação rápida (<5s) |
| Pesado | npm run benchmark:heavy | 600 registros / 100 tópicos + escala 2k | todos (armazenamento/recuperação/temporal/contexto/desempenho/escalabilidade/frio/ablação) | estresse / regressão |
Reproduzir:
npm run build
# Normal — quick
npm run benchmark
npm run benchmark -- --k 10
npm run benchmark -- --out repro/results
# Heavy — full framework, more data
npm run benchmark:heavy
# or custom:
node benchmark/run.mjs --suite all --topics 100 --distractors 100 --scale 2000 --out benchmark/results
Visualizador — comparar as últimas 3 versões (tabela + gráficos)
npm run benchmark:viewer
# or: npx serve . -l 3000
# open http://localhost:3000/benchmark/viewer/ or http://localhost:3000/result/viewer.html
O visualizador carrega benchmark/results/history.jsonl, agrupa por versão, pega a execução mais recente das 3 versões mais recentes (ex.: 2.2.6 / 2.2.7 / 2.2.8) e mostra uma tabela destacada (1 linha por versão) + gráficos de barras para Recall@5 / MRR / NDCG@5 e Latency p95. Os resultados também são salvos por versão em result/v*_benchmark_result.md e benchmark/results/versions/<ver>/.
Última execução interna (v2.2.8, quente, perfil normal, a2dcbce — não de terceiros): Recall@5=0.9060, Precision@5=0.9060, MRR=1, NDCG@5=0.9347 em 100 tópicos/600 registros. Veja result/v2.2.8_benchmark_result.md e repro/README.md para detalhes e ressalvas (interno, auto-execução em máquina única).
Limitações conhecidas
- Sem criptografia em repouso (texto simples em repouso) —
data/memory.db(modo WAL,better-sqlite3) é um arquivo SQLite simples e não criptografado.100% local & privatesignifica sem exfiltração em nuvem ou rede — não significa criptografado em repouso. Qualquer pessoa com acesso ao sistema de arquivos (máquina compartilhada, backup, malware, dispositivo roubado) pode ler preferências/lições/interações em texto simples. Para dados sensíveis, use criptografia de disco completa em nível de sistema operacional (BitLocker / FileVault / LUKS) ou um build SQLCipher opcional (requer recompilação nativa e gerenciamento de chaves). Nenhum SQLCipher/criptografia em código é aplicado por padrão esrc/db/index.tsdocumenta isso explicitamente. - Processo local de usuário único — sem camada de autenticação (Lote B-3) —
userId/sessionId/projectIdsão strings fornecidas pelo chamador com nenhuma verificação de autenticação ou autorização dentro do servidor. O isolamento de escopo (recuperação,get_context, travessia/expansão de grafo,consolidate,link_memory,merge_memory) é aplicado apenas contra os valores que o chamador envia, então um chamador pode ler ou escrever qualquer escopo passando um id diferente. Isso é adequado para um processo local de usuário único (um operador,data/memory.dblocal). Não compartilhe um servidor/banco entre usuários mutuamente não confiáveis sem uma camada de autenticação na frente que autentique cada chamador e force ouserIdcorreto (eprojectId/sessionIdpermitidos). Nenhum novo sistema de autenticação é incorporado ao servidor por design — essa camada pertence na frente dele.
Licença
MIT © 2026 worakorn-prince
Este projeto é licenciado sob a Licença MIT — veja o arquivo LICENSE para o texto completo.