Obsidian MCP Server
Gerencia notas e arquivos em um vault do Obsidian. Requer o plugin Obsidian Local REST API.
Documentação
obsidian-mcp-server
Leia, escreva, pesquise e edite cirurgicamente notas, tags e frontmatter do cofre Obsidian via MCP. STDIO ou Streamable HTTP.
Visão geral
Leia, escreva, pesquise e edite cirurgicamente notas do cofre Obsidian — seções, frontmatter, tags — por meio do plugin Local REST API, com permissões de leitura/escrita com escopo de pasta incorporadas. Executa como um processo stdio ou um servidor HTTP Streamable local.
Ferramentas
| Ferramenta | Descrição |
|---|---|
obsidian_get_note | Leia uma nota como conteúdo bruto, forma estruturada completa (conteúdo + frontmatter + tags + stat, com links de saída opcionais), mapa estrutural do documento ou uma única seção. |
obsidian_list_notes | Liste notas e subdiretórios sob um caminho do cofre. Caminhada recursiva (profundidade padrão 2, profundidade máxima 20; limite de 1000 entradas) com filtros opcionais extension e nameRegex. |
obsidian_list_tags | Liste tags do cofre com contagens de uso, incluindo pais hierárquicos. Ordenado por contagem decrescente e limitado a limit (padrão 200, máximo 10000), com o restante retido divulgado. nameRegex e minCount opcionais restringem o conjunto primeiro. |
obsidian_list_commands | Liste comandos da paleta de comandos do Obsidian, opcionalmente filtrados por nameRegex no nome de exibição. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true (emparelhado com obsidian_execute_command). |
obsidian_search_notes | Pesquise no cofre por texto, JSONLogic ou Omnisearch classificado por BM25 (quando o plugin está acessível). Os resultados paginam via cursores opacos. |
obsidian_write_note | Crie uma nota, substitua uma única seção no lugar ou — com overwrite: true — sobrescreva um arquivo existente. Recusa gravações de arquivo inteiro em um caminho existente por padrão. |
obsidian_append_to_note | Acrescente conteúdo a uma nota. Sem section, cria o arquivo se estiver ausente. Com section, acrescenta a um cabeçalho, bloco ou campo de frontmatter específico (o arquivo deve existir). |
obsidian_patch_note | append / prepend / replace cirúrgico contra um cabeçalho, referência de bloco ou campo de frontmatter. |
obsidian_replace_in_note | Substituição de pesquisa dentro de uma única nota, com escopo no corpo por padrão. Correspondência literal ou regex com opções de palavra inteira, flexibilidade de espaços em branco e sensibilidade a maiúsculas/minúsculas; suporta substituição de grupo de captura. |
obsidian_manage_frontmatter | get / set / delete atômico em uma única chave de frontmatter. |
obsidian_manage_tags | Adicione, remova ou liste tags. Padrão para o array tags: do frontmatter; location: 'inline' ou 'both' opta por modificar o corpo da nota. |
obsidian_delete_note | Exclua permanentemente uma nota. Sempre pede confirmação ao usuário primeiro — a chamada é respondida com uma solicitação de confirmação e repetida com a resposta. |
obsidian_open_in_ui | Abra um arquivo na interface do aplicativo Obsidian, com alternâncias failIfMissing e newLeaf. |
obsidian_execute_command | Execute um comando da paleta de comandos do Obsidian por ID. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true. |
Recursos
| Recurso | Descrição |
|---|---|
obsidian://vault/{+path} | Uma nota no cofre — conteúdo, frontmatter, tags e metadados do arquivo. |
obsidian://tags | Todas as tags encontradas no cofre, com contagens de uso (snapshot completo). |
obsidian://status | Acessibilidade do servidor, status de autenticação, informações de versão do plugin/Obsidian e extensões de API registradas. |
Dados de notas do cofre e tags também são acessíveis via ferramentas — obsidian_get_note para obsidian://vault/{+path}, obsidian_list_tags para obsidian://tags (classificado por contagem e limitado, ao contrário do snapshot bruto do recurso). obsidian://status não tem equivalente de ferramenta. Os recursos existem para clientes que preferem anexar uma nota ou snapshot do cofre a uma conversa.
Referência de capacidades
obsidian_get_note ferramenta
format: "content" | "full" | "document-map" | "section"seleciona a projeção;fullaceitaincludeLinks: truepara links wiki/markdown de saída (somente internos ao cofre — URLs externas são filtradas)- Endereçado pelo
pathdo cofre, o arquivoactiveou uma notaperiodic(daily/weekly/monthly/quarterly/yearly) - Seções de cabeçalho usam sintaxe
Parent::Childe encontram cabeçalhos da mesma forma que o mapa do documento — cabeçalhos setext (sublinhados com===ou---) contam, linhas#dentro de um item de lista, bloco HTML ou cerca não contam — então cada caminho de cabeçalho que o mapa lista é lido de volta como ele mesmo, no mesmo intervalo que uma gravação de seção edita; um nome de folha simples correspondendo a vários cabeçalhos, ou um caminho completo que se repete na nota, retorna a primeira correspondência e lista cada caminho conflitante emcandidates - Resolução
pathtolerante: um caminho com maiúsculas/minúsculas incompatíveis tenta novamente com o nome de arquivo canônico, uma correspondência ambígua de maiúsculas/minúsculas falha comConflict, e umNotFoundcarrega sugestõesDid you mean: …?quando existem correspondências próximas - Erros tipados incluem
note_missing,path_forbidden,no_active_file,periodic_unsupported/periodic_disabledepath_traversal
obsidian_list_notes ferramenta
- Caminhada recursiva de
path(padrão raiz do cofre);depth1–20 (padrão 2 = alvo mais filhos imediatos) - Filtros opcionais
extensionenameRegex(≤256 caracteres, sem quantificadores aninhados); um diretório que falhanameRegexé ignorado sem recursar nele - Limite rígido de 1000 entradas por chamada —
excluded.reason: "entry_cap"sinaliza uma caminhada truncada; restrinjapathou os filtros para ver o restante truncated: truepor diretório marca entradas cortadas pelo limite de profundidade ou pela política de caminho
obsidian_list_tags ferramenta
- Contagens de tags em todo o cofre, incluindo pais hierárquicos (
work/taskscontribui tanto paraworkquanto parawork/tasks) - Ordenado por contagem decrescente, limitado a
limit(padrão 200, máximo 10000);nameRegexeminCountopcionais restringem o conjunto candidato antes da classificação - Relata
truncated/shown/capquando o limite reteve resultados - Não restringido por
OBSIDIAN_READ_PATHS— nomes de tags (nunca conteúdos de notas) podem surgir de fora do escopo de leitura
obsidian_list_commands ferramenta
- Lista IDs e nomes de exibição da paleta de comandos do Obsidian;
nameRegexopcional filtra no nome de exibição - Opt-in via
OBSIDIAN_ENABLE_COMMANDS=true— ausente detools/listquando não definido - Parceiro de descoberta para
obsidian_execute_command
obsidian_search_notes ferramenta
mode: "text" | "jsonlogic"sempre;"omnisearch"é adicionado ao esquema somente quando o servidor HTTP do plugin Omnisearch está acessível na inicialização (reinicie para re-testar)text— tokens divididos por espaços em branco, todos obrigatórios, cada um correspondido sem diferenciar maiúsculas/minúsculas como substring (aspas são literais, então não há operador de frase), com janelas de contexto de tamanhocontextLength(padrão 100) e umpathPrefixopcional; tokens dentro de 2 ×contextLengthum do outro, como palavras de uma frase, compartilham um local de correspondência;jsonlogic— uma árvore JSONLogic com caminhosvarempath/content/frontmatter.<key>/tags/stat.{ctime,mtime,size}, além de operadoresglob/regexpque aceitam[PATTERN, VALUE];omnisearch— classificado por BM25, frases entre aspas, filtros-exclusion,path:/ext:, tolerância a erros de digitação, PDF/OCR via Text Extractor, limite rígido de 50 resultados upstream (truncated: truequando provavelmente atingido)- Paginação por cursor — omita
cursorpara a primeira página, passenextCursorda resposta anterior; resultados em modo texto também recortam paramaxMatchesPerHitlocais de correspondência (padrão 10), sinalizados comtruncated/totalMatches - Sem ferramenta dedicada de backlinks — expresse "o que linka aqui" via
jsonlogic:{"regexp": ["\\[\\[Target Note(\\||#|\\]\\])", {"var": "content"}]}
obsidian_write_note ferramenta
- Sem
section— gravação de arquivo inteiro; recusa sobrescrever uma nota existente a menos queoverwrite: true(conflitofile_existscaso contrário, nomeando as ferramentas de edição cirúrgica como alternativa) - Com
section—PATCH-com-substituição contra um alvo de cabeçalho/bloco/frontmatter, deixando o restante do arquivo intocado (overwriteé ignorado); uma folha de cabeçalho simples compartilhada por vários cabeçalhos falha comambiguous_sectiona menos que um deles não tenha cabeçalho pai, que a gravação então visa, e um caminho de cabeçalho completo que se repete na nota falha da mesma forma - A saída relata
created, além depreviousSizeInBytes/currentSizeInBytesem cada chamada para detectar uma sobrescrita acidental ou um caminho digitado incorretamente
obsidian_append_to_note ferramenta
- Sem
section— acrescenta a um arquivo existente, ou o cria com o conteúdo fornecido como corpo inteiro (created: truesinaliza o segundo caso) - Com
section— acrescenta a um alvo de cabeçalho/bloco/frontmatter; o arquivo deve já existir, ecreateTargetIfMissing: truetraz a própria seção à existência. No plugin v5.0 e posterior, conteúdo acrescentado a um cabeçalho é separado do conteúdo existente da seção por uma linha em branco (exceto um item de lista acrescentado a uma seção que termina em lista e não tem sub-cabeçalhos, que continua essa lista), e um cabeçalho nele deve ficar abaixo do nível da própria seção (heading_outside_sectioncaso contrário) - Alvos de referência de bloco concatenam sem separador — inclua uma nova linha inicial em
contentpara um previousSizeInBytes/currentSizeInBytesenvolvem cada chamada para detecção de desvio
obsidian_patch_note ferramenta
operation: "append" | "prepend" | "replace"contra um cabeçalho, referência de bloco ou campo de frontmatter por chamada; no plugin v5.0 e posterior, conteúdo acrescentado ou prefixado a um cabeçalho é separado do conteúdo existente da seção por uma linha em branco (exceto um item de lista acrescentado a uma seção que termina em lista, ou prefixado a uma que abre com lista, que continua essa lista), e um cabeçalho nele deve ficar abaixo do nível da própria seção (heading_outside_sectioncaso contrário)- Alvos de cabeçalho aceitam o caminho completo
Parent::Childou um nome de folha simples; uma folha correspondendo a vários cabeçalhos falha comambiguous_sectione lista os candidatos, a menos que um deles não tenha cabeçalho pai, que o patch então visa; um caminho completo que se repete na nota falha comambiguous_sectiontambém patchOptions:createTargetIfMissing,applyIfContentPreexists(proteção de idempotência — caso contráriocontent_preexists),trimTargetWhitespace(plugin v4.x apenas; v5.0 e posterior colocam as linhas em branco ao redor do conteúdo inserido por conta própria)
obsidian_replace_in_note ferramenta
- Uma ou mais
replacements, aplicadas em ordem de array, cada uma sobre a saída da anterior scope: "body"(padrão, frontmatter deixado byte-idêntico) |"frontmatter"|"both"; frontmatter/ambos re-analisam o YAML reescrito depois e não escrevem nada se quebrar (frontmatter_invalid)- Opções por substituição:
useRegex(≤1024 caracteres, sem quantificadores aninhados),caseSensitive,wholeWord(\b…\bem ambos os modos),flexibleWhitespace(somente modo literal),replaceAll(padrãotrue) perReplacement[]relatabodyCount/frontmatterCountpor entrada;totalReplacementsos soma
obsidian_manage_frontmatter tool
operation: "get" | "set" | "delete"em um únicokeyde frontmatter;setexige umvaluetipado como JSON (string, número, booleano, array ou objeto)getprecisa de acesso de leitura;set/deleteprecisam do caminho dentro deOBSIDIAN_WRITE_PATHScomOBSIDIAN_READ_ONLY=falseset/deleteretornam ofrontmattercompleto após a alteração, além depreviousSizeInBytes/currentSizeInBytes
obsidian_manage_tags tool
operation: "add" | "remove" | "list";location: "frontmatter"(padrão, array canônico detags:) |"inline"(corpo#tag,addacrescenta ao final do arquivo) |"both"(reconcilia ambos)- A detecção inline ignora código (cercado, indentado e inline), wikilinks (
[[...]]), imagens, o destino ou o rótulo de um link markdown (seu texto é lido), blocos e comentários HTML e matemática ($…$,$$…$$), de modo que uma âncora de cabeçalho ou um alias de wikilink nunca seja confundido com uma tag; comentários%% … %%ainda são lidos, assim como o Obsidian os lê - Tags inline seguem a gramática do Obsidian: uma tag começa no início da linha, após espaços em branco, após outra tag (
#a#bsão duas tags) ou logo após marcação como**,_…_,==,[, o|de uma célula de tabela,<br>ou um escape de\(**#x**é uma tag;(#x,.#x,a *#xe\#xnão são) e percorre letras e dígitos em qualquer script, emoji,_,-e/, com pelo menos um caractere que não seja um dígito ASCII (#1990s,#café,#日本語e#✅donesão tags;#1984não é) add/removerelatam tagsappliedvs.skippedalém do conjunto completo detagsapós a alteração;listignora o array detagsde entrada
obsidian_delete_note tool
- Sempre pede confirmação primeiro — a chamada inicial retorna uma solicitação de elicitação informando o tamanho do arquivo em bytes e é repetida com a resposta; recusar falha com
cancellede não emite nenhumDELETE - Não há desfazer no nível da API — a recuperação exige a lixeira local do Obsidian
- Exige um cliente MCP que possa atender a uma ida e volta de elicitação; todas as outras ferramentas funcionam sem isso
obsidian_open_in_ui tool
failIfMissing(padrãotrue) controla abrir-vs-criar: abrir um arquivo existente exige acesso de leitura; abrir um arquivo ausente (comfailIfMissing: false) o cria e exige acesso de escritanewLeafabre em um painel dividido em vez do ativo- Mesma resolução de caminho tolerante que
obsidian_get_note(fallback de maiúsculas/minúsculas, sugestões deDid you mean);obsidian_delete_notedeliberadamente não a recebe — uma operação destrutiva nunca reescreve silenciosamente seu alvo - A saída relata
createdIfMissingpara que o chamador possa saber qual ramo foi executado
obsidian_execute_command tool
- Despacha um comando da paleta de comandos do Obsidian por
commandId(descubra viaobsidian_list_commands); executa com a mesma autoridade de uma invocação por teclado - Opt-in via
OBSIDIAN_ENABLE_COMMANDS=true— ausente detools/listquando não definido - O comportamento depende do comando — alguns são destrutivos (excluir arquivo, fechar cofre), alguns abrem interface
obsidian://vault/{+path} resource
- O segmento
{+path}captura tudo após/vault/, incluindo barras - Os caminhos podem ser enviados literalmente ou codificados em percentual —
Folder/Test Note.mdeFolder/Test%20Note.mdresolvem para a mesma nota, assim como nomes não ASCII e um%simples - Retorna a mesma forma que
obsidian_get_notecomformat: "full"— conteúdo, frontmatter, tags, stat - Controlado por
OBSIDIAN_READ_PATHS/OBSIDIAN_WRITE_PATHScomo o equivalente de ferramenta
obsidian://tags resource
- Snapshot completo do payload upstream de
/tags/— não ordenado, sem limite, inclui pais hierárquicos - Não é um espelho de
obsidian_list_tags: sem ordem decrescente de contagem, semlimit/nameRegex/minCount
obsidian://status resource
- Acessibilidade, versão do plugin,
authenticated(se oOBSIDIAN_API_KEYconfigurado foi aceito) e informações do manifesto do plugin apiExtensions[]lista extensões de plugin registradas — verifiquelocal-rest-api-periodic-notesantes de confiar em alvos deperiodicno plugin v5.0.2 e posterior- Ainda relata acessibilidade quando a chave de API está mal configurada; apenas
authenticatedreflete a validade da chave
Política de caminhos (permissões com escopo de pasta)
Três variáveis de ambiente opcionais controlam quais caminhos do cofre cada ferramenta pode acessar. Padrão não definido = cofre inteiro para leituras e escritas — compatível com versões anteriores.
| Objetivo | Configuração |
|---|---|
| Padrão (comportamento atual) | todas não definidas |
Ler em qualquer lugar, escrever apenas em projects/ e scratch/ | OBSIDIAN_WRITE_PATHS=projects/,scratch/ |
Ler apenas public/, escrever apenas public/inbox/ | OBSIDIAN_READ_PATHS=public/, OBSIDIAN_WRITE_PATHS=public/inbox/ |
| Implantação somente leitura — sem escritas em qualquer lugar | OBSIDIAN_READ_ONLY=true |
A correspondência é baseada em prefixo com recursão implícita, sem diferenciar maiúsculas de minúsculas, com barras finais normalizadas. projects/ corresponde a projects/a.md, projects/sub/b.md, etc.
Caminhos de escrita são implicitamente legíveis — você não pode editar de forma sensata o que não pode ver. Portanto, uma leitura passa quando o alvo corresponde a READ_PATHS ou WRITE_PATHS.
OBSIDIAN_READ_ONLY=true entra em curto-circuito antes das verificações de caminho — toda ferramenta de escrita e o par da paleta de comandos são envolvidos com disabledTool() na inicialização (ausente de tools/list), e qualquer escrita que ainda chegue ao serviço é negada em tempo de execução, independentemente de WRITE_PATHS.
As negações são tipadas como path_forbidden (código JSON-RPC Forbidden) com o escopo ativo ecoado de volta em data.recovery.hint e data.activeScope, para que o LLM possa se autocorrigir sem inspecionar os logs do servidor. Os resultados de pesquisa de obsidian_search_notes são filtrados contra READ_PATHS silenciosamente — exibir um indicador de "ocultamos N resultados" derrotaria o controle.
A listagem de tags abrange todo o cofre. obsidian_list_tags e o recurso obsidian://tags agregam nomes de tags em todo o cofre e não são restringidos por OBSIDIAN_READ_PATHS — eles não recebem caminho para controlar, portanto, nomes de tags (nunca conteúdos de notas) de fora do escopo de leitura podem aparecer.
O banner de inicialização registra o escopo ativo para que os operadores possam verificar sua configuração na inicialização.
Recursos
Construído sobre @cyanheads/mcp-ts-core: transportes stdio e Streamable HTTP, autenticação plugável (none / jwt / oauth), armazenamento substituível (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), registro estruturado com rastreamento OpenTelemetry opcional.
Específico do Obsidian:
- Envolve o plugin Obsidian Local REST API — cliente tipado, mapeamento de erros determinístico
- Edição ciente de seções em cabeçalhos, referências de bloco e campos de frontmatter via operações
PATCH-com-alvo - Pesquisa em três modos — texto, JSONLogic e (quando acessível) Omnisearch classificado por BM25 — paginada por cursor conforme a especificação MCP 2025-11-25
- Reconciliação de tags em ambas as representações: array de frontmatter
tags:e sintaxe inline#tag - Permissões de leitura/escrita com escopo de pasta via
OBSIDIAN_READ_PATHS/OBSIDIAN_WRITE_PATHSe um interruptor globalOBSIDIAN_READ_ONLY; par da paleta de comandos opt-in controlado porOBSIDIAN_ENABLE_COMMANDS.instructionsno nível do servidor eminitializerelatam a política ativa ao chamador
Saída amigável para agentes:
- Erros guiados por recuperação — toda falha declarada carrega um
reason, um código JSON-RPC e umrecovery.hintescrito para esse caso, de modo que uma rejeição nomeia o que fazer em seguida em vez de apenas o que quebrou - Autocorreção por delta de tamanho — toda ferramenta mutável retorna
previousSizeInBytes/currentSizeInBytes, para que um chamador possa detectar uma sobrescrita acidental ou comportamento upstream inesperado sem uma leitura de acompanhamento - Ambiguidade exposta estruturalmente — um nome de folha de cabeçalho compartilhado por vários cabeçalhos retorna
candidatesem vez de escolher silenciosamente um; operações de tag relatamappliedvs.skippedpara que um chamador veja exatamente o que mudou - Contratos de saída discriminados —
formatemobsidian_get_note,operationemobsidian_manage_frontmattereobsidian_manage_tags,modeemobsidian_search_notes— chamadores ramificam em campos tipados em vez de analisar texto
Começando
Adicione o seguinte ao arquivo de configuração do seu cliente MCP. O plugin Obsidian Local REST API deve estar instalado e habilitado no seu cofre — veja Pré-requisitos.
{
"mcpServers": {
"obsidian-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["obsidian-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"OBSIDIAN_API_KEY": "your-local-rest-api-key"
}
}
}
}
Ou com npx (sem Bun necessário):
{
"mcpServers": {
"obsidian-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "obsidian-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"OBSIDIAN_API_KEY": "your-local-rest-api-key"
}
}
}
}
Ou com Docker:
{
"mcpServers": {
"obsidian-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "MCP_LOG_LEVEL=info",
"-e", "OBSIDIAN_API_KEY=your-local-rest-api-key",
"ghcr.io/cyanheads/obsidian-mcp-server:latest"
]
}
}
}
O OBSIDIAN_BASE_URL padrão (http://127.0.0.1:27123) aponta para o loopback do próprio contêiner, não para o seu host — adicione -e OBSIDIAN_BASE_URL=http://host.docker.internal:27123 (Docker Desktop) ou execute com --network host (Linux) para que o contêiner possa alcançar o plugin.
Para Streamable HTTP, defina o transporte e inicie o servidor. Variáveis de ambiente inline funcionam para execuções únicas; para uso repetido, copie os valores para .env (veja .env.example) e execute bun run start:http.
MCP_TRANSPORT_TYPE=http OBSIDIAN_API_KEY=... bun run start:http
# Server listens at http://127.0.0.1:3010/mcp by default
Pré-requisitos
- Bun v1.4.0 ou superior (ou Node.js v24+).
- O plugin Obsidian Local REST API, v4.0.0 ou posterior, instalado e habilitado no seu cofre. Gere uma chave de API em Configurações → Plugins da Comunidade → Local REST API e copie-a para
OBSIDIAN_API_KEY. Escritas direcionadas a seções e o mapa de documentos falam markdown-patch 2.0 para o plugin v5.0 e posterior e o formato 1.x para v4.x; o servidor lê a versão do plugin uma vez e escolhe o formato por conta própria. Duas escritas de linha de tabela (contentType: "json") que o markdown-patch 2.0 não pode expressar saem como 1.x também no v5.x: linhas escritas sob um cabeçalho e linhas escritas por meio de um ID de bloco em sua própria linha abaixo da tabela. O plugin v6.0 remove o 1.x, então no v6.0 essas duas formas falham; direcione a tabela por um ID em sua última linha. - Alvos de nota periódica (
target: { "type": "periodic" }) funcionam em toda essa faixa: nativamente no plugin v5.0.1 e anterior, e no v5.0.2 e posterior — que moveu as rotas de/periodic/para fora do plugin — uma vez que a extensão complementar periodic-notes API extension esteja instalada. Sem essa extensão no v5.0.2+, alvos periódicos falham com um erroperiodic_unsupportednomeando-a;obsidian://statuslista as extensões registradas se você quiser verificar primeiro. Todos os outros tipos de alvo não são afetados. - Um cliente MCP que possa responder a uma solicitação de entrada (elicitação).
obsidian_delete_notesempre pede confirmação antes de excluir, então um cliente sem esse suporte pode ler e escrever notas, mas não pode excluir uma. - Este servidor usa como padrão
http://127.0.0.1:27123por simplicidade. Habilite "Servidor não criptografado (HTTP)" nas configurações do plugin para usá-lo. Para usar a porta HTTPS sempre ativa, definaOBSIDIAN_BASE_URL=https://127.0.0.1:27124; o certificado autoassinado do plugin é tratado porOBSIDIAN_VERIFY_SSL=false(o padrão), que relaxa a verificação para as solicitações deste servidor apenas para esse endpoint.
Instalação
-
Clone o repositório:
git clone https://github.com/cyanheads/obsidian-mcp-server.git -
Navegue para o diretório:
cd obsidian-mcp-server -
Instale as dependências:
bun install -
Configure o ambiente:
cp .env.example .env # edit .env and set OBSIDIAN_API_KEY
Configuração
| Variável | Descrição | Padrão |
|---|---|---|
OBSIDIAN_API_KEY | Obrigatório. Token Bearer para o plugin Obsidian Local REST API. | — |
OBSIDIAN_BASE_URL | URL base do plugin Local REST API. Use https://127.0.0.1:27124 para a porta HTTPS sempre ativa (certificado autoassinado). Uma barra final é removida na inicialização. Quando nada responde lá (Obsidian fechado, plugin desabilitado, host ou porta errados), as chamadas falham com obsidian_unreachable — um GET, PUT ou DELETE após suas tentativas, qualquer outra solicitação na primeira tentativa. | http://127.0.0.1:27123 |
OBSIDIAN_VERIFY_SSL | Verifica o certificado TLS. Padrão false porque o plugin usa um certificado autoassinado. A flexibilização é aplicada por solicitação, apenas a um https: OBSIDIAN_BASE_URL — todas as outras conexões HTTPS que o processo faz ainda verificam normalmente, tanto no Bun quanto no Node. Com true, um certificado que o runtime não confia faz todas as chamadas falharem na primeira tentativa com certificate_rejected. | false |
OBSIDIAN_REQUEST_TIMEOUT_MS | Tempo limite por solicitação em milissegundos. | 30000 |
OBSIDIAN_ENABLE_COMMANDS | Sinalizador opcional para o par da paleta de comandos (obsidian_list_commands + obsidian_execute_command). Desativado por padrão — os comandos do Obsidian são opacos e podem ser destrutivos. | false |
OBSIDIAN_READ_PATHS | Lista de permissões de pastas relativas ao cofre, separadas por vírgula, para operações de leitura. Baseada em prefixo com recursão implícita; insensível a maiúsculas/minúsculas; barras finais normalizadas. Não definido = cofre completo. Caminhos de escrita são implicitamente legíveis. | não definido |
OBSIDIAN_WRITE_PATHS | Lista de permissões de pastas relativas ao cofre, separadas por vírgula, para operações de escrita. Mesma sintaxe que OBSIDIAN_READ_PATHS. Não definido = cofre completo. | não definido |
OBSIDIAN_READ_ONLY | Interruptor global de desligamento. Quando true, nega toda escrita independentemente de OBSIDIAN_WRITE_PATHS e suprime o par OBSIDIAN_ENABLE_COMMANDS (comandos podem mutar). | false |
OBSIDIAN_OMNISEARCH_URL | URL de substituição para o servidor HTTP do plugin Omnisearch. Quando não definido, deriva do host OBSIDIAN_BASE_URL com porta 51361 (recorrendo a http://localhost:51361). Verificado uma vez na inicialização — se acessível, o modo omnisearch é adicionado a obsidian_search_notes; caso contrário, é omitido do esquema da ferramenta. Reinicie o servidor para verificar novamente. | derivado |
MCP_TRANSPORT_TYPE | Transporte: stdio ou http. | stdio |
MCP_HTTP_HOST | Host para o servidor HTTP. | 127.0.0.1 |
MCP_HTTP_PORT | Porta para o servidor HTTP. | 3010 |
MCP_HTTP_ENDPOINT_PATH | Caminho do endpoint para o manipulador JSON-RPC. | /mcp |
MCP_SESSION_MODE | Gerenciamento de sessão para o transporte HTTP: stateless, stateful ou auto. Padrão stateful aqui — obsidian_delete_note confirma por meio de uma rodada de elicitação, e sob stateless a rodada de um cliente da era 2025 é recusada (client_capability_missing). | stateful |
MCP_PUBLIC_URL | Substituição de origem pública para implantações de proxy reverso com terminação TLS (página inicial, Server Card, metadados RFC 9728). | não definido |
MCP_AUTH_MODE | Modo de autenticação: none, jwt ou oauth. | none |
MCP_AUTH_SECRET_KEY | Obrigatório quando MCP_AUTH_MODE=jwt. Segredo compartilhado com ≥32 caracteres usado para verificar JWTs recebidos. | — |
MCP_AUTH_DISABLE_SCOPE_CHECKS | Quando true, ignora a aplicação de escopo por ferramenta após a verificação de presença do contexto de autenticação. A assinatura do token, público, emissor e validação de expiração permanecem intactos. Use apenas quando uma declaração personalizada não puder ser injetada e combine com OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS / OBSIDIAN_READ_ONLY para controle de acesso. Um WARNING é registrado na inicialização sempre que o bypass está ativo. | false |
MCP_LOG_LEVEL | Nível de log (RFC 5424). | info |
LOGS_DIR | Diretório para arquivos de log (somente Node.js). | <project-root>/logs |
OTEL_ENABLED | Habilita instrumentação OpenTelemetry (spans, métricas, logs de conclusão). | false |
Veja .env.example para a lista completa de substituições opcionais.
Executando o servidor
Desenvolvimento local
-
Compilar e executar a versão de produção:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Executar verificações e testes:
bun run devcheck # Lint, format, typecheck, security, changelog sync bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t obsidian-mcp-server .
docker run --rm -e OBSIDIAN_API_KEY=your-key -p 3010:3010 obsidian-mcp-server
O Dockerfile usa como padrão transporte HTTP, modo de sessão com estado (necessário para a rodada de confirmação obsidian_delete_note) e registra logs em /var/log/obsidian-mcp-server. Aponte OBSIDIAN_BASE_URL para http://host.docker.internal:27123 (Docker Desktop) ou execute com --network host (Linux) para que o contêiner alcance o plugin no seu host. Dependências pares do OpenTelemetry são instaladas por padrão — compile com --build-arg OTEL_ENABLED=false para omiti-las.
A imagem vincula a 0.0.0.0 dentro do contêiner (necessário para o mapeamento de porta do Docker). Para qualquer implantação acessível além da sua própria máquina, defina MCP_AUTH_MODE=jwt (com MCP_AUTH_SECRET_KEY) ou oauth — caso contrário, o ouvinte encaminha seu OBSIDIAN_API_KEY para o cofre em nome de cada chamador.
Estrutura do projeto
| Diretório | Finalidade |
|---|---|
src/index.ts | Ponto de entrada createApp() — registra ferramentas/recursos e inicializa o serviço do Obsidian. |
src/config | Análise de variáveis de ambiente específicas do servidor (OBSIDIAN_*) com Zod. |
src/services/obsidian | Cliente Local REST API, operações de frontmatter, extrator de seções, tipos de domínio. |
src/mcp-server/tools | Definições de ferramentas (*.tool.ts) e esquemas de entrada compartilhados. |
src/mcp-server/resources | Definições de recursos (*.resource.ts). |
src/mcp-server/prompts | Definições de prompts (atualmente vazio — a forma CRUD/busca não se beneficia de um modelo estruturado). |
tests/ | Testes Vitest espelhando src/. |
docs/ | Especificação OpenAPI upstream para o plugin Local REST API e o tree.md gerado. |
changelog/ | Notas de versão por versão; CHANGELOG.md é o rollup regenerado. |
Guia de desenvolvimento
Veja CLAUDE.md para diretrizes de desenvolvimento e regras arquiteturais. A versão curta:
- Manipuladores lançam, o framework captura — sem
try/catchna lógica da ferramenta - Use
ctx.logpara registro com escopo de solicitação,ctx.statepara armazenamento com escopo de locatário - Registre novas ferramentas e recursos por meio dos barris em
src/mcp-server/*/definitions/index.ts - Encapsule chamadas de API externas: valide bruto → normalize para tipo de domínio → retorne esquema de saída; nunca invente campos ausentes
Contribuindo
Bugs, solicitações de recursos e lacunas de documentação pertencem a uma issue — veja CONTRIBUTING.md para o que torna uma acionável, e CODE_OF_CONDUCT.md para como trabalhamos juntos. Relatórios de segurança passam por SECURITY.md, nunca por uma issue pública.
Execute verificações e testes antes de enviar:
bun run devcheck
bun run test
Licença
Apache-2.0 — veja LICENSE para detalhes.