Roam Research MCP Server
Acesse e gerencie seu grafo do Roam Research via sua API.
Documentação

Roam Research MCP + CLI
Introdução
Criei este projeto para resolver um problema pessoal: queria gerenciar meu grafo do Roam Research diretamente pelo Claude Code (e outros LLMs). Ao construir o servidor Model Context Protocol (MCP) para dar acesso a agentes de IA às minhas anotações, percebi que as ferramentas subjacentes eram poderosas o suficiente para se sustentarem sozinhas.
O que começou como um backend para agentes de IA evoluiu para um CLI autônomo completo. Agora, você pode usar as mesmas capacidades poderosas da API diretamente do seu terminal—enviando conteúdo via pipe para o Roam, pesquisando seu grafo e gerenciando tarefas—sem precisar de um LLM.
Quer você queira dar superpoderes ao Claude sobre sua base de conhecimento ou apenas queira um CLI robusto para seus próprios scripts, este projeto atende você.

O que há de novo na v4.0
Em uma linha: um bloco contendo uma quebra de linha suave (Shift+Enter) agora sobrevive a uma reescrita de página. Leia uma página, escreva-a de volta e nada se move.
Até agora, um bloco de várias linhas era renderizado como duas linhas físicas, a segunda na coluna 0. Isso redefinia a linha de base de indentação do parser, então todo bloco depois dele colapsava em direção à raiz e o roam_update_page_markdown gerava fielmente os movimentos para fazer sua página real corresponder. Ler uma página e escrever de volta uma revisão, o propósito documentado da ferramenta, era suficiente para acionar isso. Corpos de callout e blocos de código cercados são exatamente os blocos que carregam quebras suaves.
- Quebras suaves são renderizadas como
⏎. Uma página que contém uma ganha uma linha de marcador<!-- roam:escaped-newlines -->inicial; mantenha-a se você escrever o markdown de volta. Páginas sem bloco de várias linhas são renderizadas byte a byte idênticas à 3.x, sem marcador e sem codificação. - Barras invertidas nunca são especiais. O design anterior escapava novas linhas como
\n, que também é um prefixo comum em texto autoral:\nabla,\neq,C:\newdir. O sentinela não precisa dessa regra, então todos esses são escritos exatamente como digitados, em qualquer lugar. - Round-trips verbatim são no-ops. A saída do renderizador enviada de volta sem alterações, incluindo o cabeçalho do título, produz zero ações. O CLI compartilha a correção:
roam getcanalizado via pipe pararoam save --updatedeixa a página como estava. - Mais duas proteções em reescritas de página. Markdown não vazio que é analisado para zero blocos agora é recusado em vez de excluir todos os blocos da página (markdown genuinamente vazio ainda limpa uma página, como documentado). E um primeiro bloco escrito à mão que por acaso é um H1 ecoando o título da página não é mais removido em uma atualização comum.
- Referências vinculadas também são codificadas.
roam_fetch_page_full_viewescapa quebras suaves em blocos de referência e breadcrumbs, não apenas o conteúdo da própria página. - Clientes de navegador passam no preflight CORS. O transporte HTTP agora permite
MCP-Protocol-VersioneLast-Event-ID, ambos os quais um cliente deve enviar após a inicialização. Clientes que não são de navegador nunca foram afetados. - O SDK MCP está fixado na versão contra a qual a suíte de testes roda, então uma instalação nova obtém a superfície de protocolo que foi testada em vez do que o npm servir naquele dia.
Por que uma major. Quatro superfícies de leitura retornam bytes diferentes para qualquer página contendo um bloco de várias linhas: roam_fetch_page_by_title (format: "markdown"), roam_fetch_page_full_view, roam_get_subpages e roam get. Se você usa o servidor por meio de um assistente de IA, nada é exigido de você. Um script que analisa a saída markdown de páginas de várias linhas vê a nova codificação. Se você fixou roam-research-mcp@3, você mantém as correções da 3.2.0 e sua limitação de várias linhas documentada até você re-fixar.
Detalhes completos, incluindo os casos extremos e como cada correção foi verificada contra o estado anterior, estão no changelog.
Como isso difere do servidor MCP oficial do Roam
O Roam Research fornece seu próprio servidor MCP e CLI (@roam-research/roam-mcp). É uma boa ferramenta, e este projeto não tenta substituí-la. Eles falam com duas APIs diferentes do Roam, que é a diferença da qual tudo o mais decorre.
| Este projeto | @roam-research/roam-mcp oficial | |
|---|---|---|
| Fala com | API REST de backend do Roam (token do grafo + nome do grafo) | API HTTP local do Roam Desktop |
| Precisa do Roam rodando | Não — funciona headless | Sim, o aplicativo desktop deve estar aberto (ele usa deep-link para iniciá-lo) |
| Onde pode rodar | Em qualquer lugar: laptop, servidor, contêiner, CI | A máquina que executa o Roam Desktop |
| Daemon compartilhado | Sim — roam server roda um daemon HTTP para cada cliente | stdio por cliente |
| Multi-grafo | ROAM_GRAPHS env var, com proteção write_key para grafos escolhidos | ~/.roam-tools.json, um token por grafo |
| Grafos somente web | Funciona | Somente desktop |
Use o servidor oficial quando você quiser o caminho suportado pelo próprio Roam, ou precisar de coisas que só o aplicativo em execução pode fazer: controlar a UI do Desktop (abrir uma página, ler a seleção atual, dirigir a barra lateral), busca semântica/embeddings, sugestões de links, upload de arquivos, comentários ou invocar ferramentas que extensões do Roam registram.
Use este quando o Roam não estiver rodando ou não estiver instalado — um servidor, um contêiner, um cron job, uma etapa de CI. Ou quando você quiser os extras que este projeto desenvolveu: um CLI autônomo completo com piping de stdin, um daemon HTTP compartilhado com autenticação bearer opcional, diffing inteligente de páginas que preserva UIDs de blocos (e, portanto, suas referências de bloco), operações em lote com placeholders de UID para construir estruturas aninhadas em uma chamada e ferramentas de memória de agente.
Uma omissão deliberada: não há ferramenta de exclusão de página aqui. O Roam não tem desfazer que possa reverter uma exclusão em massa via API. O servidor oficial oferece delete_page; este projeto adota a linha mais conservadora.
Eles interoperam
Os dois servidores compartilham convenções de propósito, então rodar ambos não custa nada:
[[roam/agent guidelines]]— ambos leem a mesma página para suas convenções. Escreva-as uma vez; ambos as honram. Veja Diretrizes de agente.#.rm-hide/#.rm-private— ambos retêm blocos marcados do conteúdo voltado para IA. Marque uma vez, oculto de ambos. Veja Ocultando conteúdo da IA.
CLI autônomo: roam
O CLI roam permite que você interaja com seu grafo diretamente do terminal. Ele suporta piping de entrada padrão (stdin) para todos os comandos de criação e recuperação de conteúdo, tornando-o perfeito para fluxos de trabalho de automação.
Exemplos rápidos
# Save a quick thought to your daily page
roam save "Idea: A CLI for Roam would be cool"
# Pipe content from a file to a new page
cat meeting_notes.md | roam save --title "Meeting: Project Alpha"
# Create a TODO item on today's daily page
echo "Buy milk" | roam save --todo
# Prepend to top of page (newest-first ordering)
roam save -p "Changelog" --order first "v2.18.0 release"
# Search your graph and pipe results to another tool
roam search "important" --json | jq .
# Search for pages by namespace prefix
roam search --namespace "Convention" # Finds all Convention/* pages
# Fetch a page by title
roam get "Roam Research"
# Fetch daily pages using any date format (auto-normalized)
roam get today # Today's daily page
roam get 2026-03-21 # ISO date → "March 21st, 2026"
roam get "03/21/2026" # US date → "March 21st, 2026"
roam get "March 21" # Named (assumes current year)
# Fetch a block with ancestors (parent chain to page root)
roam get abc123def -a # Block + children + ancestors
roam get abc123def -a -d 0 # Ancestors only, no children
# Fetch page by UID or Roam URL
roam get page abc123def
roam get page "https://roamresearch.com/#/app/my-graph/page/abc123def"
# Sort and group results
roam get --tag Project --sort created --group-by tag
# Find references (backlinks) to a page
roam refs "Project Alpha"
# Update a block (e.g., toggle TODO status)
roam update ((block-uid)) --todo
# Multi-graph: read from a specific graph
roam get "Page Title" -g work
# Multi-graph: write to a protected graph
roam save "Note" -g work --write-key "$ROAM_SYSTEM_WRITE_KEY"
Comandos disponíveis: get, search, save, refs, update, batch, rename, status, server.
Execute roam <command> --help para detalhes sobre qualquer comando.
Instalação
npm install -g roam-research-mcp
# The 'roam' command is now available globally
Ferramentas do servidor MCP
O servidor MCP expõe estas ferramentas a assistentes de IA (como o Claude), permitindo que eles leiam, escrevam e organizem seu grafo do Roam de forma inteligente.
Suporte a multi-grafo: Todas as ferramentas aceitam parâmetros opcionais
graphewrite_key. Usegraphpara direcionar um grafo específico da sua configuraçãoROAM_GRAPHS, ewrite_keypara operações de escrita em grafos protegidos.
| Nome da ferramenta | Descrição |
|---|---|
roam_fetch_page_by_title | Busca o conteúdo de uma página pelo título. |
roam_fetch_page_full_view | Busca o conteúdo de uma página mais todas as referências vinculadas com contexto de breadcrumb e filhos. |
roam_fetch_block | Busca um bloco por UID com filhos opcionais (profundidade) e/ou ancestrais (até a raiz da página). |
roam_create_page | Cria novas páginas, opcionalmente com conteúdo misto de texto e tabela. |
roam_update_page_markdown | Atualiza uma página usando diff inteligente (preserva UIDs de blocos). |
roam_get_subpages | Lista subpáginas sob um prefixo de namespace (ex.: "Projeto/") com filtro de tag opcional. |
roam_search_by_text | Busca de texto completo no grafo ou dentro de páginas específicas. Suporta busca por prefixo de namespace para títulos de página. |
roam_search_block_refs | Encontra blocos que referenciam uma página, tag ou UID de bloco. |
roam_search_by_status | Encontra itens TODO ou DONE. |
roam_search_for_tag | Encontra blocos contendo tags específicas (suporta exclusão). |
roam_search_by_date | Encontra blocos/páginas por data de criação ou modificação. |
roam_find_pages_modified_today | Lista páginas modificadas desde a meia-noite. |
roam_add_todo | Adiciona itens TODO à página diária de hoje. |
roam_create_table | Cria tabelas do Roam formatadas corretamente. |
roam_create_outline | Cria esboços hierárquicos. |
roam_process_batch_actions | Executa múltiplas ações de baixo nível (criar, mover, atualizar, excluir) em um único lote. |
roam_move_block | Move um bloco para um novo pai ou posição. |
roam_remember / roam_recall | ferramentas especializadas para gerenciamento de memória de IA dentro do Roam. |
roam_datomic_query | Executa consultas Datalog brutas para filtragem avançada. |
roam_markdown_cheatsheet | Recupera a referência markdown no estilo Roam. |
roam_get_guidelines | Recupera as convenções de agente definidas pelo usuário deste grafo. |
Resultados estruturados de ferramentas de escrita (v3.0.0+)
As dez ferramentas de escrita declaram um outputSchema e retornam structuredContent — um objeto validado — junto com o texto usual. Um cliente pode ler page_uid, uid_map ou success diretamente em vez de procurar JSON dentro de uma string, o que torna chamadas em cadeia mais confiáveis:
// roam_process_batch_actions
{ "success": true, "uid_map": { "parent1": "Xk7mN2pQ9" },
"validation_passed": true, "actions_attempted": 4 }
Três coisas que vale a pena saber:
- Nada foi removido. O canal de texto está inalterado, então um cliente que ignora
structuredContentse comporta exatamente como antes. - Ferramentas de leitura deliberadamente não têm nenhum. Elas já serializam todo o resultado no canal de texto, então um schema apenas dobraria a carga útil.
- Esses campos são apenas aditivos. Alguns clientes validam respostas ao vivo contra uma lista de ferramentas em cache, então um campo será adicionado ou descontinuado — nunca renomeado ou removido fora de uma versão major.
Atualizando para 4.0.0: leituras markdown de uma página contendo uma quebra de linha suave (Shift+Enter) agora renderizam essa quebra como
⏎e carregam um marcador<!-- roam:escaped-newlines -->inicial, para que a página sobreviva a uma reescrita intacta. Páginas sem blocos de várias linhas são byte a byte idênticas à 3.x. Usuários de assistente de IA não precisam fazer nada; scripts que analisam a saída markdown de páginas de várias linhas veem a nova codificação. Veja o changelog.
Atualizando da 2.x: três campos de resultado de escrita foram renomeados —
uid→page_uid(roam_create_page),created_uids→created_blocks(roam_create_outline,roam_import_markdown) epreservedUids→preserved_uids(roam_update_page_markdown). Isso afeta apenas código que lê esses nomes; se você usa o servidor por meio de um assistente de IA, nada muda. Veja o changelog para saber o porquê.
Diretrizes de agente (por grafo)
roam_get_guidelines lê uma página dentro do grafo — [[roam/agent guidelines]] por padrão — contendo suas próprias convenções: como você marca, como você cria namespaces de páginas, o que um agente nunca deve fazer. O servidor MCP oficial do Roam lê o mesmo título de página, então uma página serve para ambos.
Isso é distinto de CUSTOM_INSTRUCTIONS_PATH, e os dois compõem:
CUSTOM_INSTRUCTIONS_PATH | [[roam/agent guidelines]] | |
|---|---|---|
| Vive em | um arquivo no disco | uma página no grafo |
| Escopo | servidor inteiro, todos os grafos | por grafo |
| Para alterar | edite o arquivo, reinicie o servidor | edite a página |
| Responde | como escrever markdown do Roam | como este usuário quer que este grafo seja tratado |
Basta criar a página. Sem nenhuma configuração, o roam_get_guidelines lê [[roam/agent guidelines]] — o mesmo título que o próprio servidor do Roam lê, então escrevê-lo uma vez faz ambos respeitarem. Criar uma página com esse título exato com namespace é o opt-in; nada é lido do grafo a menos que um agente chame explicitamente a ferramenta.
Se a página não existir, a ferramenta retorna exists: false em vez de falhar, então é sempre seguro chamá-la.
Ela também retorna as regras que não são suas para definir
Junto com suas convenções, cada resposta do roam_get_guidelines carrega um campo roamSyntax: a lista curta de coisas que destroem conteúdo — roam_update_page_markdown excluindo todo bloco que seu markdown omite, prévias truncadas de structure gravadas de volta como se fossem conteúdo, referências de bloco redigitadas como texto simples — além de um aviso de que leituras excluem silenciosamente subárvores #.rm-hide, e os poucos lugares onde o markdown do Roam inverte o markdown padrão.
Duas razões para isso estar aqui em vez de na folha de dicas. Alcança todo cliente, incluindo um que nunca chama roam_markdown_cheatsheet; e é retornado mesmo quando um grafo não tem página de diretrizes, que é exatamente o caso em que um agente tem menos contexto. A camada é deliberada: suas convenções vencem no estilo, roamSyntax vence na segurança dos dados. Nenhuma convenção pode tornar uma prévia truncada completa.
A referência completa de sintaxe — componentes, consultas, embeds, seleção de ferramentas — fica em roam_markdown_cheatsheet. roamSyntax tem ~800 tokens e é deliberadamente limitada.
Cada grafo pode apontar para uma página diferente, ou desativá-la:
ROAM_GRAPHS='{
"personal": {"token": "...", "graph": "..."},
"work": {"token": "...", "graph": "...", "guidelinesPage": "work/agent rules"},
"private": {"token": "...", "graph": "...", "guidelinesPage": false}
}'
ROAM_GUIDELINES_PAGE='team/agent guidelines' # change the default for every graph
A ordem de resolução é por grafo guidelinesPage → ROAM_GUIDELINES_PAGE → roam/agent guidelines. Acima: personal usa a substituição de ambiente, work usa sua própria página, e private tem diretrizes totalmente desativadas. Apenas um false explícito a desativa — um valor não definido nunca o faz.
Os resultados são armazenados em cache por 30 segundos — uma edição na página tem efeito sem reiniciar. Um modelo inicial vive em .roam/agent-guidelines.template.md.
Observe que as diretrizes são lidas pelo caminho normal de página, então blocos marcados com #.rm-hide / #.rm-private também são omitidos delas — veja abaixo.
Leituras de uma página contendo uma quebra de linha suave a renderizam como ⏎ para que cada bloco
fique em uma linha — uma nova linha sem escape cai na coluna 0 e reparentaliza
tudo depois dela na gravação de volta. Essas cargas úteis carregam um marcador
<!-- roam:escaped-newlines --> inicial; mantenha-o se você gravar o markdown
de volta. Markdown que você autora nunca é decodificado: barras invertidas não são especiais, e
apenas ⏎ dentro de uma carga útil marcada é interpretado. A saída roam_get_guidelines
é prosa simples — sem sentinela, sem marcador.
Ocultando conteúdo da IA
Blocos marcados com #.rm-hide ou #.rm-private — e tudo aninhado sob eles — são omitidos do conteúdo que essas ferramentas retornam. Tanto a forma de hashtag (#.rm-hide, #[[.rm-hide]]) quanto a de link ([[.rm-hide]]) funcionam. .rm-private é a tag existente do Roam "oculto de outros usuários"; .rm-hide oculta da IA especificamente.
Isso segue a mesma convenção do servidor MCP oficial do Roam, então um bloco marcado para um é ocultado do outro.
Aplicado a: roam_fetch_page_by_title, roam_fetch_block, roam_fetch_page_full_view, roam_get_subpages, roam_search_by_text, roam_search_for_tag, roam_search_by_status, roam_search_block_refs, roam_search_hierarchy, roam_search_by_date.
Blocos ocultos também são excluídos do diff de reescrita de página, que é o que impede que sejam excluídos por estarem ausentes do markdown que o agente não poderia ter escrito. roam_update_page_markdown (e roam save --update) substitui uma página pelo que você fornece, excluindo o que seu markdown omite — então sua linha de base é podada por este mesmo filtro, na regra de que a linha de base da qual um diff exclui deve ser a mesma página que o chamador tinha permissão de ler. Ele relata preserved_hidden quando protegeu algo. O conteúdo é preservado; a ordenação exata em relação aos irmãos visíveis pode mudar. Isso era um bug real de perda de dados antes da correção — veja o changelog.
Este é um filtro de conveniência, não uma garantia de segurança. roam_datomic_query lê o banco de dados diretamente e deliberadamente não o aplica, então um agente capaz ainda pode revelar blocos ocultos através de Datalog bruto. Trate essas tags como "mantenha fora do caminho da IA", não "mantenha em segredo."
A correspondência de tags não diferencia maiúsculas de minúsculas, e apenas tags exatas correspondem — #.rm-hidden e #.rm-highlight são deixadas em paz. O conjunto de UIDs ocultos é armazenado em cache por 30 segundos, então um bloco marcado agora pode permanecer visível por até esse tempo.
Configuração
Variáveis de Ambiente
Modo de Grafo Único
Para um único grafo Roam, defina estas no seu ambiente ou em um arquivo .env:
ROAM_API_TOKEN=your-api-token
ROAM_GRAPH_NAME=your-graph-name
Modo Multi-Grafo (v2.0+)
Conecte-se a múltiplos grafos Roam a partir de uma única instância de servidor:
ROAM_GRAPHS='{
"personal": {"token": "token-1", "graph": "personal-db", "memoriesTag": "#[[Personal Memories]]"},
"work": {"token": "token-2", "graph": "work-db", "protected": true, "memoriesTag": "#[[Work Memories]]"},
"research": {"token": "token-3", "graph": "research-db"}
}'
ROAM_DEFAULT_GRAPH=personal
ROAM_SYSTEM_WRITE_KEY=your-secret-key
Opções de Configuração de Grafo:
| Propriedade | Obrigatório | Descrição |
|---|---|---|
token | Sim | Token da API Roam para este grafo |
graph | Sim | Nome do grafo/identificador do banco de dados |
protected | Não | Se true, gravações exigem confirmação ROAM_SYSTEM_WRITE_KEY — exceto no grafo padrão, veja abaixo |
memoriesTag | Não | Tag para roam_remember/roam_recall (substitui o padrão global) |
Dois tipos de controle de acesso (e como eles diferem)
O servidor tem duas travas independentes. É fácil confundi-las porque ambas são "chaves" — aqui está a versão simples (ambas são opcionais e desativadas por padrão):
Token Bearer — HTTP_AUTH_TOKEN | Chave de gravação — ROAM_SYSTEM_WRITE_KEY | |
|---|---|---|
| Em uma frase | A chave da porta da frente | A trava de um cofre dentro |
| Controla | Quem pode alcançar o servidor | Se uma gravação em um grafo protected é permitida |
| Cobre | Tudo — leituras e gravações, todos os grafos | Apenas gravações, e somente em grafos marcados protected |
| Protege leituras? | Sim | Não |
| Quando você precisa | Apenas se o servidor for alcançável além da sua própria máquina (ex.: -H 0.0.0.0) | Sempre que quiser uma proteção contra edições acidentais em grafos importantes |
| Como é enviado | Cabeçalho HTTP: Authorization: Bearer <token> | Um argumento write_key em ferramentas de gravação / comandos CLI |
Pense em uma casa: o token bearer trava a porta da frente (mantém estranhos totalmente fora), e a chave de gravação trava um cofre dentro (mesmo alguém já na casa precisa dela para mudar o que está no cofre). Na sua própria máquina vinculada a 127.0.0.1, a porta da frente enfrenta uma parede — você não precisa do token bearer lá. A chave de gravação ainda é útil localmente como uma proteção "tem certeza?", porque o Roam não tem desfazer.
Então: para marcar um grafo como precisando da chave de gravação, defina protected: true nele e configure ROAM_SYSTEM_WRITE_KEY; chamadores então passam um write_key correspondente para qualquer gravação nesse grafo.
⚠️
protectednão faz nada no seu grafo padrão. Gravações no grafo queROAM_DEFAULT_GRAPHnomeia são sempre permitidas, antes deprotectedser consultado — a flag protege os grafos que você precisa pedir pelo nome, no raciocínio de que alcançar um grafo não padrão é o ato deliberado que vale confirmar. Se você quer um grafo protegido para gravação, ele não pode ser o seu padrão.
Opcional:
ROAM_MEMORIES_TAG: Tag padrão pararoam_remember/roam_recall(fallback quandomemoriesTagpor grafo não está definido).HTTP_STREAM_PORT: Porta para o transporte HTTP Stream (padrão 8088). Apenas modo--server— o modo stdio não abre socket, então isso é ignorado lá.HTTP_STREAM_HOST: Host para vincular o transporte HTTP (padrão127.0.0.1, apenas loopback). Apenas modo--server. Defina como0.0.0.0para expor na LAN, e definaHTTP_AUTH_TOKENquando fizer isso.HTTP_AUTH_TOKEN: Token bearer opcional que trava o endpoint HTTP inteiro. Não definido = aberto (ok para loopback). Quando definido, toda solicitação MCP deve enviarAuthorization: Bearer <token>(GET /healthpermanece aberto). Use-o sempre que vincular além de127.0.0.1. Diferente deROAM_SYSTEM_WRITE_KEY— veja Dois tipos de controle de acesso.
Executando o Servidor
1. Modo Padrão (stdio) Melhor para integração local (ex.: Claude Desktop, extensões de IDE). O cliente MCP inicia o processo por sessão e fala com ele via stdin/stdout. Nenhuma porta é aberta — nada sobre MCP via stdio precisa de uma.
Antes da 3.1.0 este modo também abria um listener HTTP, e o vinculava a toda interface. Se você estava usando esse endpoint, execute um daemon
--server; veja abaixo.
npx roam-research-mcp
2. Modo de Servidor Compartilhado (--server)
Melhor para um daemon único de longa duração, apenas HTTP, que múltiplos clientes MCP compartilham — em vez de cada sessão gerar seu próprio subprocesso. Isso economiza memória e dá aos clientes uma URL estável.
HTTP_STREAM_PORT=8088 npx roam-research-mcp --server
Ou gerencie-o através do CLI roam, que adiciona start/stop/status/logs:
roam server start # start the shared daemon in the background
roam server start -H 0.0.0.0 # expose on the LAN (no transport auth!)
roam server status # is it up? version, graphs, active sessions
roam server logs -f # follow the log
roam server stop # stop a CLI-started daemon
roam server status funciona não importa como o daemon foi iniciado (ele sonda /health), então também relata um daemon iniciado por uma unidade LaunchAgent/systemd. Estado (pidfile + log) vive em ~/.roam/ (substitua com ROAM_HOME).
Os dois modos são mutuamente exclusivos, e cada um abre exatamente um transporte: o modo stdio fala stdio e não vincula nada, --server fala HTTP e não lê stdin. No modo --server o servidor:
- executa apenas HTTP (sem transporte stdio),
- vincula a exata
HTTP_STREAM_PORTemHTTP_STREAM_HOSTe sai com código não zero se a porta estiver ocupada (sem deriva silenciosa — um daemon compartilhado deve manter uma URL estável), - expõe
GET /health→{"status":"ok", ...}para verificações de vivacidade.
Aponte clientes MCP para ele com uma configuração de transporte HTTP:
{
"mcpServers": {
"roam-research-mcp": {
"type": "http",
"url": "http://127.0.0.1:8088/mcp"
}
}
}
Variáveis de ambiente (tokens, grafos) vivem com o processo do servidor, não na configuração do cliente.
Protegendo um servidor exposto (duas camadas):
Se você vincular além do loopback (-H 0.0.0.0), adicione a trava de perímetro:
HTTP_AUTH_TOKEN=$(openssl rand -hex 32) roam server start -H 0.0.0.0
Clientes então enviam o token como cabeçalho:
{
"mcpServers": {
"roam-research-mcp": {
"type": "http",
"url": "http://<host>:8088/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
Mantenha ambos — eles fazem trabalhos diferentes (veja Dois tipos de controle de acesso acima): o token bearer controla quem pode conectar, a chave de gravação apenas protege gravações em grafos protegidos.
⚠️ A chave de gravação não é um substituto para o token bearer. Em um servidor exposto sem
HTTP_AUTH_TOKEN, qualquer pessoa na rede ainda pode ler todos os grafos (e gravar nos não protegidos). Para qualquer coisa além do loopback, definaHTTP_AUTH_TOKEN.
Mantendo-o em execução (LaunchAgent do macOS):
Crie ~/Library/LaunchAgents/com.example.roam-mcp.plist com RunAtLoad + KeepAlive, suas variáveis de ambiente sob EnvironmentVariables, e --server como a última entrada ProgramArguments. Mantenha StandardOutPath/StandardErrorPath em um caminho local (ex.: ~/Library/Logs/), então:
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.roam-mcp.plist
curl -s http://127.0.0.1:8088/health # verify
3. Docker
docker run -p 8088:8088 --env-file .env roam-research-mcp --server
Configurando em LLMs
Claude Desktop / Cline:
Adicione ao seu arquivo de configurações MCP (ex.: ~/Library/Application Support/Claude/claude_desktop_config.json):
Fixando a versão.
npx -y roam-research-mcpbusca a versão mais recente toda vez que seu cliente inicia o servidor, então uma nova versão principal chega sem aviso. Fixe a versão principal para decidir por conta própria quando migrar:
argsVocê recebe ["-y", "roam-research-mcp"]Mais recente, sempre — incluindo a próxima versão principal ["-y", "roam-research-mcp@3"]Apenas 3.x; versões principais exigem uma edição aqui ["-y", "roam-research-mcp@3.0.0"]Exatamente esta compilação Fixar a versão principal é o padrão sensato: você ainda recebe correções e novas ferramentas, mas uma mudança que quebra algo se torna algo que você opta por adotar. Os exemplos abaixo permanecem sem fixação para corresponder ao que a maioria das pessoas cola primeiro.
Gráfico Único:
{
"mcpServers": {
"roam-research": {
"command": "npx",
"args": ["-y", "roam-research-mcp"],
"env": {
"ROAM_API_TOKEN": "your-token",
"ROAM_GRAPH_NAME": "your-graph"
}
}
}
}
Multi-Gráfico:
{
"mcpServers": {
"roam-research": {
"command": "npx",
"args": ["-y", "roam-research-mcp"],
"env": {
"ROAM_GRAPHS": "{\"personal\":{\"token\":\"token-1\",\"graph\":\"personal-db\",\"memoriesTag\":\"#[[Memories]]\"},\"work\":{\"token\":\"token-2\",\"graph\":\"work-db\",\"protected\":true}}",
"ROAM_DEFAULT_GRAPH": "personal",
"ROAM_SYSTEM_WRITE_KEY": "your-secret-key"
}
}
}
}
Analisador de Bloco de Consulta (v2.11.0+)
Um utilitário para analisar e executar blocos de consulta do Roam programaticamente. Converte a sintaxe {{[[query]]: ...}} em consultas Datalog.
Cláusulas Suportadas
| Cláusula | Sintaxe | Descrição |
|---|---|---|
| Referência de página | [[page]] | Blocos que referenciam uma página |
| Referência de bloco | ((uid)) | Blocos que referenciam um bloco |
and | {and: [[a]] [[b]]} | Todas as condições devem corresponder |
or | {or: [[a]] [[b]]} | Qualquer condição corresponde |
not | {not: [[tag]]} | Excluir correspondências |
between | {between: [[date1]] [[date2]]} | Filtro de intervalo de datas |
search | {search: text} | Pesquisa de texto completo |
daily notes | {daily notes: } | Apenas páginas de notas diárias |
by | {by: [[User]]} | Criado ou editado por usuário |
created by | {created by: [[User]]} | Criado por usuário |
edited by | {edited by: User} | Editado por usuário |
Datas Relativas
A cláusula between suporta datas relativas: today, yesterday, last week, last month, this year, 7 days ago, 2 months ago, etc.
Uso
import { QueryExecutor } from 'roam-research-mcp/query';
const executor = new QueryExecutor(graph);
// Execute a query
const results = await executor.execute(
'{{[[query]]: "My Query" {and: [[Project]] {between: [[last month]] [[today]]}}}}'
);
// Parse without executing (for debugging)
const { name, query } = QueryParser.parseWithName(queryBlock);
Funções Utilitárias
import { isQueryBlock, extractQueryBlocks } from 'roam-research-mcp/query';
// Detect if text is a query block
isQueryBlock('{{[[query]]: [[tag]]}}'); // true
// Extract all query blocks from a string
extractQueryBlocks(pageContent); // ['{{[[query]]: ...}}', ...]
Suporte
Se este projeto ajuda você a gerenciar sua base de conhecimento ou criar agentes legais, considere me pagar um café! Isso ajuda a manter as atualizações chegando.
Licença
Licença MIT - Criado por Ian Shen.
