Releases: fabiolenine/mcp_deepmen0
Release list
deepmem0-mcp-v0.3.0 — a UI do cofre navega o corpus
O cofre deixa de ser apenas o gestor de credenciais e passa a ser a interface gráfica da memória: as telas de corpus entram no mesmo app Starlette, com a mesma sessão, a mesma CSP fechada, o mesmo i18n PT/EN e a mesma auditoria. Nenhuma porta nova, nenhuma dependência nova.
Telas
| Rota | O que mostra |
|---|---|
/memories |
Lista com facetas e cursor real |
/memories/{id} |
Payload completo, ativação ACT-R, cadeia de versões, histórico, entidades vinculadas, proveniência de documento |
/search |
Busca semântica com as_of, event_date e recordação histórica |
/queue |
Fila de ingestão: profundidade, progresso por trecho, dead letters |
/entities |
Entity store com identidade normalizada |
/users |
Usuários e tokens (extraídos do painel) |
Por que cada superfície lê de onde lê
Não é preferência — é restrição medida do servidor:
- a navegação vai ao Qdrant direto porque
get_memoriesé primeira-página-com-teto e não expõe cursor: num corpus maior que o teto, o excedente é inalcançável pela tool; - os campos de ACT-R não passam pela whitelist de metadata do
search_memories; - o entity store não tem caminho MCP nenhum (
list_entitiesfaceta a collection principal, e as tools de grafo falam com um Neo4j que não está de pé); - fila e histórico saem do SQLite em
mode=ro+query_only; - a busca é a exceção deliberada e vai pelo MCP, porque o valor está no pipeline (denso + BM25 + reranker + ativação +
as_of), não no índice. Sempre comreinforce=false: navegar num console de operador não é um re-encontro da memória, e contá-lo enviesaria o ranking que a própria tela exibe.
Três achados do servidor real que moldaram o código
facet()exige índice de payload. Campos comodomainnão têm e devolvem400. As facetas são contadas em Python sobre um scroll projetado. Filtrar por campo sem índice funciona; só facetar é que não.- A paginação é por valor, e o valor não é único. Com
order_byo scroll não devolvenext_page_offset,start_fromé inclusivo, e os trechos de um documento nascem no mesmocreated_at— um instante pode ter mais pontos que a página inteira. O cursor carrega os ids já entregues naquele instante: guardar só o timestamp repetiria o grupo para sempre, e um contador dependeria de uma ordem que a API não promete. - O SDK esconde a causa. O transporte roda num task group, então um token recusado chegava como
ExceptionGroup: unhandled errors in a TaskGroup. A causa é desembrulhada antes de virar mensagem.
Segurança e robustez
- Telas de corpus estritamente de leitura — um teste lê o fonte do leitor e reprova qualquer verbo de escrita
- Fonte indisponível não derruba a UI: vira card na tela, e as telas de credencial seguem servindo
- O pacote sai com defaults neutros; o escopo e a collection de cada instalação vivem no env file, e o
.env.exampleavisa que deixá-los em branco rende uma tela vazia sem erro nenhum
Gate
1278 testes verdes, paridade i18n PT/EN travada por teste, e smoke de 16 verificações contra corpus real.
deepmem0-mcp-v0.2.1 — tie-band defaults aligned with the fork
Documentation-only patch, released to keep the MCP's lineage legible next to DeepMem0 v0.13.0, which fixed the rerank_score score space.
What changed
Two env-var fallbacks in config.py were describing a score space that no longer exists. Both live inside if opt_env(...), so the second argument to env() is unreachable — nothing about behaviour changes. They were misleading to read:
MEM0_DYNAMICS_TIE_BANDfell back to"0.002", the tie band measured on the doubly-sigmoided axis the fork removed in v0.13.0;MEM0_EVENT_TIE_BANDfell back to"0.05", the wide event band a prior review rejected for overfitting a single pair. Written there, it read like a reference value.
Both now read "0.008", matching RERANK_TIE_BAND in the fork.
Context
DeepMem0 v0.13.0 found that rerank_score is already an absolute relevance in [0, 1] — sentence-transformers applies nn.Sigmoid for a num_labels=1 cross-encoder — and the core was applying a second sigmoid on top, compressing the axis to [0.5, 0.731]. Ordering never moved (a sigmoid is monotonic), but the superseded penalty and the tie bands were measured against a compressed axis. The band constant was converted, not re-fitted.
Deployments that set either variable explicitly are unaffected; the value they set is the value used, exactly as before.
Verification
1063 tests pass (-m "not integration"). The production golden retrieval eval, run against this MCP with the v0.13.0 core, is unchanged at +0.0000 across all 16 metrics.
deepmem0-mcp-v0.2.0 — escopo de delete e readiness
Escopo de delete e readiness. Um tema só: valor errado que não produz erro.
O escopo do delete não era normalizado
O filtro do vector store é casamento EXATO. " alice" e "alice" são escopos
diferentes, e a diferença não aparece como erro — aparece como resultado vazio.
Num delete isso é pior que um crash: nada casa, nada é removido, e a chamada
responde sucesso. Medido no corpus de produção: um user_id real casa 1159
pontos; o MESMO valor com um espaço à esquerda casa 0.
delete_all_memories e delete_entities normalizam antes de montar o filtro, e
safe_bulk_delete normaliza como defesa em profundidade — só as quatro chaves de
ESCOPO, porque normalizar chave livre quebraria filtro de metadata legítimo. A
regra é importada do core (normalize_scope_id), nunca reimplementada: duas
definições de "escopo válido" foi o que deixou importance='high' entrar no
corpus.
O escopo resolvido escapava por dois caminhos
Só o argumento do cliente passava pela regra. get_default_user_id() lê por
env(), que apara apenas as BORDAS, então MEM0_USER_ID="a b" chegava como
'a b' e " " como '' — os dois sem erro, em toda tool escopada. E o
user_id amarrado ao token vinha do cofre, que nunca aplicou a regra. O valor
RESOLVIDO agora normaliza, e o erro nomeia a ORIGEM: sem isso o operador procura
o defeito no cliente enquanto ele está no drop-in do systemd.
Isso também corrige a comparação de autorização, que rodava != sobre a string
crua — um token amarrado a "alice" recusava " alice" como "token não pode
acessar esse escopo", mensagem errada para um erro de digitação.
A sonda de readiness não podia reprovar
O /health passa a expor entity_pipeline e a responder 503 quando o
pipeline de entidade está degradado. O campo era documentado e não existia.
A versão anterior chamava get_nlp_full() sem argumento, o que inspeciona o
modelo do idioma DEFAULT: dizia en_core_web_sm num deployment português.
Carregar também dispara spacy.cli.download, e uma sonda de readiness que toca a
rede pendura. O idioma vem de configured_language(), a mesma função que o
build_config() usa — uma sonda que discorda do runtime é pior que sonda
nenhuma, porque ela afirma.
Proveniência do próprio MCP no boot
O carimbo identificava só o FORK. É o servidor que decide escopo, autorização e
contrato de resposta, então um deploy dele era verificável apenas pelo
comportamento — bom para o smoke do dia, inútil no dia seguinte.
boot_mcp_sha, boot_mcp_tree_dirty e os hashes de server.py/helpers.py
entram no /health.
Testes
A suíte de integração fixava anthropic na mão enquanto o deployment roda
MEM0_PROVIDER=ollama sem token Anthropic nenhum: exercitava um provedor que
produção não usa e morria em 429 no setup. Passou a resolver com a mesma
precedência do build_config(). Medido: 8 falhas antes, 15 passed / 4
skipped depois.
v0.3.0
v0.3.0 (2026-07-24)
Features
-
Event-date-aware ranking (v0.6) + sync to current (
c0a9767) -
v0.6 event-date-aware ranking: search_memories gains event_from/event_to
event-time window filter + automatic query-anchor ranking (fusion boost +
bounded post-rerank tie-break, decoupled from the ACT-R tie-break) -
DeepMem0 Vault: bearer-token auth gate + admin UI (deepmem0-vault)
-
async ingest queue, document/vision extraction, and related server updates
Detailed Changes: v0.2.0...v0.3.0
v0.2.0
v0.2.0 (2026-07-19)
Documentation
- Restore fork note in README (lost in snapshot sync) (
4143a4d)
Features
- scope: Passive memory_scope passthrough (ontology v1, step 2) (
0bba795)
Validates the 4-value scope enum (or null = absence) and leveled evidence on add_memory/add_document, stamps default provenance (version=1, source=manual), and exposes memory_scope + provenance fields through the metadata whitelist. No routing, no search behavior change — scope-aware retrieval is a later step gated on its own eval. Mirrors deepmen0 0.6.0 (promoted key + keyword index).
Detailed Changes: v0.1.0...v0.2.0
v0.1.0
v0.1.0 (2026-07-14)
Features
- update: Make update_memory asynchronous via the durable queue (
cd51fb6)
Mirrors add_memory's async contract with a new kind="update": the tool validates the memory exists + resolves owner scope at submit, enqueues, and returns {status:"queued", task_id} immediately; the worker re-embeds + re-classifies the metadata in the background. An identical re-submit while the job is active returns the same task_id (sentinel idempotency key) — no double-apply. Fixes the ambiguous client-timeout on the previously synchronous update path (the update succeeded server-side while the client saw a timeout).
Detailed Changes: v0.0.0...v0.1.0