@leonardocrdso/obsidian-mcp 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/build/index.js +55 -51
  2. package/package.json +2 -1
package/build/index.js CHANGED
@@ -6506,6 +6506,7 @@ __export(exports_setup, {
6506
6506
  loadSavedConfig: () => loadSavedConfig
6507
6507
  });
6508
6508
  import { writeFileSync, readFileSync, existsSync } from "fs";
6509
+ import { createInterface } from "readline";
6509
6510
  import { homedir } from "os";
6510
6511
  import { join } from "path";
6511
6512
  function loadSavedConfig() {
@@ -6517,74 +6518,64 @@ function loadSavedConfig() {
6517
6518
  return null;
6518
6519
  }
6519
6520
  }
6520
- function write(text) {
6521
- process.stderr.write(text);
6521
+ function mask(value) {
6522
+ if (value.length <= 8)
6523
+ return "*".repeat(value.length);
6524
+ return value.slice(0, 4) + "*".repeat(value.length - 4);
6522
6525
  }
6523
- function createLineReader() {
6526
+ function createPrompt() {
6524
6527
  const lines = [];
6525
6528
  let waiting = null;
6526
- let buffer = "";
6527
- process.stdin.setEncoding("utf-8");
6528
- process.stdin.on("data", (chunk) => {
6529
- buffer += chunk;
6530
- const parts = buffer.split(`
6531
- `);
6532
- buffer = parts.pop();
6533
- for (const line of parts) {
6534
- if (waiting) {
6535
- const resolve = waiting;
6536
- waiting = null;
6537
- resolve(line.trim());
6538
- } else {
6539
- lines.push(line.trim());
6540
- }
6529
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
6530
+ rl.on("line", (line) => {
6531
+ if (waiting) {
6532
+ const resolve = waiting;
6533
+ waiting = null;
6534
+ resolve(line);
6535
+ } else {
6536
+ lines.push(line);
6541
6537
  }
6542
6538
  });
6543
- process.stdin.on("end", () => {
6544
- if (buffer && waiting) {
6539
+ rl.on("close", () => {
6540
+ if (waiting) {
6545
6541
  const resolve = waiting;
6546
6542
  waiting = null;
6547
- resolve(buffer.trim());
6548
- buffer = "";
6543
+ resolve("");
6549
6544
  }
6550
6545
  });
6551
- return (label, fallback) => {
6546
+ const ask = (label, fallback) => {
6552
6547
  const suffix = fallback ? ` [${fallback}]` : "";
6553
- write(` ${label}${suffix}: `);
6548
+ process.stderr.write(` ${label}${suffix}: `);
6554
6549
  if (lines.length > 0) {
6555
6550
  const value = lines.shift();
6556
- write(value + `
6551
+ process.stderr.write(value + `
6557
6552
  `);
6558
- return Promise.resolve(value || fallback || "");
6553
+ return Promise.resolve(value.trim() || fallback || "");
6559
6554
  }
6560
6555
  return new Promise((resolve) => {
6561
- waiting = (value) => resolve(value || fallback || "");
6556
+ waiting = (value) => resolve(value.trim() || fallback || "");
6562
6557
  });
6563
6558
  };
6564
- }
6565
- function mask(value) {
6566
- if (value.length <= 8)
6567
- return "*".repeat(value.length);
6568
- return value.slice(0, 4) + "*".repeat(value.length - 4);
6559
+ return { ask, close: () => rl.close() };
6569
6560
  }
6570
6561
  async function runSetup() {
6571
6562
  const existing = loadSavedConfig();
6572
- const prompt = createLineReader();
6573
- write(`
6563
+ const { ask, close } = createPrompt();
6564
+ process.stderr.write(`
6574
6565
  Obsidian MCP — Configuracao
6575
6566
 
6576
6567
  `);
6577
- const apiKey = await prompt("API Key", existing?.apiKey ? mask(existing.apiKey) : undefined);
6578
- const host = await prompt("Host", existing?.host ?? "127.0.0.1");
6579
- const port = await prompt("Porta", existing?.port ?? "27124");
6580
- const protocol = await prompt("Protocolo", existing?.protocol ?? "https");
6581
- process.stdin.destroy();
6568
+ const apiKey = await ask("API Key", existing?.apiKey ? mask(existing.apiKey) : undefined);
6569
+ const host = await ask("Host", existing?.host ?? "127.0.0.1");
6570
+ const port = await ask("Porta", existing?.port ?? "27124");
6571
+ const protocol = await ask("Protocolo", existing?.protocol ?? "https");
6572
+ close();
6582
6573
  if (!apiKey || apiKey.includes("*")) {
6583
6574
  if (existing?.apiKey && apiKey.includes("*")) {
6584
6575
  saveConfig({ apiKey: existing.apiKey, host, port, protocol });
6585
6576
  return;
6586
6577
  }
6587
- write(`
6578
+ process.stderr.write(`
6588
6579
  API Key e obrigatoria.
6589
6580
 
6590
6581
  `);
@@ -6594,13 +6585,13 @@ async function runSetup() {
6594
6585
  }
6595
6586
  function saveConfig(config2) {
6596
6587
  writeFileSync(CONFIG_PATH, JSON.stringify(config2, null, 2));
6597
- write(`
6588
+ process.stderr.write(`
6598
6589
  Configuracao salva em ${CONFIG_PATH}
6599
6590
  `);
6600
- write(`
6591
+ process.stderr.write(`
6601
6592
  Adicione ao Claude Code:
6602
6593
  `);
6603
- write(` claude mcp add obsidian -- npx @leonardocrdso/obsidian-mcp
6594
+ process.stderr.write(` claude mcp add obsidian -- npx @leonardocrdso/obsidian-mcp
6604
6595
 
6605
6596
  `);
6606
6597
  }
@@ -19608,9 +19599,10 @@ var STATUS_MESSAGES = {
19608
19599
  };
19609
19600
  function formatObsidianError(error2) {
19610
19601
  if (error2 instanceof ObsidianApiError) {
19611
- const baseMessage = STATUS_MESSAGES[error2.statusCode] ?? "Erro inesperado na API do Obsidian.";
19602
+ const isInvalidTarget = error2.statusCode === 400 && error2.message.includes("invalid-target");
19603
+ const baseMessage = isInvalidTarget ? "O alvo (heading, block ou frontmatter) não foi encontrado no arquivo. Verifique se o nome existe exatamente como especificado, ou use createTargetIfMissing: true." : STATUS_MESSAGES[error2.statusCode] ?? "Erro inesperado na API do Obsidian.";
19612
19604
  const parts = [`[${error2.statusCode}] ${baseMessage}`];
19613
- if (error2.message)
19605
+ if (!isInvalidTarget && error2.message)
19614
19606
  parts.push(`Detalhe: ${error2.message}`);
19615
19607
  return parts.join(`
19616
19608
  `);
@@ -19775,19 +19767,23 @@ function registerVaultTools(server, client) {
19775
19767
  targetType: exports_external.enum(["heading", "block", "frontmatter"]).describe("Tipo do alvo: heading, block ou frontmatter"),
19776
19768
  target: exports_external.string().describe("Identificador do alvo (nome do heading, ID do block, ou chave do frontmatter)"),
19777
19769
  targetDelimiter: exports_external.string().optional().describe("Delimitador para separar conteúdo inserido (ex: '\\n')"),
19778
- trimTargetWhitespace: exports_external.boolean().optional().describe("Remover espaços do target antes de comparar")
19770
+ trimTargetWhitespace: exports_external.boolean().optional().describe("Remover espaços do target antes de comparar"),
19771
+ createTargetIfMissing: exports_external.boolean().optional().describe("Criar o alvo se não existir no arquivo")
19779
19772
  }, safeTool(async (params) => {
19780
19773
  const headers = {
19781
19774
  "Content-Type": "text/markdown",
19782
19775
  Operation: params.operation,
19783
19776
  "Target-Type": params.targetType,
19784
- Target: params.target
19777
+ Target: encodeURIComponent(params.target)
19785
19778
  };
19786
19779
  if (params.targetDelimiter)
19787
19780
  headers["Target-Delimiter"] = params.targetDelimiter;
19788
19781
  if (params.trimTargetWhitespace !== undefined) {
19789
19782
  headers["Trim-Target-Whitespace"] = String(params.trimTargetWhitespace);
19790
19783
  }
19784
+ if (params.createTargetIfMissing !== undefined) {
19785
+ headers["Create-Target-If-Missing"] = String(params.createTargetIfMissing);
19786
+ }
19791
19787
  await client.fetchVoid(`/vault/${client.encodePath(params.path)}`, {
19792
19788
  method: "PATCH",
19793
19789
  headers,
@@ -19922,19 +19918,23 @@ function registerActiveFileTools(server, client) {
19922
19918
  targetType: exports_external.enum(["heading", "block", "frontmatter"]).describe("Tipo do alvo: heading, block ou frontmatter"),
19923
19919
  target: exports_external.string().describe("Identificador do alvo"),
19924
19920
  targetDelimiter: exports_external.string().optional().describe("Delimitador para separar conteúdo inserido"),
19925
- trimTargetWhitespace: exports_external.boolean().optional().describe("Remover espaços do target antes de comparar")
19921
+ trimTargetWhitespace: exports_external.boolean().optional().describe("Remover espaços do target antes de comparar"),
19922
+ createTargetIfMissing: exports_external.boolean().optional().describe("Criar o alvo se não existir no arquivo")
19926
19923
  }, safeTool(async (params) => {
19927
19924
  const headers = {
19928
19925
  "Content-Type": "text/markdown",
19929
19926
  Operation: params.operation,
19930
19927
  "Target-Type": params.targetType,
19931
- Target: params.target
19928
+ Target: encodeURIComponent(params.target)
19932
19929
  };
19933
19930
  if (params.targetDelimiter)
19934
19931
  headers["Target-Delimiter"] = params.targetDelimiter;
19935
19932
  if (params.trimTargetWhitespace !== undefined) {
19936
19933
  headers["Trim-Target-Whitespace"] = String(params.trimTargetWhitespace);
19937
19934
  }
19935
+ if (params.createTargetIfMissing !== undefined) {
19936
+ headers["Create-Target-If-Missing"] = String(params.createTargetIfMissing);
19937
+ }
19938
19938
  await client.fetchVoid("/active/", {
19939
19939
  method: "PATCH",
19940
19940
  headers,
@@ -19998,19 +19998,23 @@ function registerPeriodicTools(server, client) {
19998
19998
  targetType: exports_external.enum(["heading", "block", "frontmatter"]).describe("Tipo do alvo: heading, block ou frontmatter"),
19999
19999
  target: exports_external.string().describe("Identificador do alvo"),
20000
20000
  targetDelimiter: exports_external.string().optional().describe("Delimitador para separar conteúdo inserido"),
20001
- trimTargetWhitespace: exports_external.boolean().optional().describe("Remover espaços do target antes de comparar")
20001
+ trimTargetWhitespace: exports_external.boolean().optional().describe("Remover espaços do target antes de comparar"),
20002
+ createTargetIfMissing: exports_external.boolean().optional().describe("Criar o alvo se não existir no arquivo")
20002
20003
  }, safeTool(async (params) => {
20003
20004
  const headers = {
20004
20005
  "Content-Type": "text/markdown",
20005
20006
  Operation: params.operation,
20006
20007
  "Target-Type": params.targetType,
20007
- Target: params.target
20008
+ Target: encodeURIComponent(params.target)
20008
20009
  };
20009
20010
  if (params.targetDelimiter)
20010
20011
  headers["Target-Delimiter"] = params.targetDelimiter;
20011
20012
  if (params.trimTargetWhitespace !== undefined) {
20012
20013
  headers["Trim-Target-Whitespace"] = String(params.trimTargetWhitespace);
20013
20014
  }
20015
+ if (params.createTargetIfMissing !== undefined) {
20016
+ headers["Create-Target-If-Missing"] = String(params.createTargetIfMissing);
20017
+ }
20014
20018
  await client.fetchVoid(`/periodic/${params.period}/`, {
20015
20019
  method: "PATCH",
20016
20020
  headers,
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@leonardocrdso/obsidian-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "MCP server for Obsidian — full Local REST API integration with 22 tools",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
7
7
  "bin": {
8
+ "@leonardocrdso/obsidian-mcp": "build/index.js",
8
9
  "obsidian-mcp": "build/index.js"
9
10
  },
10
11
  "files": [