Engram

Servidor MCP auto-hospedado que dá aos agentes de IA memória persistente — Markdown como fonte da verdade, busca híbrida BM25+embedding, relações de grafo tipadas.

Documentação

Inkwell — Servidor MCP de Base de Conhecimento Persistente

License: MIT Docker Pulls Python 3.12+

O Inkwell é um servidor Model Context Protocol auto-hospedado que dá aos agentes de IA memória persistente entre sessões e projetos. Três coisas o diferenciam dos outros servidores de memória: arquivos Markdown simples são a fonte da verdade (o índice é um cache descartável que você pode excluir e reconstruir), as entradas são conectadas por um grafo kb:// tipado em vez de despejadas em uma pilha plana, e as atualizações são bitemporais — substituir um fato mantém a versão antiga legível em vez de sobrescrevê-la.

Docker Hub: foreigndmitryi/inkwell-memory · Site: veronchenko.github.io/inkwell-memory

Antes chamado Engram. Renomeado na versão 0.14.0 — existem uma dúzia de projetos não relacionados chamados "Engram" e o nome havia deixado de ser encontrável. Não é afiliado a nenhum deles, nem ao layout de teclado Engram. Veja o changelog para saber o que a renomeação quebra.

Sumário

Conceito

As conversas dos agentes terminam e levam seu contexto junto. O Inkwell é a peça que sobrevive: uma base de conhecimento que um agente pesquisa antes de agir e na qual escreve depois de resolver algo não óbvio, para que a próxima sessão — mesmo projeto ou um diferente — comece com o que já foi aprendido em vez de redescobrir tudo.

Ele armazena deliberadamente zero informação descobrível. Se um fato pode ser obtido do código, histórico do git, arquivos de configuração ou documentação existente, ele não pertence ao Inkwell — é para isso que servem greps e releituras. O que pertence é o tipo de conhecimento que uma conversa perderia de outra forma: uma decisão e as alternativas que ela descartou, a causa raiz e a correção de um bug, um procedimento aprendido da maneira difícil, uma preferência declarada uma vez que deve valer daí em diante.

Duas coisas mantêm a base utilizável à medida que cresce:

  • Atomicidade — uma entrada, um fato. O remember avisa (sem bloquear) sobre cabeçalhos Markdown, mais de 3 parágrafos ou conteúdo acima de 512 B/1 KB, empurrando despejos de múltiplos fatos de volta para entradas separadas e vinculadas, em vez de um muro de texto que nenhuma busca ranqueará bem.
  • O grafo, não uma pilha — as entradas se vinculam entre si por meio de referências kb://uuid#type, para que fatos relacionados (um projeto central, seus recursos, um diagnóstico ligado a um deles) permaneçam navegáveis nos dois sentidos, em vez de viverem como linhas isoladas.

Recursos

  • Busca híbrida — SQLite FTS5 (BM25, stemming de Porter) fundido com similaridade de cosseno sobre embeddings locais Model2Vec e um canal de correspondência exata ponderado por IDF sobre título/tags via Fusão por Rank Recíproco; encontra entradas por significado ou por um nome próprio literal que BM25/embeddings sozinhos diluiriam entre distratores lexicalmente semelhantes, com zero dependência de nuvem
  • Relações de grafo tipadas — links kb://uuid#type entre entradas, resolvidos nos dois sentidos (saídas + backlinks) em cada recall, com um segundo salto opcional (hops=2) para ver como duas entradas se conectam por meio de uma intermediária
  • Tipos de entrada com esquema obrigatóriohub, decision, diagnostic, feature, procedure, integration, pattern, snippet, preference, idea — declarados em schema.json e expostos ao cliente como um enum, para que um tipo inválido não possa ser gravado; filtráveis na busca/lista
  • Passada de integridade doctor — uma verificação orientada por esquema sobre os arquivos Markdown para links pendentes e substituídos, tipos não declarados, campos de template ausentes, supernós e colisões de tag/tipo
  • Associação estrutural part_of — vincula uma entrada de detalhe (decision, diagnostic, feature, procedure, integration, ...) ao seu hub, aplicada por tipo pelo esquema; filtrável em search/list, agrupada junto com os backlinks kb:// no resumo recall de um hub
  • Versionamento bitemporalremember(..., supersede=True) cria uma nova versão em vez de sobrescrever; versões antigas permanecem no histórico (include_superseded=True) em vez de serem perdidas
  • Detecção de duplicatas e sugestões de linksremember encontra títulos quase idênticos para evitar entradas duplicadas e retorna suggested_links (correspondências por similaridade de embedding) para que fatos relacionados sejam referenciados cruzadamente em vez de órfãos
  • Proteções de atomicidade — avisos não bloqueantes sobre antipadrões estruturais (cabeçalhos, >3 parágrafos, conteúdo superdimensionado) para que a base permaneça um-fato-por-entrada à medida que escala
  • Painel web — visualização de grafo dirigido por força, busca híbrida e um painel CRUD sobre a mesma base de conhecimento que as ferramentas MCP usam (veja Painel)
  • Três transportes — stdio (gerenciado por agente), SSE, streamable-http — para que o mesmo servidor funcione para um único agente local ou uma implantação multiagente compartilhada
  • Markdown como fonte da verdade — o índice SQLite é um cache reconstruível; exclua-o e execute rebuild, nenhum dado é perdido

Comparação

InkwellMem0Zep / GraphitiLangMem
Fonte da verdadeArquivos Markdown no discoVector DB / API gerenciadaGrafo de conhecimento temporalVector store (baseado em LangChain)
BuscaBM25 + embeddings locais Model2Vec (fusão RRF)Similaridade vetorialTravessia de grafo + embeddingsSimilaridade vetorial
RelaçõesLinks kb://uuid#type explícitos, criados pelo agenteImplícitas (fatos extraídos por LLM)Arestas de grafo temporal extraídas automaticamenteNenhuma embutida
Modelo temporalvalid_at/supersede bitemporal na gravaçãoSobrescrita de fatoGrafo temporal nativo (arestas bitemporais)Nenhum embutido
ImplantaçãoAuto-hospedado, imagem Docker única, sem dependência de nuvemAPI hospedada ou auto-hospedada + vector DBAuto-hospedado, requer Neo4jBiblioteca, sem servidor
Centro de designDeliberadamente mínimo — zero informação descobrível, o agente decide o que vale manterExtração automática de fatos de conversasExtração automática de entidades/relacionamentosPrimitivas de memória compostáveis para agentes LangGraph

O Inkwell troca a extração automática (Mem0, Zep) por uma base de conhecimento atômica, explicitamente vinculada e curada pelo agente — sem pipeline de ingestão dirigido por LLM, sem dependência de banco de dados de grafo, e o Markdown no disco permanece legível por humanos e passível de diff.

Medido Contra um Wiki Markdown Simples

O Inkwell foi avaliado contra a mesma base de conhecimento empacotada como um wiki Markdown comum — um arquivo por tópico, organizado em pastas por projeto e categoria (decisão, diagnóstico, recurso, procedimento, ...), cada projeto com uma página de índice e links cruzados entre páginas relacionadas, navegado com Read/Grep/Glob/Bash. Mesmo conteúdo, duas formas de encontrá-lo — para cobertura de fatos equivalente, o search/recall do Inkwell usou:

InkwellWikiMelhoria
Chamadas de ferramenta104186-44%
Total de tokens3,87M5,91M-35%
Custo$1,31$1,62-19%
Tempo de parede (soma)577s760s-24%
Taxa de fatos1,0000,964+3,7%

32 perguntas (salto único, múltiplos saltos, negativas, multilíngues, substituição) sobre 5.070 entradas — 70 fatos curados em 4 projetos fictícios mais 5.000 entradas distratoras de texto real, para que a recuperação funcione em uma escala que não cabe no contexto de um agente:

wiki/
├── Ledgerbird/                       )
├── Pipewren/                         )  4 curated projects — 70 real
├── Snipfox/                          )  decisions/diagnostics/features/
├── Featherstore/                     )  procedures/integrations/snippets
│   ├── README.md                        <- project index page, links to every entry below
│   ├── decision/
│   │   ├── offline-engine-duckdb-over-spark.md
│   │   ├── online-value-serialization-msgpack.md
│   │   └── ... (2 more)
│   ├── diagnostic/
│   │   ├── redis-memory-doubling-from-ttl-less-deprecated-feature-groups.md
│   │   └── training-serving-skew-from-tz-naive-event-timestamps.md
│   ├── feature/       (4 entries)
│   ├── integration/   (2 entries)
│   ├── procedure/     (1 entry)
│   └── snippet/       (1 entry)
├── _shared/                             cross-project patterns & preferences
└── haystack-project-00000.../00199/     200 distractor projects x 25 pages
    │                                     = 5,000 real-text (Wikipedia) pages
    ├── README.md                        <- same index-page shape as a real project
    ├── decision/   (2 entries)
    ├── feature/    (3 entries)
    ├── idea/       (7 entries)
    ├── pattern/    (4 entries)
    ├── procedure/  (1 entry)
    └── snippet/    (2 entries)

Cada pasta de categoria é uma lista plana de um-arquivo-por-entrada, e cada pasta de projeto (real ou distratora) tem sua própria página de índice README.md vinculando a todas elas — estruturalmente idênticas, para que o braço do wiki não consiga distinguir fato curado de distrator apenas pela forma.

Um agente que já sabe onde procurar não precisa grep, reler e grep seu caminho até lá — a vantagem de eficiência se mantém para cobertura de fatos equivalente. O ranqueamento da recuperação também tem melhorado: adicionar um canal de correspondência exata ponderado por IDF (abaixo) moveu o MRR de 0,851 para 0,857 e o recall@5 de 0,851 para 0,869, sem regressão em nenhum idioma.

Padrões de Design

  • Fusão por Rank Recíproco — os ranqueamentos BM25 e de embeddings são calculados de forma independente e mesclados pela posição no rank, em vez de pela pontuação bruta, evitando a necessidade de normalizar métricas de similaridade incomparáveis.
  • Cache reconstruível sobre fonte da verdade — o índice SQLite é totalmente derivado dos arquivos Markdown (rebuild o regenera do zero); o banco de dados nunca é a única cópia de um fato.
  • Versionamento bitemporalsupersede grava uma nova entrada e aponta a antiga para ela via superseded_by, em vez de sobrescrever no lugar, para que o histórico permaneça consultável (include_superseded).
  • Travessia de grafo estilo HATEOAS — toda resposta de recall carrega seus próprios links kb:// de entrada/saída, para que navegar pelo grafo de conhecimento não exija uma consulta separada por salto.
  • Camada de domínio compartilhada, dois transportes — a API REST do painel e as ferramentas MCP chamam os mesmos métodos KnowledgeBase/SQLiteBackend, de modo que há exatamente um caminho de código para gravações, independentemente de qual superfície as acionou.

Arquitetura

Markdown files  --->  Search index  --->  MCP  --->  Agent
(source of truth)     (SQLite FTS5 +      (server.py)  (Claude Code,
                        Model2Vec,                       ChatGPT, ...)
                        rebuildable cache)

Os arquivos Markdown em <data-path>/entries/ são a única fonte da verdade. O índice SQLite (search_backend.py) é um cache descartável construído a partir deles — BM25 + embeddings Model2Vec fundidos via Fusão por Rank Recíproco, além do grafo de relações kb:// — e sempre pode ser regenerado com rebuild. O server.py expõe esse índice a um agente como ferramentas MCP (remember/recall/search/...); o painel é um ponto de entrada alternativo na mesma camada, acessando o mesmo KnowledgeBase/índice diretamente via REST em vez de MCP, para que remember/delete se comportem de forma idêntica, seja chamados por um agente ou editados manualmente no navegador.

  • src/server.py — Definições de ferramentas MCP (remember, recall, search, list, tags, forget, rebuild, doctor), um processo, transporte stdio/SSE/streamable-http
  • src/database.pyKnowledgeBase: CRUD de Markdown + frontmatter YAML, atribuição de UUID, detecção de duplicatas, lógica bi-temporal de substituição (supersede)
  • src/schema.json + src/schema.py — a taxonomia de entradas e suas regras por tipo como dados, além do carregador que as transforma no enum entry_type que o cliente MCP valida
  • src/doctor.py — a passagem de integridade orientada por esquema compartilhada pela ferramenta doctor, pelos avisos de rebuild e pela verificação de conformidade de remember
  • src/search_backend.pySQLiteBackend: BM25 (FTS5) combinado com similaridade de cosseno Model2Vec via Fusão de Rank Recíproco, além de extração de relações/ travessia de grafo kb://; embeddings são calculados de forma lazy na escrita e armazenados como coluna BLOB
  • src/dashboard/ — um segundo processo opcional (app.py FastAPI REST + /api/graph, static/index.html grafo em canvas com JavaScript puro, __main__.py seu próprio ponto de entrada uvicorn) reutilizando o mesmo KnowledgeBase
  • plugins/inkwell-hooks/ — um plugin autocontido instalável tanto no Claude Code quanto no Codex (.claude-plugin/plugin.json, SessionStart/Stop/SessionEnd + uma barreira PreToolUse de buscar-antes-de-lembrar, além de um agente inkwell-project-onboarder exclusivo do Claude Code) que aplica mecanicamente o fluxo de trabalho buscar-primeiro, lembrar-depois, em vez de depender apenas de um prompt de sistema; listado como inkwell-hooks tanto no .claude-plugin/marketplace.json da raiz do repositório (Claude Code) quanto no plugins/marketplace.json (Codex)

No Docker, o servidor MCP e o dashboard rodam como dois processos em um único contêiner (docker-entrypoint.sh), compartilhando o mesmo volume /knowledge; o contêiner é encerrado se qualquer um dos processos morrer.

Início Rápido

stdio

Seu agente gerencia o servidor. Recomendado para Claude Code, ChatGPT Desktop, Cursor.

claude mcp add --transport stdio inkwell -- \
  docker run -i --rm -v ./knowledge:/knowledge foreigndmitryi/inkwell-memory

SSE

Servidor persistente na rede. Compartilhe conhecimento entre vários agentes.

docker run -d --name inkwell \
  -p 8192 \
  -v ./knowledge:/knowledge \
  foreigndmitryi/inkwell-memory --transport sse

docker port inkwell 8192   # host port Docker assigned
claude mcp add --transport sse inkwell http://your-host:<port>/sse

-p 8192 (porta do host omitida) faz o Docker escolher uma porta efêmera livre no host em vez de falhar quando uma porta fixa como 8192 já está ocupada por outro contêiner — verifique a atribuição real com docker port. Use -p 8192:8192 se você precisar que a porta do host permaneça fixa.

HTTP

Sem estado, balanceável.

docker run -d --name inkwell \
  -p 8192 \
  -v ./knowledge:/knowledge \
  foreigndmitryi/inkwell-memory --transport streamable-http

docker port inkwell 8192   # host port Docker assigned
claude mcp add --transport http inkwell http://your-host:<port>/mcp

Multi-tenant

Um servidor, várias equipes, cada uma isolada em sua própria pasta de dados e chave de API. Defina INKWELL_MULTI_TENANT=1 (requer INKWELL_PUBLIC_URL e INKWELL_ADMIN_API_KEY, e um transporte de rede — nunca stdio); src/app.py então serve MCP, a interface de administração e o dashboard a partir de um único processo. Provisione uma equipe com inkwell add-team <name> (executado via docker exec, veja src/cli.py) para obter sua chave de API, depois aponte o agente de cada equipe para /mcp com essa chave como token bearer:

claude mcp add --transport http inkwell https://your-host/mcp \
  --header "Authorization: Bearer <TEAM_API_KEY>"

Ou como uma configuração mcpServers bruta (Claude Desktop e outros clientes):

{
  "mcpServers": {
    "inkwell": {
      "type": "http",
      "url": "https://your-host/mcp",
      "headers": {
        "Authorization": "Bearer <TEAM_API_KEY>"
      }
    }
  }
}

TeamTokenVerifier (src/server.py) aplica hash ao token e o consulta em admin.db a cada requisição — sem cache, então uma chave revogada para de funcionar imediatamente.

Busca

Híbrida: SQLite FTS5 (stemming de Porter, BM25) combinada com similaridade de cosseno sobre embeddings locais Model2Vec (minishlab/potion-multilingual-128M, sem dependência de nuvem) via Fusão de Rank Recíproco — então uma consulta que não compartilha nenhuma palavra literal com uma entrada ainda pode encontrá-la pelo significado. Retorna apenas a busca por palavras-chave se o modelo de embeddings não puder ser carregado. O índice continua sendo um único arquivo SQLite que você pode consultar com ferramentas SQL padrão.

Ferramentas

FerramentaDescriçãoParâmetros Principais
rememberCriar ou atualizar uma entrada (upsert com detecção de duplicatas, ou versioná-la via supersede). entry_type é obrigatório, e part_of (UUIDs de hub) é obrigatório/opcional/rejeitado por tipo conforme a regra membership do esquema. Retorna size, atomicidade não bloqueante warnings (cabeçalhos Markdown, >3 parágrafos, >512 B / >1 KB), e suggested_links — entradas quase duplicadas/relacionadas (por similaridade de embeddings) que valem referência cruzada com um link kb://.title, content, tags, entry_type, entry_id, force, resource, supersede, part_of
recallLer uma entrada com suas relações de grafo (saídas + backlinks). Retorna size e last_modified. Tipos de alto grau (hubs) retornam in_digest — backlinks agrupados por tipo de ligação, além de membros agrupados por part_of — em vez de uma lista truncada arbitrariamente.entry_id, relations_limit
searchBusca híbrida por palavras-chave + semântica, filtrável por tags, entry_type exato e/ou part_ofquery, tags, limit, include_superseded, entry_type, part_of
listNavegar por entradas ordenadas por título, filtrável por tags, entry_type exato e/ou part_oftags, limit, include_superseded, entry_type, part_of
tagsListar todas as tags com contagens de entradas
forgetExcluir uma entrada (arquivo e índice). Avisa quando outras entradas ainda linkam para ela.entry_id
rebuildReconstruir o índice de busca a partir dos arquivos Markdown; também executa doctor
doctorVerificar cada entrada contra schema.json: alvos kb:// pendentes/substituídos, tipos não declarados, campos obrigatórios de frontmatter e corpo de template ausentes, supernós, colisões de tag/tipo

Esquema de entradas

A taxonomia de entradas e suas regras por tipo vivem em schema.json, não em Python: quais campos de frontmatter um tipo exige, quais campos de corpo de template deve carregar, se a busca pode impulsioná-la por uso, e se recall digere seus backlinks. entry_type é exposto ao cliente MCP como um enum construído a partir desse esquema, então um tipo não declarado é rejeitado antes que a chamada chegue ao servidor.

Ordem de resolução, o primeiro encontrado vence: <data-path>/schema.json, depois o src/schema.json empacotado. Um arquivo do usuário substitui o empacotado por completo — os dois nunca são mesclados, então copie o padrão e edite-o. O esquema é lido uma vez na inicialização, então editá-lo exige reiniciar o servidor. Entradas escritas manualmente ainda podem carregar um tipo não declarado; doctor relata esses casos.

Dashboard

Uma interface web para explorar visualmente e editar manualmente a mesma base de conhecimento que as ferramentas MCP usam — sem duplicação de protocolo, toda ação passa por KnowledgeBase.

  • Grafo de força direcionada de todas as entradas e suas relações kb:// — clique em um nó para inspecioná-lo, clique em um chip da legenda para filtrar por tipo (as correspondências permanecem acesas, as demais escurecem)
  • Busca híbrida sobre o mesmo índice BM25 + semântico da ferramenta MCP search, com filtros de tag e entry_type
  • Painel CRUD para criar, editar, substituir ou excluir entradas sem tocar em arquivos Markdown manualmente — relações de grafo de entrada/saída mostradas junto aos campos
  • Interface escura, densa, sem enfeites — uma ferramenta de manutenção, não um aplicativo de consumo (veja PRODUCT.md/DESIGN.md)

Desabilitado por padrão (o contêiner só executa o servidor MCP). Habilite-o como segundo processo no mesmo contêiner:

docker run -d --name inkwell \
  -e INKWELL_ENABLE_DASHBOARD=1 \
  -p 8192 -p 8193 \
  -v ./knowledge:/knowledge \
  foreigndmitryi/inkwell-memory --transport sse

docker port inkwell 8193   # dashboard host port

Ou execute-o standalone localmente: python -m dashboard (veja Configuração para INKWELL_DASHBOARD_HOST/INKWELL_DASHBOARD_PORT).

Relações de Grafo

Linke entradas com URLs kb://uuid#type no conteúdo Markdown:

This service runs on [Saturn](kb://a1b2c3d4-...#runs-on)
and depends on [PostgreSQL](kb://f9e8d7c6-...#depends-on).

recall retorna ambas as direções, além de metadados de tamanho:

{
  "id": "a1b2c3d4-...",
  "title": "My API Service",
  "content": "...",
  "tags": ["..."],
  "size": 1024,
  "last_modified": "2026-03-14",
  "relations": {
    "out": [{"type": "runs-on", "id": "e5f6...", "title": "Saturn"}],
    "in": [{"type": "depends-on", "id": "b7c8...", "title": "Frontend App"}]
  }
}

Como HATEOAS para conhecimento — cada resposta carrega os links para navegar pelo grafo.

Exemplos de Uso

Armazenar conhecimento

Peça ao seu agente:

"Lembre-se de que nossa API roda na porta 8080 e depende do PostgreSQL 15."

O Inkwell cria um arquivo Markdown com um UUID único, indexa-o e confirma. O agente agora pode recuperar esse fato em qualquer sessão futura.

Buscar

"O que sabemos sobre PostgreSQL?"

O Inkwell busca em todas as entradas por conteúdo, título e tags. Os resultados são classificados por relevância.

Navegar pelo grafo

"O que depende do PostgreSQL?"

Se entradas linkam para o artigo do PostgreSQL com kb://uuid#depends-on, o Inkwell retorna todos os backlinks — mostrando cada serviço que depende dele, sem que o agente precise buscar por cada um.

Compartilhar conhecimento entre agentes

Inicie o Inkwell com transporte SSE ou HTTP. Vários agentes — mesmo de provedores diferentes (Claude, ChatGPT, Copilot) — conectam-se ao mesmo servidor. O que um agente lembra, todos os outros podem recuperar.

Agent A: "Remember that the deploy key rotates every 90 days."
Agent B: "When does the deploy key expire?"
→ Agent B finds the answer immediately.

Instrua Seu Agente

Adicione isto ao seu prompt de sistema ou instruções do projeto para fazer seu agente usar o Inkwell como reflexo, não como pensamento posterior:

Inkwell is your persistent memory. Using it is mandatory, not optional.

Inkwell stores ZERO discoverable information. If you can derive it from
code, git history, configuration files, or existing documentation, it
does not belong in Inkwell. Inkwell captures decisions and their context,
diagnostics and their root causes, procedures learned the hard way —
the kind of knowledge that is lost when a conversation ends.

Before working on any topic: search Inkwell first. Always. Even if you think you know.
Before answering a question about infrastructure or architecture: search first.
Before proposing a solution: check if a past decision exists in Inkwell.

After resolving a diagnostic: remember the root cause and the fix.
After executing a procedure: remember the steps.
After making an architecture decision: remember the choice and the rationale.
After discovering something about the infrastructure: remember it.

Um prompt de sistema é fácil de esquecer no meio da sessão. plugins/inkwell-hooks/ inclui um plugin autocontido (SessionStart, Stop, SessionEnd, PreToolUse handlers, além de um agente inkwell-project-onboarder exclusivo do Claude Code) que empurra mecanicamente o agente a buscar no Inkwell antes de começar o trabalho e lembra-o de remember mudanças não triviais antes de terminar, em vez de depender de ele lembrar desta seção sem aviso. Funciona tanto no Claude Code quanto no Codex — instale com /plugin marketplace add <this-repo> e depois /plugin install inkwell-hooks@inkwell-memory (Claude Code), ou codex plugin marketplace add <this-repo>/plugins e depois codex plugin add inkwell-hooks@inkwell-memory (Codex) — veja plugins/inkwell-hooks/README.md para o que cada hook faz e o fluxo completo de instalação.

Desative a memória automática integrada do Claude Code

O Claude Code inclui sua própria memória automática (MEMORY.md em ~/.claude/projects/<project>/memory/, carregada a cada sessão). Executá-la junto com o Inkwell significa dois sistemas escrevendo notas sobrepostas e competindo pela atenção do agente, o que atrapalha mais do que ajuda. Desligue-a em settings.json:

{
  "autoMemoryEnabled": false
}

Ou via variável de ambiente (tem precedência sobre a configuração e o alternador /memory): CLAUDE_CODE_DISABLE_AUTO_MEMORY=1. Veja documentação de memória do Claude Code para detalhes.

Configuração

Todas as opções têm fallbacks de variáveis de ambiente INKWELL_*. Argumentos de CLI têm prioridade.

OpçãoVariável de ambientePadrãoDescrição
--data-pathINKWELL_DATA_PATH/knowledgeCaminho raiz para dados de conhecimento
--transportINKWELL_TRANSPORTstdioTransporte MCP
--hostINKWELL_HOST0.0.0.0Endereço de escuta (SSE/HTTP)
--portINKWELL_PORT8192Porta de escuta (SSE/HTTP)
--embedding-modelINKWELL_EMBEDDING_MODELminishlab/potion-multilingual-128MModelo Model2Vec para busca semântica
INKWELL_ENABLE_DASHBOARDnão definido (desligado)Valor verdadeiro (1/true/yes) inicia o dashboard como segundo processo junto com o servidor MCP
--host (dashboard)INKWELL_DASHBOARD_HOST0.0.0.0Endereço de escuta do dashboard
--port (dashboard)INKWELL_DASHBOARD_PORT8193Porta de escuta do dashboard (a primeira porta livre igual ou acima deste valor é usada)
INKWELL_QUERY_LOGnão definido (desligado)Caminho de arquivo; quando definido, search/recall anexam um rastreamento JSONL de cada chamada para análise de qualidade de recuperação

Formato de Armazenamento

Entradas são arquivos Markdown com frontmatter YAML em <data-path>/entries/:

---
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
title: Entry Title
tags: [infrastructure, postgresql]
type: decision
resource: /path/to/relevant/file
---

Markdown content here...

type é obrigatório em toda chamada de remember (um campo dedicado, não parte de tags) — ele classifica a entrada (hub, decision, diagnostic, procedure, preference, snippet, ...) e é filtrável via search/list. resource é opcional: um caminho canônico de arquivo/pasta que a entrada descreve. Entradas legadas escritas antes desses campos existirem ainda são lidas normalmente.

O índice de busca é um cache reconstruível em <data-path>/index/inkwell.db. Exclua-o e rebuild — nenhum dado é perdido. rebuild também relata avisos de conformidade de esquema (type ausente, resource malformado) nas entradas existentes.

Desenvolvimento

# Build
docker build -t inkwell .

# Test (separate Dockerfile — pytest/tests/ never ship in the production image)
docker build -f tests/Dockerfile -t inkwell-test .
docker run --rm inkwell-test

# Run locally (SSE)
docker run -d --name inkwell -p 8192 -v ./knowledge:/knowledge inkwell --transport sse

Licença

MIT