Memora
Um servidor MCP leve para armazenamento de memória semântica, grafos de conhecimento e contexto entre sessões
Documentação
Memora
"Você nunca sabe realmente o valor de um momento até que ele se torne uma memória."
Dê aos seus agentes de IA memória coletiva persistente
Uma camada de memória MCP para agentes: armazenamento estruturado, recuperação semântica, relações em grafo e contexto entre sessões com respaldo em fontes.
Absorva o trabalho do agente em memória de grafo durável e use memory_digest(topic) para recuperar memórias relevantes, TODOs/questões, arestas relacionadas e IDs de fonte.
Recursos · Pré-visualização · Instalação · Uso · Configuração · Multi-DB · Contêineres · Grafo ao Vivo · Grafo na Nuvem · Chat · Busca Semântica · Documentos · Dedup com LLM · Vinculação · Neovim
Recursos
Armazenamento Principal
- 💾 Armazenamento Persistente - SQLite com sincronização opcional na nuvem (S3, R2, D1)
- 🗄️ Roteamento multi-banco de dados - Um processo atende a vários armazenamentos; um workspace alcança o seu próprio em
/mcp/<name>(veja Roteamento multi-banco de dados) - 📂 Organização Hierárquica - Estrutura de seção/subseção com atribuição automática de hierarquia
- 📦 Exportar/Importar - Backup e restauração com estratégias de mesclagem
Absorção e Linhagem
- 🧬 Absorção - Alimente fatos; um LLM classifica cada um em relação ao armazenamento (duplicado / atualização / contradição / relacionado / novo), ignora duplicados, vincula relações e consolida fatos relacionados — com pré-visualização
dry_run - 🌱 Linhagem de Substituição - Atualizações substituem conhecimento antigo em vez de excluí-lo; a recuperação segue a cadeia até a versão atual por padrão (modos
follow:active,latest,full_history) - 🗞️ Resumo de Tópico -
memory_digest(topic)agrupa memórias relevantes, TODOs/questões abertos, arestas relacionadas e IDs de fonte em uma única recuperação
Busca e Inteligência
- 🔍 Busca Semântica - Embeddings vetoriais (TF-IDF, sentence-transformers, OpenAI)
- 🎯 Consultas Avançadas - Texto completo, intervalos de datas, filtros de tags (AND/OR/NOT), busca híbrida
- 🔀 Referências Cruzadas - Memórias relacionadas vinculadas automaticamente com base em similaridade
- 🤖 Deduplicação com LLM - Encontre e mescle duplicados com comparação alimentada por IA
- 🔗 Vinculação de Memórias - Arestas tipadas, aumento de importância e detecção de clusters
Armazenamento de Documentos
- 📄 Documentos Estruturados - Armazene documentos markdown como árvores de fragmentos pesquisáveis (afirmações, itens de plano, referências, riscos)
- 🔒 Integridade de Fragmentos - Protege contra exclusão/mesclagem/absorção acidental de fragmentos de documentos
- 🔍 Busca Granular - Afirmações e descobertas individuais são pesquisáveis semanticamente enquanto o documento completo permanece recuperável como uma unidade
Ferramentas e Visualização
- ⚡ Automação de Memória - Ferramentas estruturadas para TODOs, questões e seções
- 🕸️ Grafo de Conhecimento - Visualização interativa com renderização Mermaid e sobreposições de clusters
- 🌐 Servidor de Grafo ao Vivo - Servidor HTTP integrado com opção hospedada na nuvem (D1/Pages)
- 💬 Converse com Memórias - Painel de chat com RAG e chamada de ferramentas LLM para buscar, criar, atualizar e excluir memórias via chat em streaming
- 📡 Notificações de Eventos - Sistema baseado em polling para comunicação entre agentes
- 📊 Estatísticas e Análises - Uso de tags, tendências e insights de conexões
- 🧠 Insights de Memória - Resumo de atividade, detecção de obsolescência, sugestões de consolidação e análise de padrões alimentada por LLM
- 📜 Histórico de Ações - Acompanhe todas as operações de memória (criar, atualizar, excluir, mesclar, aumentar, vincular) com visualização de linha do tempo agrupada
Pré-visualização
Instalação
Dois caminhos. pip é um filho stdio local que o cliente inicia. Um contêiner é um serviço HTTP destacado que você inicia com up; com MEMORA_DATABASES ele atende a vários armazenamentos a partir de um único processo. O LaunchAgent supervisiona o proxy, não o contêiner — após uma reinicialização do host, o ouvinte pode voltar enquanto seu upstream ainda está parado. Se você está executando memora como um serviço, o caminho do contêiner é a instalação.
pip (local / stdio)
pip install memora-mcp
O pacote PyPI é memora-mcp (memora puro no PyPI é um projeto não relacionado). Inclui armazenamento em nuvem (S3/R2) e embeddings OpenAI prontos para uso.
# Optional: local embeddings (offline, ~2GB for PyTorch)
pip install "memora-mcp[local]"
# Latest development version straight from git
pip install "git+https://github.com/agentic-box/memora.git"
Em seguida, inicie-o a partir de .mcp.json com "command": "memora-server" (veja Configuração).
Contêiner (serviço HTTP)
O runtime padrão é o CLI container da Apple. Cada operação de contêiner que scripts/memora-instance.sh executa (build, up, status, logs, down) usa $MEMORA_CONTAINER_BIN (padrão container). O processo proxy gerado não usa; ele codifica container list.
Antes do primeiro build:
-
Instale o CLI
containerda Apple (pkg assinado dos seus releases no GitHub). Ele precisa de um Mac com silício da Apple executando macOS 26 — a Apple não suporta versões mais antigas do macOS paracontainer. -
Inicie o runtime — o primeiro comando documentado pela Apple, que também instala um kernel se nenhum estiver configurado:
container system start -
Clone este repositório e
cdnele:git clone https://github.com/agentic-box/memora.git cd memora -
Copie o modelo de instância. Ele vem com
INSTANCE=myinstancepara que as linhasbuild/up/proxyposteriores correspondam sem renomear. EditePORTe um backend (STORAGE_URI,VOLUMEouMEMORA_DATABASES):cp instances/example.env instances/myinstance.env -
Crie o arquivo de credenciais e instale o proxy que o LaunchAgent executará.
cred_args()requer um.mcp.jsoncujomcpServers.memora.envcontémCLOUDFLARE_API_TOKEN(acesso D1) e as chaves de embedding/LLM —upfalha se esse arquivo estiver ausente. O script procura por~/.config/memora/credentials.mcp.jsonse esse arquivo existir, caso contrário~/repos/agentic-box/.mcp.json. DefinaCRED_SOURCEno arquivo de instância para escolher um caminho. Separadamente,proxyrenderiza um plist cujo executável é$MEMORA_PROXY_BIN(padrão~/.local/libexec/memora/memora_proxy.py) e cujos logs ficam em$MEMORA_LOG_DIR(padrão~/.local/var/log) — nada cria qualquer um deles em um clone novo.mkdir -p ~/.config/memora ~/.local/libexec/memora ~/.local/var/log cp scripts/memora_proxy.py ~/.local/libexec/memora/ # real values; any key is fine, an absent file is not # the default umask is permissive -- chmod 600 keeps other local accounts out cat > ~/.config/memora/credentials.mcp.json <<'JSON' {"mcpServers":{"memora":{"env":{"CLOUDFLARE_API_TOKEN":"REPLACE","OPENAI_API_KEY":"REPLACE"}}}} JSON chmod 600 ~/.config/memora/credentials.mcp.jsonEsse JSON é a configuração mínima correta: tanto o LLM quanto os embeddings usam o host padrão da OpenAI com uma chave OpenAI real. Não adicione
OPENAI_BASE_URLapontando para OpenRouter sem o par de embeddings de Embeddings — OpenRouter não tem endpoint de embeddings, toda chamada de embed retorna 404, e memora silenciosamente recorre a sacos de palavras TF-IDF enquanto parece saudável.
Em seguida:
./scripts/memora-instance.sh build myinstance # tags IMAGE from myinstance.env (memora-pilot if IMAGE is unset)
./scripts/memora-instance.sh up myinstance # runs that same IMAGE
./scripts/memora-instance.sh proxy myinstance # render the LaunchAgent; run the printed launchctl
up não publica uma porta do host. O ouvinte ao qual o workspace se conecta é o proxy. proxy apenas renderiza um LaunchAgent do macOS e imprime os comandos launchctl — ele não carrega o serviço. Execute esses comandos impressos.
A URL do workspace impressa é sempre http://127.0.0.1:<PORT>/mcp (o padrão do registro). Para um armazenamento não padrão, acrescente /<name> você mesmo — um /mcp puro em um registro vincula silenciosamente MEMORA_DEFAULT_DB:
{"mcpServers": {"memora": {"type": "http", "url": "http://127.0.0.1:<PORT>/mcp/<store>"}}}
Justificativa do proxy, credenciais, arquivos de instância e MEMORA_CONTAINER_BIN: Implantação de Contêiner.
Uso
O servidor é executado automaticamente quando configurado no Claude Code. Invocação manual:
# Default (stdio mode for MCP)
memora-server
# With graph visualization server
memora-server --graph-port 8765
# HTTP transport (alternative to stdio)
memora-server --transport streamable-http --host 127.0.0.1 --port 8080
Configuração
Claude Code
Adicione a .mcp.json na raiz do seu projeto:
Banco de dados local:
{
"mcpServers": {
"memora": {
"command": "memora-server",
"args": [],
"env": {
"MEMORA_DB_PATH": "~/.local/share/memora/memories.db",
"MEMORA_ALLOW_ANY_TAG": "1",
"MEMORA_GRAPH_PORT": "8765"
}
}
}
}
Banco de dados na nuvem (Cloudflare D1) - Recomendado:
{
"mcpServers": {
"memora": {
"command": "memora-server",
"args": ["--no-graph"],
"env": {
"MEMORA_STORAGE_URI": "d1://<account-id>/<database-id>",
"CLOUDFLARE_API_TOKEN": "<your-api-token>",
"MEMORA_ALLOW_ANY_TAG": "1"
}
}
}
}
Com D1, use --no-graph para desabilitar o servidor de visualização local. Em vez disso, use o grafo hospedado na sua URL do Cloudflare Pages (veja Grafo na Nuvem).
Banco de dados na nuvem (S3/R2) - Modo de sincronização:
{
"mcpServers": {
"memora": {
"command": "memora-server",
"args": [],
"env": {
"AWS_PROFILE": "memora",
"AWS_ENDPOINT_URL": "https://<account-id>.r2.cloudflarestorage.com",
"MEMORA_STORAGE_URI": "s3://memories/memories.db",
"MEMORA_CLOUD_ENCRYPT": "true",
"MEMORA_ALLOW_ANY_TAG": "1",
"MEMORA_GRAPH_PORT": "8765"
}
}
}
}
Codex CLI
Adicione a ~/.codex/config.toml:
[mcp_servers.memora]
command = "memora-server" # or full path: /path/to/bin/memora-server
args = ["--no-graph"]
env = {
AWS_PROFILE = "memora",
AWS_ENDPOINT_URL = "https://<account-id>.r2.cloudflarestorage.com",
MEMORA_STORAGE_URI = "s3://memories/memories.db",
MEMORA_CLOUD_ENCRYPT = "true",
MEMORA_ALLOW_ANY_TAG = "1",
}
Variáveis de Ambiente
| Variável | Descrição |
|---|---|
MEMORA_DB_PATH | Caminho local do banco de dados SQLite (padrão: ~/.local/share/memora/memories.db) |
MEMORA_STORAGE_URI | URI de armazenamento: d1://<account>/<db-id> (D1) ou s3://bucket/memories.db (S3/R2). Usado quando MEMORA_DATABASES não está definido. |
MEMORA_DATABASES | Objeto JSON {name: uri} mapeando cada armazenamento que este processo atende. Os nomes são um segmento de caminho de URL (/mcp/<name>): letras, dígitos, -, _, . apenas. Chaves duplicadas, valores vazios, nomes inseguros ou não-objetos recusam a inicialização em vez de escolher silenciosamente um armazenamento. Não definido = armazenamento único (legado). Consulte Roteamento de múltiplos bancos de dados. |
MEMORA_DEFAULT_DB | Nome do registro que um /mcp simples usa. Obrigatório quando o registro tem mais de um banco de dados; com exatamente um nome, esse nome é o padrão. Um valor que não está no registro recusa a inicialização. |
CLOUDFLARE_API_TOKEN | Token de API para D1 (URI d1://). CF_API_TOKEN é aceito como alias. |
MEMORA_CLOUD_ENCRYPT | Criptografa o arquivo local antes de enviar para S3/R2. Não definido/false = desativado; 1/true/yes = ativado. |
MEMORA_CLOUD_COMPRESS | Comprime o arquivo local antes de enviar para S3/R2. Não definido/false = desativado; 1/true/yes = ativado. |
MEMORA_CACHE_DIR | Diretório de cache local para um banco de dados sincronizado com S3/R2. Não definido: o backend escolhe um caminho de cache. |
MEMORA_ALLOW_ANY_TAG | Permite qualquer tag sem validação contra a lista de permissões (1 para ativar) |
MEMORA_TAG_FILE | Caminho para um arquivo JSON contendo uma matriz de tags permitidas, ex.: ["plan", "memora/issues"] |
MEMORA_TAGS | Lista de tags permitidas separadas por vírgula |
MEMORA_HOST | Endereço de bind para transportes HTTP (padrão 127.0.0.1). Substituível com --host. |
MEMORA_PORT | Porta de bind para transportes HTTP (padrão 8000). Substituível com --port. |
MEMORA_GRAPH_PORT | Porta para o servidor de visualização do grafo de conhecimento (padrão: 8765) |
MEMORA_TRANSPORT | stdio (padrão), sse ou streamable-http. Um valor env desconhecido cai para stdio; --transport ainda rejeita valores desconhecidos. O roteamento de múltiplos bancos de dados e a proteção de sessão são executados apenas em streamable-http. |
MEMORA_TOOL_PROFILE | Subconjunto de ferramentas exposto aos clientes: full (padrão, todas as 43), leader (19), agent (12). Não definido/vazio = full; um valor desconhecido recusa a inicialização. Consulte Perfis de Ferramentas. |
MEMORA_MAX_SESSIONS | Limite máximo de sessões MCP simultâneas (padrão 128). 0 desativa. Uma taxa de criação mais um tempo limite de inatividade não é um limite — um cliente que mantém IDs de sessão ativos pode crescer sem limite na taxa de criação. Valores inválidos recusam a inicialização. Apenas Streamable-HTTP. |
MEMORA_MAX_INIT_PER_MIN | Novas sessões admitidas por minuto (padrão 120). 0 desativa. Valores inválidos recusam a inicialização. Apenas Streamable-HTTP. |
MEMORA_MAX_INIT_BODY_BYTES | Tamanho máximo do corpo da solicitação de inicialização aceito/armazenado em buffer (padrão 65536, mínimo 1024). Solicitações maiores recebem 413. Valores inválidos recusam a inicialização. Apenas Streamable-HTTP. |
MEMORA_SESSION_IDLE_TIMEOUT | Segundos antes que uma sessão válida abandonada seja coletada (padrão 1800). 0 desativa. Valores inválidos recusam a inicialização. Apenas Streamable-HTTP. |
MEMORA_HEALTH_TOKEN | Token Bearer para corpos /health/db detalhados (nomes, contagens, texto de erro). Não definido: apenas um peer de loopback vê detalhes; todos os outros recebem status agregado. FastMCP custom_route() não é autenticado mesmo quando a autenticação MCP está configurada. Apenas transportes HTTP (memora.health é importado para SSE/streamable-http, não stdio). |
MEMORA_HEALTH_TTL | Segundos que um snapshot de prontidão pode ser servido antes que uma atualização seja devida (padrão 10, limite 3600). Deve ser > 0. Valores inválidos recusam a inicialização. Apenas transportes HTTP — um valor malformado não aborta stdio. |
MEMORA_HEALTH_TIMEOUT | Limite em uma passagem de atualização e em cada sondagem de armazenamento (padrão 15, limite 300). Deve ser > 0. Apenas transportes HTTP. |
MEMORA_HEALTH_REFRESH_INTERVAL | Com que frequência o servidor atualiza a prontidão por conta própria (padrão 15, limite 3600). 0 = somente sondagem. Sem isso, uma implantação de proxy não tem chamador de loopback e a superfície de alerta permanece unknown enquanto todos os bancos de dados estão OK. Quando a atualização periódica está ativada, interval + timeout deve ser < MEMORA_HEALTH_MAX_STALE. Apenas transportes HTTP. |
MEMORA_HEALTH_MAX_STALE | Idade após a qual um resultado por banco de dados em cache não pode mais ser relatado como pronto (padrão 60, limite 3600). Deve ser >= MEMORA_HEALTH_TTL. Apenas transportes HTTP. |
MEMORA_STALE_DAYS | Dois consumidores, dois padrões, mesmo nome: memory_insights trata um TODO/issue aberto como desatualizado após 14 dias; a UI do grafo esmaece itens fechados após 30 dias. Defina a variável para substituir ambos. |
MEMORA_EMBEDDING_MODEL | Backend de embeddings: openai (padrão), sentence-transformers ou tfidf |
SENTENCE_TRANSFORMERS_MODEL | Modelo para sentence-transformers (padrão: all-MiniLM-L6-v2) |
MEMORA_EMBEDDING_API_KEY | Chave de API do provedor de embeddings (atômica com URL base — veja abaixo) |
MEMORA_EMBEDDING_BASE_URL | URL base do provedor de embeddings (atômica com chave de API — veja abaixo) |
MEMORA_EMBEDDING_STRICT | Recomendamos 1. Falhe de forma rígida em erros de embeddings em vez de TF-IDF silencioso. Sem isso, um endpoint quebrado continua respondendo enquanto cada vetor se torna um saco de palavras-chave (como 756 memórias degradaram despercebidas). |
OPENAI_API_KEY | Somente LLM (dedup/chat) quando MEMORA_EMBEDDING_* está definido. Embeddings caem para esta chave apenas se ambos MEMORA_EMBEDDING_API_KEY e MEMORA_EMBEDDING_BASE_URL não estiverem definidos |
OPENAI_BASE_URL | URL base do LLM (OpenRouter, Azure, etc.). Mesma regra de fallback atômica que a chave — não é uma URL de embeddings quando você usa uma configuração dividida |
OPENAI_EMBEDDING_MODEL | ID do modelo para o backend de embeddings openai. Deve existir no host de embeddings (padrão text-embedding-3-small é somente OpenAI; Cloudflare precisa, ex.: @cf/baai/bge-m3) |
MEMORA_LLM_ENABLED | Ativa a comparação de deduplicação alimentada por LLM (true/1/yes; padrão: true) |
MEMORA_LLM_MODEL | Modelo para comparação de deduplicação e, se não definido, para reescrita de consulta e chat local (padrão: gpt-4o-mini) |
MEMORA_LLM_TIMEOUT | Segundos que o cliente OpenAI espera (padrão 60, mínimo de 1). Um valor não numérico cai para 60. |
MEMORA_REWRITE_MODEL | Modelo para reescrita de consulta RAG no painel de chat do grafo. Não definido/vazio usa MEMORA_LLM_MODEL. |
MEMORA_VECTOR_SCAN_PAGE_SIZE | Linhas por página ao carregar embeddings do D1 (padrão 1000; não numérico ou <1 cai para 1000; limite máximo 10000). No padrão, um armazenamento com menos de 1000 linhas retorna o corpus inteiro mais cada embedding em uma resposta D1, o que competiu com o limite de 30s por solicitação da Cloudflare e fez memory_absorb falhar completamente. Use 100 no D1 (o script da instância já injeta isso). Paginação é uma mitigação, não a correção: absorb lê o corpus uma vez por chamada e reutiliza um cache local ao processo chaveado no embedding_change_epoch monotônico do banco. |
CHAT_MODEL | Modelo para o painel de chat do grafo local. Não definido/vazio cai para MEMORA_LLM_MODEL. (O padrão deepseek/deepseek-chat é Cloudflare Pages wrangler.toml, não este processo.) |
MEMORA_CLOUD_GRAPH_ENABLED | true/1/yes para notificar o grafo hospedado sobre gravações (padrão desativado). |
MEMORA_CLOUD_GRAPH_WORKER_URL | URL base do worker para essas transmissões (POST <url>/broadcast). Não definido: transmissões são ignoradas. |
MEMORA_CLOUD_GRAPH_DEBOUNCE | Segundos para agrupar gravações rápidas antes de transmitir (padrão 1.0). |
MEMORA_CLOUD_GRAPH_SYNC_SCRIPT | Caminho capturado na inicialização (padrão: memora-graph/scripts/sync.sh se esse arquivo existir). O caminho de gravação atual não executa este script — D1 é a fonte da verdade e apenas a transmissão do worker é executada. |
AWS_PROFILE | Perfil de credenciais AWS de ~/.aws/credentials (útil para R2) |
AWS_ENDPOINT_URL | Endpoint compatível com S3 para R2/MinIO |
R2_PUBLIC_DOMAIN | Domínio público para URLs de imagens R2 |
Perfis de Ferramentas (MEMORA_TOOL_PROFILE)
Todas as 43 ferramentas MCP são registradas incondicionalmente, então cada sessão de agente é injetada com o esquema completo de ferramentas de ~12.700 tokens, mesmo quando a maioria das ferramentas nunca é chamada. MEMORA_TOOL_PROFILE expõe um subconjunto por implantação, de modo que uma ferramenta restrita está genuinamente ausente — faltando em tools/list E não despachável (call_tool retorna unknown-tool, não uma execução oculta). O perfil é aplicado e atestado na inicialização; o perfil ativo e a contagem de ferramentas expostas são registrados em stderr.
| Valor | Ferramentas | Uso |
|---|---|---|
full (padrão) | todas as 43 | Uso direto via stdio; toda implantação existente permanece byte por byte inalterada |
leader | 19 | O conjunto de agente mais memory_create_section, memory_store_document, memory_get_document, memory_tags, memory_delete, memory_digest, memory_list |
agent | 12 | A superfície de leitura/criação que um agente trabalhador precisa: memory_absorb, memory_semantic_search, memory_hybrid_search, memory_list_compact, memory_get, memory_related, memory_link, memory_stats, memory_create, memory_create_issue, memory_create_todo, memory_update |
- Não definido / vazio =
full. Nenhuma implantação existente muda de comportamento. - Um valor desconhecido aborta a inicialização com uma mensagem listando os valores válidos. Ele nunca cai silenciosamente para
full— um erro de digitação não deve reexpor ferramentas de manutenção destrutivas (memory_rebuild_embeddings,memory_delete_batch) a todos os workers. Falha fechada. memory_listestá emleader, mas não emagent. Foi excluído de ambos enquanto custava 163-174s em um armazenamento D1 contra os 0,22s dememory_list_compact; #973 corrigiu isso (agora ~1,1s). Ele permanece fora deagentporque a superfície de leitura de um worker é deliberadamente estreita, não por velocidade.- A fronteira líder/agente é dados em
memora/tool_profile.py(dois frozensets). Editá-la é uma linha, não uma varredura de 43 decoradores. - O prune exclui do dict privado
_tool_manager._toolsdo FastMCP, entãomemorafixamcp>=1.27,<1.28(o minor auditado) e executa uma atestação de inicialização através dos manipuladores de requisição MCP registrados de baixo nível (_mcp_server.request_handlers[ListToolsRequest]/[CallToolRequest]— o callable de despacho real que requisições de clientes usam, não os helpers PythonFastMCP.list_tools/call_tool) que se recusa a iniciar se o SDK instalado rotear listagem/despacho para outro lugar (deriva de implementação privada). O pin é a proteção estática; a atestação é o backstop em tempo de execução. Aumentar o limite superior exige reexecutartests/test_tool_profile.py. - Sob implantação em contêiner, o perfil é por contêiner, enquanto os papéis são por agente. Um contêiner servindo o líder de um workspace e seus workers precisa do superconjunto líder;
agentremoveriacreate_section/store_document/delete/digest/tagsdo líder. memora-server(ou seja,memora.server.main()) é o único caminho de serviço com perfil suportado. Um embedder direto que importamemora.server.mcpe chamamcp.run()por conta própria ignora o perfilamento completamente (omcpglobal ainda contém todas as 43 ferramentas); embedders que desejam perfilamento devem chamarapply_tool_profilepor conta própria ou usarmain().
# Leader deployment — exposes 19 tools
MEMORA_TOOL_PROFILE=leader memora-server
# Agent worker — exposes 12 tools
MEMORA_TOOL_PROFILE=agent memora-server
# Full (default) — all 43 tools, existing behaviour
memora-server
# Typo refuses to start:
# MEMORA_TOOL_PROFILE=agnt memora-server
# Error: unknown MEMORA_TOOL_PROFILE='agnt'; valid values: full, leader, agent
Roteamento multi-banco de dados
Um processo memora pode servir todos os workspaces. MEMORA_DATABASES é um registro
JSON de {name: storage URI}; um cliente alcança seu armazenamento em /mcp/<name>.
O seletor é a URL já em .mcp.json, não um argumento de ferramenta — um db
opcional em cada ferramenta são 43 chances de esquecer um, e cada erro escreveria no
armazenamento de outra pessoa.
MEMORA_DATABASES não definido é o formato antigo: um backend de MEMORA_STORAGE_URI
/ MEMORA_DB_PATH, um /mcp. Implantações stdio existentes não mudam.
Roteamento (streamable-http apenas):
| URL | Resolve para |
|---|---|
/mcp/<name> | Essa entrada do registro. Nomes desconhecidos retornam 404 {"error":"unknown database"} — o corpo não lista os outros nomes. |
/mcp | MEMORA_DEFAULT_DB. Obrigatório quando o registro tem mais de um banco de dados; um registro de nome único usa esse nome. |
A vinculação é fixa por sessão MCP, não por requisição. Uma sessão aberta em
/mcp/alpha e reutilizada contra /mcp/beta ainda resolve para alpha. Um cliente
não pode trocar de banco de dados no meio de uma conversa.
Configuração malformada se recusa a iniciar (não cai para o banco de dados
legado): JSON inválido, um não-objeto, chaves duplicadas, uma URI vazia, um nome
que não é um segmento de caminho de URL, ou MEMORA_DEFAULT_DB ausente/desconhecido quando
mais de um banco de dados está listado.
Par funcional — execute isto, conecte-se a isto. Um listener streamable-HTTP, não
uma entrada MCP command (isso geraria um filho stdio que nunca fala MCP
no stdio). As credenciais vivem no processo do servidor.
MEMORA_DATABASES='{"memora":"d1://<account-id>/<memora-db-id>","ob1":"d1://<account-id>/<ob1-db-id>"}' \
MEMORA_DEFAULT_DB=memora \
CLOUDFLARE_API_TOKEN='<token>' \
MEMORA_VECTOR_SCAN_PAGE_SIZE=100 \
memora-server --transport streamable-http --host 127.0.0.1 --port 8000 --no-graph
{
"mcpServers": {
"memora": {
"type": "http",
"url": "http://127.0.0.1:8000/mcp/ob1"
}
}
}
Variante contêiner / proxy (o lançador usual deste host, não o comando
acima): scripts/memora-instance.sh up myinstance inicia o mesmo servidor HTTP
dentro de um contêiner e coloca scripts/memora_proxy.py em 127.0.0.1:<PORT>
(8910 para a instância memora). A URL do workspace é então
http://127.0.0.1:8910/mcp/ob1. Veja Implantação em Contêiner.
Um registro pode misturar d1://, s3:// e caminhos locais; parse_backend_uri
despacha com base no esquema.
memory_stats relata o banco de dados vinculado. Ele retorna database (o nome
que esta sessão realmente resolveu) e database_source (path,
registry_default ou unconfigured). Um nome válido, mas errado, em .mcp.json
é de outra forma indetectável: toda ferramenta funciona, leituras são bem-sucedidas e escritas
caem silenciosamente no armazenamento de outro projeto. Chame memory_stats e verifique database
contra o workspace que você pretendia.
Saúde de um processo multi-banco de dados: GET /health é liveness (sem I/O de banco de dados
— o único sinal em que um supervisor pode reiniciar). GET /health/db é uma superfície
de alerta (sempre HTTP 200; status é ok, degraded, unknown — sem
snapshot ainda, uma atualização expirou, ou evidência mais antiga que a máxima obsolescência — ou
error se o próprio registro está inutilizável). GET /health/db/{name} é a
sonda específica do workspace (200 ou 503). Retirar todo o processo porque
um armazenamento está degradado derruba os saudáveis junto.
Implantação em Contêiner
Com MEMORA_DATABASES não definido, um processo ainda vincula um banco de dados por
sua vida útil (MEMORA_STORAGE_URI / MEMORA_DB_PATH). Esse é o formato original
um-armazenamento-um-contêiner-uma-porta.
Com MEMORA_DATABASES definido, um contêiner serve todos os workspaces e
clientes selecionam um armazenamento pelo caminho da URL (/mcp/<name>). Veja
Roteamento multi-banco de dados. scripts/memora-instance.sh
quer um de STORAGE_URI, VOLUME ou MEMORA_DATABASES por arquivo de instância
(load() exige pelo menos um). Se mais de um estiver definido, cmd_up usa
MEMORA_DATABASES, depois STORAGE_URI, depois VOLUME.
Dockerfile constrói uma imagem sem credenciais; scripts/memora-instance.sh implanta uma
instância de instances/myinstance.env (ou outro arquivo nomeado). O CLI de tempo de execução do script é
$MEMORA_CONTAINER_BIN (padrão container — o CLI da Apple). Cada operação
de contêiner que o script executa honra essa substituição (build, up, status,
logs, down). O processo memora_proxy.py gerado codifica
container list, que também é o motivo do proxy existir: esse tempo de execução reatribui
o IP do contêiner a cada início.
./scripts/memora-instance.sh build myinstance # build the image
./scripts/memora-instance.sh up myinstance # run the container
./scripts/memora-instance.sh proxy myinstance # render a LaunchAgent + print install commands
./scripts/memora-instance.sh status # every instance at a glance
Então aponte o workspace para ele — a configuração completa do cliente, sem segredos nela.
Uma instância de registro precisa do armazenamento no caminho (/mcp/<name>); /mcp puro é
o padrão do registro:
{"mcpServers": {"memora": {"type": "http", "url": "http://127.0.0.1:8910/mcp/ob1"}}}
Credenciais nunca entram na imagem, no arquivo de instância ou na configuração HTTP do
workspace. Elas são lidas em tempo de execução de uma configuração de credenciais separada
($CRED_SOURCE — em si um .mcp.json contendo apenas o bloco mcpServers.memora.env)
e injetadas com -e. Se o arquivo de instância não definir
CRED_SOURCE, o script usa ~/.config/memora/credentials.mcp.json quando esse
arquivo existe, caso contrário ~/repos/agentic-box/.mcp.json. Passe todas as
variáveis que esse arquivo define, não algumas escolhidas a dedo: um contêiner iniciado com apenas
as chaves de incorporação perde silenciosamente a consolidação LLM de memory_absorb em vez de
falhar ruidosamente.
Por que o proxy existe — leia isto antes de decidir que você não precisa dele. O
tempo de execução padrão (container da Apple) reatribui o IP de um contêiner a cada início, não apenas na recriação.
Um cliente MCP lê sua configuração uma vez na inicialização, então um endereço movido não produz um
erro: produz um travamento silencioso permanente. scripts/memora_proxy.py mantém um
127.0.0.1:<PORT> estável na frente do endereço móvel e re-resolve por conexão.
Dois modos de falha que ele distingue, que custaram uma interrupção para aprender:
- A consulta executou e o contêiner não está listado → ele realmente se foi. Recuse.
- A consulta não pôde executar (timeout sob pressão de memória do host) → nada de novo é
conhecido. Continue servindo o último endereço bom conhecido, limitado por
MEMORA_PROXY_STALE_GRACE(300s). Conflitar os dois derrubou todos os workspaces enquanto os contêineres estavam respondendo normalmente em endereços inalterados.
Defina MEMORA_TOOL_PROFILE por instância (veja Perfis de Ferramentas). Observe que o perfil é
por contêiner, enquanto os papéis são por agente: se um contêiner serve o líder de um workspace
e seus workers, ele precisa do superconjunto líder.
Variável de script em tempo de implantação (não uma variável de ambiente do memora-server — ela nunca chega ao processo dentro do contêiner):
| Variável | Significado |
|---|---|
MEMORA_CONTAINER_BIN | CLI que toda operação de contêiner memora-instance.sh usa (build, up, status, logs, down; padrão container). O processo memora_proxy.py gerado não honra isto; ele codifica container list. |
instances/README.md cobre os campos de configuração e launchd/README.md o proxy
supervisionado. REVERT.md documenta a restauração de um workspace para o servidor stdio direto.
Busca Semântica e Embeddings
Memora suporta três backends de embeddings:
| Backend | Instalação | Qualidade | Velocidade |
|---|---|---|---|
openai (padrão) | Incluído | Alta qualidade | Latência de API |
sentence-transformers | pip install memora[local] | Boa, roda offline | Média |
tfidf | Incluído | Correspondência básica de palavras-chave | Rápida |
Embeddings e o LLM são configurados separadamente.
| Papel | Variáveis |
|---|---|
| LLM (deduplicação, chat) | OPENAI_API_KEY + OPENAI_BASE_URL |
| Embeddings | MEMORA_EMBEDDING_API_KEY + MEMORA_EMBEDDING_BASE_URL (ambos ou nenhum — par atômico) |
| Fallback | Se ambos MEMORA_EMBEDDING_* estiverem não definidos, embeddings usam o par completo OPENAI_* |
Uma divisão parcial (apenas um MEMORA_EMBEDDING_* definido) é rejeitada para que o segredo de um provedor nunca seja enviado a outro host.
Armadilha — OpenRouter não tem endpoint de embeddings. O catálogo do OpenRouter é apenas chat/multimodal (sem modelos de embedding). Não aponte o caminho de embeddings para o OpenRouter via OPENAI_BASE_URL (ou uma URL base MEMORA). Essa combinação retorna 404 em toda chamada de embed; sem MEMORA_EMBEDDING_STRICT=1, Memora cai para TF-IDF e continua respondendo, então o armazenamento se enche de sacos de palavras-chave enquanto parece saudável. OpenRouter continua bom apenas para o LLM.
Exemplo funcional (LLM via OpenRouter, embeddings via Cloudflare Workers AI):
@cf/baai/bge-m3 é 1024-dimensional. O token precisa de permissão Workers AI. Formato do endpoint:
https://api.cloudflare.com/client/v4/accounts/<account_id>/ai/v1
{
"env": {
"MEMORA_EMBEDDING_MODEL": "openai",
"OPENAI_API_KEY": "<openrouter-key>",
"OPENAI_BASE_URL": "https://openrouter.ai/api/v1",
"MEMORA_LLM_MODEL": "deepseek/deepseek-chat",
"MEMORA_EMBEDDING_API_KEY": "<cloudflare-api-token-with-workers-ai>",
"MEMORA_EMBEDDING_BASE_URL": "https://api.cloudflare.com/client/v4/accounts/<account_id>/ai/v1",
"OPENAI_EMBEDDING_MODEL": "@cf/baai/bge-m3",
"MEMORA_EMBEDDING_STRICT": "1"
}
}
O que esta correção faz (sem exageros): embeddings e LLM podem usar provedores diferentes; uma divisão parcial é rejeitada; o modo estrito transforma degradação silenciosa em uma falha dura e nomeada.
Automático: Embeddings e referências cruzadas são calculados automaticamente quando você memory_create, memory_update ou memory_create_batch.
Reconstrução manual necessária quando a impressão digital do armazenamento muda — não apenas MEMORA_EMBEDDING_MODEL, mas também:
- Endpoint de embeddings (
MEMORA_EMBEDDING_BASE_URL/ host) - ID real do modelo (
OPENAI_EMBEDDING_MODEL, por exemplo, mudar para@cf/baai/bge-m3) - Tipo ou dimensões do vetor (sacos TF-IDF de palavras-chave vs densos 1024-d; ou 384 vs 1024)
- Armazenamento misto (algumas linhas densas, algumas esparsas) — similaridade de cosseno só compartilha chaves, então tipos mistos resultam em recall 0.0 para linhas antigas
Formato da impressão digital: backend|model|repr (por exemplo, openai|@cf/baai/bge-m3|dense:1024). O valor de meta legado openai sozinho é tratado como incompatibilidade.
# After changing embedding model/endpoint, rebuild all embeddings
memory_rebuild_embeddings
# Then rebuild cross-references to update the knowledge graph
memory_rebuild_crossrefs
Servidor de Grafo ao Vivo
Um servidor HTTP integrado inicia automaticamente junto com o servidor MCP, fornecendo uma visualização interativa do grafo de conhecimento.
![]() Painel de Detalhes | ![]() Painel de Linha do Tempo |
Acesso local:
http://localhost:8765/graph
Acesso remoto via SSH:
ssh -L 8765:localhost:8765 user@remote
# Then open http://localhost:8765/graph in your browser
Configuração:
{
"env": {
"MEMORA_GRAPH_PORT": "8765"
}
}
Para desativar: adicione "--no-graph" aos argumentos na sua configuração MCP.
Recursos da Interface do Grafo
- Painel de Detalhes - Visualize conteúdo de memórias, metadados, tags e memórias relacionadas
- Painel de Linha do Tempo - Navegue pelas memórias cronologicamente, clique para destacar no grafo
- Painel de Histórico - Registro de ações de todas as operações com entradas consecutivas agrupadas e referências de memória clicáveis (memórias excluídas mostradas com tachado)
- Painel de Chat - Faça perguntas sobre suas memórias usando chat com LLM baseado em RAG, com respostas em streaming e referências
[Memory #ID]clicáveis - Controle Deslizante de Tempo - Filtre memórias por intervalo de datas, arraste para explorar o histórico
- Atualizações em Tempo Real - Grafo, linha do tempo e histórico atualizam via SSE quando as memórias mudam
- Filtros - Menus suspensos de tag/seção, controles de zoom
- Renderização Mermaid - Blocos de código são renderizados como diagramas
Cores dos Nós
- 🟣 Tags - Tons de roxo por tag
- 🔴 Problemas - Vermelho (aberto), Laranja (em andamento), Verde (resolvido), Cinza (não será corrigido)
- 🔵 TODOs - Azul (aberto), Laranja (em andamento), Verde (concluído), Vermelho (bloqueado)
O tamanho do nó reflete o número de conexões.
Grafo na Nuvem (Recomendado para D1)
Ao usar Cloudflare D1 como seu banco de dados, a visualização do grafo é hospedada no Cloudflare Pages - sem necessidade de servidor local.
Benefícios:
- Acesso de qualquer lugar (sem túnel SSH)
- Atualizações em tempo real via WebSocket
- Suporte a múltiplos bancos de dados via parâmetro
?db= - Acesso seguro com Cloudflare Zero Trust
Configuração:
-
Crie o banco de dados D1:
npx wrangler d1 create memora-graph npx wrangler d1 execute memora-graph --file=memora-graph/schema.sql -
Implante o Pages:
cd memora-graph npx wrangler pages deploy ./public --project-name=memora-graph -
Configure os bindings no painel do Cloudflare:
- Pages → memora-graph → Configurações → Bindings
- Adicione D1:
DB_MEMORA→ seu banco de dados - Adicione R2:
R2_MEMORA→ seu bucket (para imagens)
-
Configure o MCP com o URI D1:
{ "env": { "MEMORA_STORAGE_URI": "d1://<account-id>/<database-id>", "CLOUDFLARE_API_TOKEN": "<your-token>" } }
Acesso: https://memora-graph.pages.dev
Proteja com Zero Trust:
- Painel do Cloudflare → Zero Trust → Access → Applications
- Adicione um aplicativo para
memora-graph.pages.dev - Crie uma política com e-mails permitidos
- Pages → Configurações → Ative a Política de Acesso
Consulte memora-graph/ para configuração detalhada e configuração de múltiplos bancos de dados.
Chat com Memórias
Faça perguntas sobre sua base de conhecimento diretamente da interface do grafo. O painel de chat usa RAG (Geração Aumentada por Recuperação) para buscar memórias relevantes e transmitir respostas do LLM com suporte a chamadas de ferramentas.
- Alternar via o ícone de chat flutuante no canto inferior direito
- Busca semântica encontra as memórias mais relevantes como contexto
- Respostas em streaming com referências
[Memory #ID]clicáveis que focam o nó do grafo - Chamada de ferramentas — o LLM pode criar, atualizar e excluir memórias diretamente do chat (ex.: "salve isso como uma memória", "exclua a memória #42", "atualize a memória #10 com...")
- Funciona tanto no servidor local quanto na implantação do Cloudflare Pages
Configure o modelo de chat:
| Backend | Variável | Padrão |
|---|---|---|
| Servidor local | variável de ambiente CHAT_MODEL | Recorre a MEMORA_LLM_MODEL |
| Cloudflare Pages | CHAT_MODEL em wrangler.toml | deepseek/deepseek-chat |
Requer uma API compatível com OpenAI (OPENAI_API_KEY + OPENAI_BASE_URL para local, segredo OPENROUTER_API_KEY para Cloudflare). O modelo de chat deve suportar uso de ferramentas (chamada de funções).
Deduplicação com LLM
Encontre e mescle memórias duplicadas usando comparação semântica com IA:
# Find potential duplicates (uses cross-refs + optional LLM analysis)
memory_find_duplicates(min_similarity=0.7, max_similarity=0.95, limit=10, use_llm=True)
# Merge duplicates (append, prepend, or replace strategies)
memory_merge(source_id=123, target_id=456, merge_strategy="append")
Comparação com LLM analisa pares de memórias e retorna:
verdict: "duplicado", "semelhante" ou "diferente"confidence: pontuação de 0,0 a 1,0reasoning: Breve explicaçãosuggested_action: "mesclar", "manter_ambas" ou "revisar"
Funciona com qualquer API de chat compatível com OpenAI (OpenAI, OpenRouter, Azure, etc.) via OPENAI_BASE_URL. OpenRouter é adequado para este caminho de LLM; ele não fornece embeddings — configure embeddings separadamente (consulte Busca Semântica e Embeddings).
Armazenamento de Documentos
Armazene documentos estruturados (relatórios de pesquisa, decisões de arquitetura, post-mortems) como árvores de fragmentos pesquisáveis:
# Store a markdown document — auto-parsed into typed fragments
memory_store_document(
content="# Research Report\n\n## Evidence Table\n| Claim | Confidence |\n...",
document_key="research/memora-enhancements-2026-04-08",
tags=["memora/research"]
)
# Returns: {root_id: 230, fragment_count: 100, node_map: {claim: [...], plan_item: [...], ...}}
# Retrieve the full document or specific fragment types
memory_get_document(document_key="research/memora-enhancements-2026-04-08")
memory_get_document(document_key="...", node_kinds=["claim"], content_mode="full")
# Delete a document and all its fragments
memory_delete_document(document_key="research/memora-enhancements-2026-04-08")
Como funciona: O analisador divide o markdown por estrutura — tabelas se tornam afirmações individuais, listas numeradas se tornam itens de plano, listas de URLs se tornam referências, e seções de risco se tornam fragmentos de risco. Cada fragmento é pesquisável independentemente via memory_semantic_search enquanto o documento completo é recuperável como uma unidade.
Tipos de fragmentos: claim, plan_item, reference, section_chunk, risk
Proteções de integridade: Fragmentos de documentos são protegidos contra modificação acidental:
memory_deleterequerforce=Truepara fragmentosmemory_mergerecusa mesclar fragmentosmemory_absorbexclui fragmentos da correspondência de similaridadememory_find_duplicatesememory_detect_supersessionsignoram fragmentos- A interface do grafo oculta fragmentos, mostrando apenas o nó raiz do documento
Ferramentas de Automação de Memórias
Ferramentas estruturadas para tipos comuns de memória:
# Create a TODO with status and priority
memory_create_todo(content="Implement feature X", status="open", priority="high", category="backend")
# Create an issue with severity
memory_create_issue(content="Bug in login flow", status="open", severity="major", component="auth")
# Create a section placeholder (hidden from graph)
memory_create_section(content="Architecture", section="docs", subsection="api")
Insights de Memórias
Analise memórias armazenadas e apresente insights acionáveis:
# Full analysis with LLM-powered pattern detection
memory_insights(period="7d", include_llm_analysis=True)
# Quick summary without LLM (faster, no API key needed)
memory_insights(period="1m", include_llm_analysis=False)
Retorna:
- Resumo de atividade — memórias criadas no período, agrupadas por tipo e tag
- Itens em aberto — TODOs e problemas abertos com detecção de obsoletos (configurável via
MEMORA_STALE_DAYS; padrãomemory_insightsde 14, padrão da interface do grafo de 30 — mesma variável, dois consumidores) - Candidatos a consolidação — pares de memórias semelhantes que poderiam ser mesclados
- Análise com LLM — temas, áreas de foco, lacunas de conhecimento e um resumo (requer
OPENAI_API_KEY)
Vinculação de Memórias
Gerencie relacionamentos entre memórias:
# Create typed edges between memories
memory_link(from_id=1, to_id=2, edge_type="implements", bidirectional=True)
# Edge types: references, implements, supersedes, extends, contradicts, related_to
# Remove links
memory_unlink(from_id=1, to_id=2)
# Boost memory importance for ranking
memory_boost(memory_id=42, boost_amount=0.5)
# Detect clusters of related memories
memory_clusters(min_cluster_size=2, min_score=0.3)
Exportação do Grafo de Conhecimento (Opcional)
Para visualização offline, exporte memórias como um arquivo HTML estático:
memory_export_graph(output_path="~/memories_graph.html", min_score=0.25)
Isso é opcional - o Servidor de Grafo ao Vivo fornece a mesma visualização com atualizações em tempo real.
Integração com Neovim
Navegue pelas memórias diretamente no Neovim com Telescope. Copie o plugin para sua configuração:
# For kickstart.nvim / lazy.nvim
cp nvim/memora.lua ~/.config/nvim/lua/kickstart/plugins/
Uso: Pressione <leader>sm para abrir o navegador de memórias com busca difusa e pré-visualização.
Requer: telescope.nvim, plenary.nvim e memora instalados no seu ambiente Python.

