ICUICU
critical

joplin-mcp-server

v2.1.0

MCP server for Joplin

npmGitHub ActionsFirst seen Feb 24, 2026

81

Total

18

Critical

34

High

29

Medium

Findings

unknown
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    510: 	const profileDir = extractArg(args, "--profile").or(Option(process.env.JOPLIN_PROFILE)).map(expandPath).orElse(`${os.homedir()}/.config/joplin-mcp`);
    511: 	const syncResult = buildSyncTarget({
>>> 512: 		syncTarget: extractArg(args, "--sync-target").or(Option(process.env.JOPLIN_SYNC_TARGET)),
    513: 		syncPath: extractArg(args, "--sync-path").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath),
    514: 		syncUsername: extractArg(args, "--sync-username").or(Option(process.env.JOPLIN_SYNC_USERNAME)),
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    508: 		httpPort = parsed;
    509: 	});
>>> 510: 	const profileDir = extractArg(args, "--profile").or(Option(process.env.JOPLIN_PROFILE)).map(expandPath).orElse(`${os.homedir()}/.config/joplin-mcp`);
    511: 	const syncResult = buildSyncTarget({
    512: 		syncTarget: extractArg(args, "--sync-target").or(Option(process.env.JOPLIN_SYNC_TARGET)),
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    491: 	extractArg(args, "--env-file").fold(() => loadEnvFile(".env"), (file) => loadEnvFile(resolve(process.cwd(), file)));
    492: 	extractArg(args, "--token").fold(() => {}, (token) => {
>>> 493: 		process.env.JOPLIN_TOKEN = token;
    494: 	});
    495: 	extractArg(args, "--transport").fold(() => {}, (value) => {
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    489: 		}
    490: 	};
>>> 491: 	extractArg(args, "--env-file").fold(() => loadEnvFile(".env"), (file) => loadEnvFile(resolve(process.cwd(), file)));
    492: 	extractArg(args, "--token").fold(() => {}, (token) => {
    493: 		process.env.JOPLIN_TOKEN = token;
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    477: 							const value = valueParts.join("=").replace(/^["']|["']$/g, "");
    478: 							if (!process.env[key.trim()]) {
>>> 479: 								process.env[key.trim()] = value;
    480: 								loadedVars.push(key.trim());
    481: 							}
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    476: 						if (key && valueParts.length > 0) {
    477: 							const value = valueParts.join("=").replace(/^["']|["']$/g, "");
>>> 478: 							if (!process.env[key.trim()]) {
    479: 								process.env[key.trim()] = value;
    480: 								loadedVars.push(key.trim());
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    368: //#endregion
    369: //#region src/lib/parse-args.ts
>>> 370: const expandVars = (p) => p.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "").replace(/\$(\w+)/g, (_, name) => process.env[name] ?? "");
    371: const isWsl = (() => {
    372: 	try {
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    80: };
    81: const findJoplinCli = async () => {
>>> 82: 	const envCli = process.env.JOPLIN_CLI;
    83: 	if (envCli) {
    84: 		if (fs.existsSync(envCli)) return Right(envCli);
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

>>> 1: {"version":3,"file":"bin.js","names":[],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Version is injected at build time via tsdown.config.ts define\ndeclare const __VERSION__: string\n\n// Force stdio mode for CLI/npx usage (unless explicitly overridden)\nif (!process.env.TRANSPORT_TYPE) {\n  process.env.TRANSPORT_TYPE = \"stdio\"\n}\n\n// Handle command line arguments BEFORE any other imports\nconst args = process.argv.slice(2)\n\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n  console.log(__VERSION__)\n  process.exit(0)\n}\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n  console.log(`\nJoplin MCP Server v${__VERSION__}\n\nUsage: joplin-mcp-server [options]\n\nOptions:\n  -v, --version        Show version number\n  -h, --help           Show help\n\nEnvironment Variables:\n  JOPLIN_TOKEN         Joplin API token (required)\n  JOPLIN_HOST          Connect to existing Joplin at this host (skips sidecar)\n  JOPLIN_PORT          Connect to existing Joplin on this port (skips sidecar)\n  JOPLIN_CLI           Path to joplin CLI binary (overrides auto-detection)\n\nFor more information, visit: https://github.com/jordanburke/joplin-mcp-server\n`)\n  process.exit(0)\n}\n\n// Import and start server if not showing version/help\nasync function main() {\n  // Import and run the main function from the FastMCP server\n  await import(\"./index.js\")\n}\n\nmain().then()\n"],"mappings":";;AAMA,IAAI,CAAC,QAAQ,IAAI,eACf,SAAQ,IAAI,iBAAiB;AAI/B,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAElC,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,KAAK,EAAE;AACrD,SAAQ,YAAgB;AACxB,SAAQ,KAAK,EAAE;;AAGjB,IAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,EAAE;AAClD,SAAQ,IAAI;;;;;;;;;;;;;;;;EAgBZ;AACA,SAAQ,KAAK,EAAE;;AAIjB,eAAe,OAAO;AAEpB,OAAM,OAAO;;AAGf,MAAM,CAAC,MAAM"}
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    1: #!/usr/bin/env node
    2: //#region src/bin.ts
>>> 3: if (!process.env.TRANSPORT_TYPE) process.env.TRANSPORT_TYPE = "stdio";
    4: const args = process.argv.slice(2);
    5: if (args.includes("--version") || args.includes("-v")) {
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    1710: }
    1711: const joplinToken = (() => {
>>> 1712: 	if (process.env.JOPLIN_TOKEN) return process.env.JOPLIN_TOKEN;
    1713: 	if (externalMode) return `mcp-${crypto.randomUUID().replace(/-/g, "").slice(0, 32)}`;
    1714: 	const tokenPath = path.join(profileDir, ".mcp-token");
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    1705: const externalPort = process.env.JOPLIN_PORT ? parseInt(process.env.JOPLIN_PORT, 10) : void 0;
    1706: const externalMode = !!(externalHost || externalPort);
>>> 1707: if (!process.env.JOPLIN_TOKEN && externalMode) {
    1708: 	process.stderr.write("Error: JOPLIN_TOKEN is required in external mode. Use --token <token> or set JOPLIN_TOKEN environment variable.\n");
    1709: 	process.exit(1);
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    1703: const isHttpMode = transport === "http";
    1704: const externalHost = process.env.JOPLIN_HOST;
>>> 1705: const externalPort = process.env.JOPLIN_PORT ? parseInt(process.env.JOPLIN_PORT, 10) : void 0;
    1706: const externalMode = !!(externalHost || externalPort);
    1707: if (!process.env.JOPLIN_TOKEN && externalMode) {
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    1702: const { transport, httpPort, profileDir, syncTarget } = parseArgs();
    1703: const isHttpMode = transport === "http";
>>> 1704: const externalHost = process.env.JOPLIN_HOST;
    1705: const externalPort = process.env.JOPLIN_PORT ? parseInt(process.env.JOPLIN_PORT, 10) : void 0;
    1706: const externalMode = !!(externalHost || externalPort);
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    513: 		syncPath: extractArg(args, "--sync-path").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath),
    514: 		syncUsername: extractArg(args, "--sync-username").or(Option(process.env.JOPLIN_SYNC_USERNAME)),
>>> 515: 		syncPassword: extractArg(args, "--sync-password").or(Option(process.env.JOPLIN_SYNC_PASSWORD))
    516: 	});
    517: 	if (Either.isLeft(syncResult)) {
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    512: 		syncTarget: extractArg(args, "--sync-target").or(Option(process.env.JOPLIN_SYNC_TARGET)),
    513: 		syncPath: extractArg(args, "--sync-path").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath),
>>> 514: 		syncUsername: extractArg(args, "--sync-username").or(Option(process.env.JOPLIN_SYNC_USERNAME)),
    515: 		syncPassword: extractArg(args, "--sync-password").or(Option(process.env.JOPLIN_SYNC_PASSWORD))
    516: 	});
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

    511: 	const syncResult = buildSyncTarget({
    512: 		syncTarget: extractArg(args, "--sync-target").or(Option(process.env.JOPLIN_SYNC_TARGET)),
>>> 513: 		syncPath: extractArg(args, "--sync-path").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath),
    514: 		syncUsername: extractArg(args, "--sync-username").or(Option(process.env.JOPLIN_SYNC_USERNAME)),
    515: 		syncPassword: extractArg(args, "--sync-password").or(Option(process.env.JOPLIN_SYNC_PASSWORD))
Report false positive
criticalDE-002Data ExfiltrationHigh ConfidenceLine 0

Environment file access

Detected by automated pattern matching (rule DE-002) with medium confidence. May be a false positive.

>>> 1: {"version":3,"file":"index.js","names":["crypto"],"sources":["../src/lib/joplin-sidecar.ts","../src/lib/parse-args.ts","../src/lib/joplin-api-client.ts","../src/lib/tools/base-tool.ts","../src/lib/tools/create-folder.ts","../src/lib/tools/create-note.ts","../src/lib/tools/delete-folder.ts","../src/lib/tools/delete-note.ts","../src/lib/tools/edit-folder.ts","../src/lib/tools/edit-note.ts","../src/lib/tools/list-notebooks.ts","../src/lib/tools/read-multi-note.ts","../src/lib/tools/read-note.ts","../src/lib/tools/read-notebook.ts","../src/lib/tools/search-notes.ts","../src/server-core.ts","../src/server-fastmcp.ts","../src/index.ts"],"sourcesContent":["import { type ChildProcess, exec, execFile, spawn } from \"child_process\"\nimport crypto from \"crypto\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { join } from \"path\"\nimport { promisify } from \"util\"\n\nconst execAsync = promisify(exec)\nconst execFileAsync = promisify(execFile)\n\nconst isWindows = process.platform === \"win32\"\nconst whichCmd = isWindows ? \"where\" : \"which\"\n\nexport const DEFAULT_API_PORT = 41184\nconst MAX_PORT_ATTEMPTS = 10\n\nexport type SyncTarget =\n  | { type: \"none\" }\n  | { type: \"filesystem\"; path: string }\n  | { type: \"joplin-cloud\"; email: string; password: string }\n  | { type: \"joplin-server\"; url: string; email: string; password: string }\n  | { type: \"webdav\"; url: string; username: string; password: string }\n  | { type: \"nextcloud\"; url: string; username: string; password: string }\n  | { type: \"s3\"; bucket: string; region: string; accessKey: string; secretKey: string }\n  | { type: \"dropbox\" }\n  | { type: \"onedrive\" }\n\nexport type SidecarConfig = {\n  profileDir: string\n  apiPort: number\n  apiToken: string\n  syncTarget?: SyncTarget\n  syncInterval?: number\n}\n\nexport type SidecarError = {\n  code:\n    | \"CLI_NOT_FOUND\"\n    | \"CONFIG_FAILED\"\n    | \"SPAWN_FAILED\"\n    | \"HEALTH_CHECK_FAILED\"\n    | \"STOP_FAILED\"\n    | \"SYNC_FAILED\"\n    | \"PORT_CONFLICT\"\n    | \"PORT_OCCUPIED\"\n    | \"PORT_EXHAUSTED\"\n  message: string\n  cause?: unknown\n}\n\nconst sidecarError = (code: SidecarError[\"code\"], message: string, cause?: unknown): SidecarError => ({\n  code,\n  message,\n  cause,\n})\n\nconst syncTargetId = (target: SyncTarget): number =>\n  Match(target.type)\n    .case(\"none\", () => 0)\n    .case(\"filesystem\", () => 2)\n    .case(\"webdav\", () => 6)\n    .case(\"nextcloud\", () => 5)\n    .case(\"dropbox\", () => 7)\n    .case(\"onedrive\", () => 3)\n    .case(\"s3\", () => 8)\n    .case(\"joplin-server\", () => 9)\n    .case(\"joplin-cloud\", () => 10)\n    .default(() => 0)\n\nconst fetchWithTimeout = async (url: string, timeoutMs: number = 5_000): Promise<Response> => {\n  const controller = new AbortController()\n  const timer = setTimeout(() => controller.abort(), timeoutMs)\n  try {\n    return await fetch(url, { signal: controller.signal })\n  } finally {\n    clearTimeout(timer)\n  }\n}\n\ntype PortProbeResult = \"free\" | \"joplin_ours\" | \"joplin_foreign\" | \"occupied_other\" | \"occupied_unresponsive\"\n\nconst isConnectionRefused = (err: unknown): boolean => {\n  const e = err as { cause?: { code?: string; errors?: Array<{ code?: string }> } }\n  if (e?.cause?.code === \"ECONNREFUSED\") return true\n  if (e?.cause?.errors?.some((inner) => inner?.code === \"ECONNREFUSED\")) return true\n  return false\n}\n\nconst probePort = async (port: number, token: string): Promise<PortProbeResult> => {\n  try {\n    const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`, 3_000)\n    const body = await pingResponse.text()\n    if (body !== \"JoplinClipperServer\") return \"occupied_other\"\n\n    // It's a Joplin server — check if the token matches\n    try {\n      const authResponse = await fetchWithTimeout(\n        `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n        3_000,\n      )\n      if (authResponse.ok) return \"joplin_ours\"\n      return \"joplin_foreign\"\n    } catch {\n      return \"joplin_foreign\"\n    }\n  } catch (err: unknown) {\n    // ECONNREFUSED = nothing listening on the port = genuinely free\n    if (isConnectionRefused(err)) return \"free\"\n    // Timeout or other error = port is occupied but not responding to HTTP\n    return \"occupied_unresponsive\"\n  }\n}\n\ntype PortResolution =\n  | { outcome: \"reuse_existing\"; port: number; desktopDetected: boolean }\n  | { outcome: \"free\"; port: number; desktopDetected: boolean }\n  | { outcome: \"exhausted\" }\n\nconst resolveAvailablePort = async (startPort: number, token: string): Promise<PortResolution> => {\n  let desktopDetected = false\n  for (let port = startPort; port < startPort + MAX_PORT_ATTEMPTS; port++) {\n    const status = await probePort(port, token)\n    if (status === \"free\") return { outcome: \"free\", port, desktopDetected }\n    if (status === \"joplin_ours\") return { outcome: \"reuse_existing\", port, desktopDetected }\n    if (status === \"joplin_foreign\") {\n      desktopDetected = true\n      process.stderr.write(`[joplin-sidecar] Port ${port}: Joplin Desktop detected (different token), skipping\\n`)\n    } else {\n      process.stderr.write(`[joplin-sidecar] Port ${port} occupied (${status}), trying next...\\n`)\n    }\n  }\n  return { outcome: \"exhausted\" }\n}\n\nconst findJoplinCli = async (): Promise<Either<SidecarError, string>> => {\n  // 1. User override via env var\n  const envCli = process.env.JOPLIN_CLI\n  if (envCli) {\n    if (fs.existsSync(envCli)) return Right(envCli)\n    return Left(sidecarError(\"CLI_NOT_FOUND\", `JOPLIN_CLI path not found: ${envCli}`))\n  }\n\n  // 2. Bundled in node_modules (if joplin is a dependency)\n  const localBin = join(process.cwd(), \"node_modules\", \".bin\", isWindows ? \"joplin.cmd\" : \"joplin\")\n  if (fs.existsSync(localBin)) return Right(localBin)\n\n  // 3. Global install\n  try {\n    const { stdout } = await execAsync(`${whichCmd} joplin`, { encoding: \"utf-8\", timeout: 10_000 })\n    const joplinPath = stdout.trim().split(\"\\n\")[0]\n    return Right(joplinPath)\n  } catch {\n    // not found\n  }\n\n  // 4. npx fallback (auto-downloads on first run)\n  try {\n    const { stdout } = await execAsync(`${whichCmd} npx`, { encoding: \"utf-8\", timeout: 10_000 })\n    const npxPath = stdout.trim().split(\"\\n\")[0]\n    process.stderr.write(\"[joplin-sidecar] No local joplin found, using npx (may download on first run)\\n\")\n    return Right(npxPath)\n  } catch {\n    // not found\n  }\n\n  return Left(\n    sidecarError(\n      \"CLI_NOT_FOUND\",\n      \"Joplin CLI not found. Install with: npm install -g joplin, or set JOPLIN_CLI=/path/to/joplin\",\n    ),\n  )\n}\n\nconst buildSettingsRecord = (config: SidecarConfig): Record<string, string> => {\n  const settings: Record<string, string> = {\n    \"api.token\": config.apiToken,\n    \"api.port\": String(config.apiPort),\n  }\n\n  const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n  settings[\"sync.target\"] = String(syncTargetId(syncTarget))\n\n  if (syncTarget.type === \"filesystem\") {\n    settings[\"sync.2.path\"] = syncTarget.path\n  } else if (syncTarget.type === \"webdav\") {\n    settings[\"sync.6.path\"] = syncTarget.url\n    settings[\"sync.6.username\"] = syncTarget.username\n    settings[\"sync.6.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"nextcloud\") {\n    settings[\"sync.5.path\"] = syncTarget.url\n    settings[\"sync.5.username\"] = syncTarget.username\n    settings[\"sync.5.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"s3\") {\n    settings[\"sync.8.path\"] = syncTarget.bucket\n    settings[\"sync.8.region\"] = syncTarget.region\n    settings[\"sync.8.username\"] = syncTarget.accessKey\n    settings[\"sync.8.password\"] = syncTarget.secretKey\n  } else if (syncTarget.type === \"joplin-server\") {\n    settings[\"sync.9.path\"] = syncTarget.url\n    settings[\"sync.9.username\"] = syncTarget.email\n    settings[\"sync.9.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"joplin-cloud\") {\n    settings[\"sync.10.username\"] = syncTarget.email\n    settings[\"sync.10.password\"] = syncTarget.password\n  }\n\n  const interval = Option(config.syncInterval).orElse(300)\n  settings[\"sync.interval\"] = String(interval)\n\n  return settings\n}\n\nconst runJoplinConfig = async (cli: string, profileDir: string, key: string, value: string): Promise<void> => {\n  const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n  const args = cli.endsWith(\"npx\")\n    ? [\"joplin\", \"config\", \"--profile\", profileDir, key, value]\n    : [\"config\", \"--profile\", profileDir, key, value]\n  await execFileAsync(cmd, args, { encoding: \"utf-8\", timeout: 30_000, shell: isWindows })\n}\n\nconst configureJoplin = async (cli: string, config: SidecarConfig): Promise<Either<SidecarError, void>> => {\n  const settings = buildSettingsRecord(config)\n  try {\n    fs.mkdirSync(config.profileDir, { recursive: true })\n    for (const [key, value] of Object.entries(settings)) {\n      await runJoplinConfig(cli, config.profileDir, key, value)\n    }\n    return Right(undefined as void)\n  } catch (e) {\n    return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to configure Joplin via CLI\", e))\n  }\n}\n\nconst spawnServer = (cli: string, config: SidecarConfig): Either<SidecarError, ChildProcess> => {\n  try {\n    const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n    const args = cli.endsWith(\"npx\")\n      ? [\"joplin\", \"server\", \"start\", \"--profile\", config.profileDir]\n      : [\"server\", \"start\", \"--profile\", config.profileDir]\n\n    const proc = spawn(cmd, args, {\n      stdio: [\"ignore\", \"pipe\", \"pipe\"],\n      detached: false,\n      shell: isWindows,\n    })\n\n    proc.stderr?.on(\"data\", (data: Buffer) => {\n      process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n    })\n\n    proc.stdout?.on(\"data\", (data: Buffer) => {\n      process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n    })\n\n    proc.on(\"error\", (err) => {\n      process.stderr.write(`[joplin-sidecar] Process error: ${err.message}\\n`)\n    })\n\n    return Right(proc as ChildProcess)\n  } catch (e) {\n    return Left(sidecarError(\"SPAWN_FAILED\", \"Failed to spawn Joplin server process\", e))\n  }\n}\n\nconst waitForReady = async (\n  port: number,\n  token: string,\n  proc: ChildProcess | null,\n  maxRetries: number = 30,\n  intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n  const deadline = Date.now() + 60_000\n\n  // Track if the spawned process exits early (e.g., port already taken)\n  let procExitCode: number | null = null\n  if (proc) {\n    proc.once(\"exit\", (code) => {\n      procExitCode = code\n    })\n  }\n\n  for (let i = 0; i < maxRetries; i++) {\n    if (Date.now() > deadline) break\n\n    if (procExitCode !== null) {\n      return Left(\n        sidecarError(\n          \"SPAWN_FAILED\",\n          `Joplin server process exited unexpectedly (code ${procExitCode}). ` +\n            `Port ${port} may already be in use by another Joplin instance or process.`,\n        ),\n      )\n    }\n\n    try {\n      const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`)\n      if (pingResponse.ok) {\n        // Verify auth with token\n        try {\n          const authResponse = await fetchWithTimeout(\n            `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n          )\n          if (authResponse.ok) {\n            process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n            return Right(true as const)\n          }\n          process.stderr.write(\n            `[joplin-sidecar] Ping OK but auth failed (status ${authResponse.status}), retrying...\\n`,\n          )\n        } catch {\n          process.stderr.write(\"[joplin-sidecar] Ping OK but auth check timed out, retrying...\\n\")\n        }\n      }\n    } catch {\n      // Not ready yet\n    }\n    await new Promise((resolve) => setTimeout(resolve, intervalMs))\n  }\n\n  return Left(sidecarError(\"HEALTH_CHECK_FAILED\", \"Joplin server did not become ready within 60s\"))\n}\n\nconst CONFIG_CACHE_FILE = \".mcp-configured\"\n\nconst computeConfigHash = (config: SidecarConfig): string => {\n  const hashData = {\n    apiPort: config.apiPort,\n    apiToken: config.apiToken,\n    syncTarget: config.syncTarget,\n    syncInterval: config.syncInterval,\n  }\n  return crypto.createHash(\"sha256\").update(JSON.stringify(hashData)).digest(\"hex\").slice(0, 16)\n}\n\nconst isConfigCached = (profileDir: string, hash: string): boolean => {\n  try {\n    const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n    if (!fs.existsSync(cachePath)) return false\n    const cached = JSON.parse(fs.readFileSync(cachePath, \"utf-8\"))\n    return cached.hash === hash\n  } catch {\n    return false\n  }\n}\n\nconst writeConfigCache = (profileDir: string, hash: string): void => {\n  try {\n    const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n    fs.writeFileSync(cachePath, JSON.stringify({ hash, timestamp: Date.now() }))\n  } catch {\n    // Non-critical — skip silently\n  }\n}\n\nexport class JoplinSidecar {\n  private config: SidecarConfig\n  private childProcess: ChildProcess | null = null\n  private startPromise: Promise<Either<SidecarError, ChildProcess>> | null = null\n  private portResolution: PortResolution | null = null\n\n  constructor(config: Partial<SidecarConfig> & { apiToken: string }) {\n    this.config = {\n      profileDir: config.profileDir ?? join(os.homedir(), \".config\", \"joplin-mcp\"),\n      apiPort: config.apiPort ?? DEFAULT_API_PORT,\n      apiToken: config.apiToken,\n      syncTarget: config.syncTarget,\n      syncInterval: config.syncInterval,\n    }\n  }\n\n  async resolvePort(): Promise<Either<SidecarError, number>> {\n    const resolution = await resolveAvailablePort(this.config.apiPort, this.config.apiToken)\n    this.portResolution = resolution\n    if (resolution.outcome === \"exhausted\") {\n      const lastPort = this.config.apiPort + MAX_PORT_ATTEMPTS - 1\n      return Left(\n        sidecarError(\n          \"PORT_EXHAUSTED\",\n          `All ports ${this.config.apiPort}-${lastPort} are occupied. Free a port or stop other Joplin instances.`,\n        ),\n      )\n    }\n    this.config.apiPort = resolution.port\n    if (resolution.desktopDetected) {\n      process.stderr.write(\n        \"[joplin-sidecar] WARNING: Joplin Desktop is running. The sidecar uses a separate database.\\n\" +\n          \"[joplin-sidecar] Notes will sync between them only if both are configured with the same sync target.\\n\",\n      )\n    }\n    return Right(resolution.port)\n  }\n\n  isDesktopDetected(): boolean {\n    return this.portResolution?.outcome !== \"exhausted\" && (this.portResolution?.desktopDetected ?? false)\n  }\n\n  async start(): Promise<Either<SidecarError, ChildProcess>> {\n    if (this.startPromise) return this.startPromise\n    this.startPromise = this.doStart()\n    return this.startPromise\n  }\n\n  private async doStart(): Promise<Either<SidecarError, ChildProcess>> {\n    // Step 1: Find CLI\n    const cliResult = await findJoplinCli()\n    if (Either.isLeft(cliResult)) {\n      return Left(\n        cliResult.fold(\n          (e) => e,\n          () => null as never,\n        ),\n      )\n    }\n    const cli = cliResult.fold(\n      () => \"\",\n      (v) => v,\n    )\n    process.stderr.write(`[joplin-sidecar] Found CLI: ${cli}\\n`)\n\n    // Step 2: Resolve port if not already done (defensive fallback)\n    if (!this.portResolution) {\n      const portResult = await this.resolvePort()\n      if (Either.isLeft(portResult)) {\n        return Left(\n          portResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n    }\n\n    // Step 3: If we found an existing instance with our token, reuse it\n    if (this.portResolution?.outcome === \"reuse_existing\") {\n      process.stderr.write(\n        `[joplin-sidecar] Existing Joplin server with matching token on port ${this.config.apiPort}, reusing\\n`,\n      )\n      return Right(this.childProcess ?? (null as unknown as ChildProcess))\n    }\n\n    // Step 4: Configure via CLI (skip if config is cached)\n    const configHash = computeConfigHash(this.config)\n    if (isConfigCached(this.config.profileDir, configHash)) {\n      process.stderr.write(\"[joplin-sidecar] Configuration cached, skipping config step\\n\")\n    } else {\n      const configResult = await configureJoplin(cli, this.config)\n      if (Either.isLeft(configResult)) {\n        return Left(\n          configResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n      writeConfigCache(this.config.profileDir, configHash)\n      process.stderr.write(\"[joplin-sidecar] Configuration applied via CLI\\n\")\n    }\n\n    // Step 5: Spawn server (port is free, skip if already spawned from a previous attempt)\n    if (!this.childProcess) {\n      const spawnResult = spawnServer(cli, this.config)\n      if (Either.isLeft(spawnResult)) {\n        return Left(\n          spawnResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n      const proc = spawnResult.fold(\n        () => null as unknown as ChildProcess,\n        (v) => v,\n      )\n      this.childProcess = proc\n      process.stderr.write(`[joplin-sidecar] Server spawned (pid: ${proc.pid})\\n`)\n    } else {\n      process.stderr.write(\n        `[joplin-sidecar] Server already spawned (pid: ${this.childProcess.pid}), waiting for ready\\n`,\n      )\n    }\n\n    // Step 6: Wait for ready\n    const readyResult = await waitForReady(this.config.apiPort, this.config.apiToken, this.childProcess)\n    if (Either.isLeft(readyResult)) {\n      return Left(\n        readyResult.fold(\n          (e) => e,\n          () => null as never,\n        ),\n      )\n    }\n\n    return Right(this.childProcess!)\n  }\n\n  async stop(): Promise<Either<Error, true>> {\n    // Reset startPromise to allow retry via start() after stop()\n    this.startPromise = null\n\n    if (!this.childProcess) return Right(true as const)\n\n    const proc = this.childProcess\n    try {\n      proc.kill(\"SIGTERM\")\n\n      await new Promise<void>((resolve) => {\n        const timeout = setTimeout(() => {\n          proc.kill(\"SIGKILL\")\n          resolve()\n        }, 5000)\n\n        proc.on(\"exit\", () => {\n          clearTimeout(timeout)\n          resolve()\n        })\n      })\n\n      this.childProcess = null\n      process.stderr.write(\"[joplin-sidecar] Server stopped\\n\")\n      return Right(true as const)\n    } catch (error) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async healthCheck(): Promise<Either<Error, true>> {\n    try {\n      const response = await fetchWithTimeout(`http://127.0.0.1:${this.config.apiPort}/ping`, 5_000)\n      if (!response.ok) return Left(new Error(`Health check failed: ${response.status}`))\n      return Right(true as const)\n    } catch (error) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  getPort(): number {\n    return this.config.apiPort\n  }\n\n  getHost(): string {\n    return \"127.0.0.1\"\n  }\n}\n","import fs from \"fs\"\nimport { Either, Left, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { join, resolve } from \"path\"\n\nimport type { SyncTarget } from \"./joplin-sidecar.js\"\n\nexport type ParsedArgs = {\n  remainingArgs: string[]\n  transport: \"stdio\" | \"http\"\n  httpPort: number\n  profileDir: string\n  syncTarget: Option<SyncTarget>\n}\n\nconst expandVars = (p: string): string =>\n  p\n    .replace(/\\$\\{(\\w+)\\}/g, (_, name) => process.env[name] ?? \"\")\n    .replace(/\\$(\\w+)/g, (_, name) => process.env[name] ?? \"\")\n\nconst isWsl = (() => {\n  try {\n    return fs.readFileSync(\"/proc/version\", \"utf-8\").toLowerCase().includes(\"microsoft\")\n  } catch {\n    return false\n  }\n})()\n\nconst isNonEmptyDir = (p: string): boolean => {\n  try {\n    const entries = fs.readdirSync(p)\n    return entries.length > 0\n  } catch {\n    return false\n  }\n}\n\nconst resolveWslPath = (linuxPath: string, relativeToHome: string): string => {\n  if (!isWsl) return linuxPath\n  if (fs.existsSync(linuxPath) && isNonEmptyDir(linuxPath)) return linuxPath\n  try {\n    const usersDir = \"/mnt/c/Users\"\n    const users = fs\n      .readdirSync(usersDir)\n      .filter((u) => ![\"Public\", \"Default\", \"Default User\", \"All Users\"].includes(u))\n    for (const user of users) {\n      const winPath = join(usersDir, user, relativeToHome)\n      if (isNonEmptyDir(winPath)) {\n        process.stderr.write(`[wsl] Path ${linuxPath} empty/missing, using Windows path: ${winPath}\\n`)\n        return winPath\n      }\n    }\n  } catch {\n    // /mnt/c not accessible — fall through\n  }\n  return linuxPath\n}\n\nconst expandPath = (p: string): string => {\n  const expanded = expandVars(p)\n  if (expanded.startsWith(\"~/\") || expanded === \"~\") {\n    const linuxPath = expanded.replace(\"~\", os.homedir())\n    const relativeToHome = expanded.slice(2) // strip ~/\n    return resolveWslPath(linuxPath, relativeToHome)\n  }\n  return expanded\n}\n\nconst extractArg = (args: string[], flag: string): Option<string> => {\n  const index = args.indexOf(flag)\n  if (index === -1) return Option.none()\n  const value = args[index + 1]\n  if (!value || value.startsWith(\"--\")) return Option.none()\n  args.splice(index, 2)\n  return Option(value)\n}\n\nexport const buildSyncTarget = (args: {\n  syncTarget: Option<string>\n  syncPath: Option<string>\n  syncUsername: Option<string>\n  syncPassword: Option<string>\n}): Either<string, SyncTarget> => {\n  const targetType = args.syncTarget.orElse(\"none\")\n\n  switch (targetType) {\n    case \"none\":\n      return Right({ type: \"none\" } as SyncTarget)\n\n    case \"filesystem\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for filesystem sync target\"),\n        (path) => Right({ type: \"filesystem\", path } as SyncTarget),\n      )\n\n    case \"webdav\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for webdav sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for webdav sync target\"),\n            (username) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for webdav sync target\"),\n                (password) => Right({ type: \"webdav\", url, username, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"nextcloud\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for nextcloud sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for nextcloud sync target\"),\n            (username) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for nextcloud sync target\"),\n                (password) => Right({ type: \"nextcloud\", url, username, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"joplin-cloud\":\n      return args.syncUsername.fold(\n        () => Left(\"--sync-username required for joplin-cloud sync target\"),\n        (email) =>\n          args.syncPassword.fold(\n            () => Left(\"--sync-password required for joplin-cloud sync target\"),\n            (password) => Right({ type: \"joplin-cloud\", email, password } as SyncTarget),\n          ),\n      )\n\n    case \"joplin-server\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for joplin-server sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for joplin-server sync target\"),\n            (email) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for joplin-server sync target\"),\n                (password) => Right({ type: \"joplin-server\", url, email, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"s3\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path (bucket) required for s3 sync target\"),\n        (bucket) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username (access key) required for s3 sync target\"),\n            (accessKey) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password (secret key) required for s3 sync target\"),\n                (secretKey) => Right({ type: \"s3\", bucket, region: \"us-east-1\", accessKey, secretKey } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"dropbox\":\n      return Right({ type: \"dropbox\" } as SyncTarget)\n\n    case \"onedrive\":\n      return Right({ type: \"onedrive\" } as SyncTarget)\n\n    default:\n      return Left(\n        `Unknown sync target: ${targetType}. Valid targets: none, filesystem, webdav, nextcloud, joplin-cloud, joplin-server, s3, dropbox, onedrive`,\n      )\n  }\n}\n\nfunction parseArgs(): ParsedArgs {\n  const args = process.argv.slice(2)\n  let transport: \"stdio\" | \"http\" = \"stdio\"\n  let httpPort = 3000\n\n  // Load environment variables without dotenv debug output (for MCP stdio compatibility)\n  const loadEnvFile = (envPath: string) => {\n    try {\n      if (fs.existsSync(envPath)) {\n        process.stderr.write(`Loading environment from: ${envPath}\\n`)\n        const envContent = fs.readFileSync(envPath, \"utf-8\")\n        const envLines = envContent.split(\"\\n\")\n        const loadedVars: string[] = []\n\n        for (const line of envLines) {\n          const trimmedLine = line.trim()\n          if (trimmedLine && !trimmedLine.startsWith(\"#\")) {\n            const [key, ...valueParts] = trimmedLine.split(\"=\")\n            if (key && valueParts.length > 0) {\n              const value = valueParts.join(\"=\").replace(/^[\"']|[\"']$/g, \"\")\n              if (!process.env[key.trim()]) {\n                process.env[key.trim()] = value\n                loadedVars.push(key.trim())\n              }\n            }\n          }\n        }\n\n        if (loadedVars.length > 0) {\n          process.stderr.write(`Loaded variables: ${loadedVars.join(\", \")}\\n`)\n        }\n      }\n    } catch (error: unknown) {\n      process.stderr.write(`Error loading environment file: ${error}\\n`)\n    }\n  }\n\n  // Handle --env-file\n  const envFile = extractArg(args, \"--env-file\")\n  envFile.fold(\n    () => loadEnvFile(\".env\"),\n    (file) => loadEnvFile(resolve(process.cwd(), file)),\n  )\n\n  // Handle --token\n  extractArg(args, \"--token\").fold(\n    () => {},\n    (token) => {\n      process.env.JOPLIN_TOKEN = token\n    },\n  )\n\n  // Handle --transport\n  extractArg(args, \"--transport\").fold(\n    () => {},\n    (value) => {\n      if (value !== \"stdio\" && value !== \"http\") {\n        process.stderr.write(\"Error: --transport must be either 'stdio' or 'http'\\n\")\n        process.exit(1)\n      }\n      transport = value as \"stdio\" | \"http\"\n    },\n  )\n\n  // Handle --http-port\n  extractArg(args, \"--http-port\").fold(\n    () => {},\n    (value) => {\n      const parsed = parseInt(value, 10)\n      if (isNaN(parsed) || parsed < 1 || parsed > 65535) {\n        process.stderr.write(\"Error: --http-port must be a valid port number (1-65535)\\n\")\n        process.exit(1)\n      }\n      httpPort = parsed\n    },\n  )\n\n  // Handle --profile\n  const profileDir = extractArg(args, \"--profile\")\n    .or(Option(process.env.JOPLIN_PROFILE))\n    .map(expandPath)\n    .orElse(`${os.homedir()}/.config/joplin-mcp`)\n\n  // Handle sync args\n  const syncTarget = extractArg(args, \"--sync-target\").or(Option(process.env.JOPLIN_SYNC_TARGET))\n  const syncPath = extractArg(args, \"--sync-path\").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath)\n  const syncUsername = extractArg(args, \"--sync-username\").or(Option(process.env.JOPLIN_SYNC_USERNAME))\n  const syncPassword = extractArg(args, \"--sync-password\").or(Option(process.env.JOPLIN_SYNC_PASSWORD))\n\n  // Build and validate sync target\n  const syncResult = buildSyncTarget({ syncTarget, syncPath, syncUsername, syncPassword })\n  if (Either.isLeft(syncResult)) {\n    const err = syncResult.fold(\n      (e) => e,\n      () => \"\",\n    )\n    process.stderr.write(`Error: ${err}\\n`)\n    process.exit(1)\n  }\n  const syncTargetValue = syncResult.fold(\n    () => ({ type: \"none\" }) as SyncTarget,\n    (v) => v,\n  )\n  const resolvedSyncTarget: Option<SyncTarget> =\n    syncTargetValue.type === \"none\" ? Option.none<SyncTarget>() : Option(syncTargetValue as SyncTarget)\n\n  // Handle --help\n  if (args.includes(\"--help\") || args.includes(\"-h\")) {\n    process.stderr.write(`\nJoplin MCP Server (Sidecar Mode)\n\nUSAGE:\n  joplin-mcp-server [OPTIONS]\n\nOPTIONS:\n  --env-file <file>          Load environment variables from file\n  --token <token>            Joplin API token\n  --transport <type>         Transport type: stdio (default) or http\n  --http-port <port>         HTTP server port (default: 3000, only with --transport http)\n  --profile <dir>            Joplin data directory (default: ~/.config/joplin-mcp)\n  --sync-target <type>       Sync target: none, filesystem, webdav, nextcloud,\n                             joplin-cloud, joplin-server, s3, dropbox, onedrive\n  --sync-path <url>          URL or path for sync target\n  --sync-username <user>     Username/email for sync\n  --sync-password <pass>     Password for sync\n  --help, -h                 Show this help message\n\nENVIRONMENT VARIABLES:\n  JOPLIN_TOKEN               Joplin API token (required)\n  JOPLIN_HOST                Connect to existing Joplin at this host (skips sidecar)\n  JOPLIN_PORT                Connect to existing Joplin on this port (skips sidecar)\n  JOPLIN_CLI                 Path to joplin CLI binary (overrides auto-detection)\n  JOPLIN_PROFILE             Joplin data directory\n  JOPLIN_SYNC_TARGET         Sync target type\n  JOPLIN_SYNC_PATH           Sync target URL/path\n  JOPLIN_SYNC_USERNAME       Sync username/email\n  JOPLIN_SYNC_PASSWORD       Sync password\n  LOG_LEVEL                  Log level: debug, info, warn, error (default: info)\n\nMODES:\n  Sidecar (default):\n    Spawns and manages its own Joplin Terminal process.\n    No Joplin desktop app or Web Clipper needed.\n    Uses an isolated profile at --profile path (default: ~/.config/joplin-mcp).\n\n  External (JOPLIN_HOST/JOPLIN_PORT set):\n    Connects directly to an existing Joplin instance.\n    Useful for WSL connecting to Windows Joplin desktop.\n\nEXAMPLES:\n  # Minimal - local notes, no sync\n  joplin-mcp-server --token my_token\n\n  # Joplin Cloud sync\n  joplin-mcp-server --token my_token \\\\\n    --sync-target joplin-cloud \\\\\n    --sync-username user@example.com --sync-password pass\n\n  # WebDAV sync\n  joplin-mcp-server --token my_token \\\\\n    --sync-target webdav \\\\\n    --sync-path https://dav.example.com/joplin \\\\\n    --sync-username user --sync-password pass\n\n  # Filesystem sync (Syncthing, NAS)\n  joplin-mcp-server --token my_token \\\\\n    --sync-target filesystem --sync-path /mnt/sync/joplin\n\n  # HTTP transport for web apps\n  joplin-mcp-server --token my_token --transport http --http-port 3000\n\n  # External mode - connect to existing Joplin (e.g. Windows desktop from WSL)\n  JOPLIN_HOST=172.x.x.x JOPLIN_PORT=41184 joplin-mcp-server --token my_token\n\nFind your Joplin token in: Tools > Options > Web Clipper\n`)\n    process.exit(0)\n  }\n\n  return {\n    remainingArgs: args,\n    transport,\n    httpPort,\n    profileDir,\n    syncTarget: resolvedSyncTarget,\n  }\n}\n\nexport default parseArgs\n","import axios, { type AxiosResponse } from \"axios\"\nimport { Either, Left, Right } from \"functype\"\n\ntype JoplinAPIClientConfig = {\n  host?: string\n  port?: number\n  token: string\n}\n\ntype JoplinAPIResponse<T = unknown> = {\n  items: T[]\n  has_more: boolean\n}\n\ntype RequestOptions = {\n  query?: Record<string, unknown>\n  [key: string]: unknown\n}\n\nclass JoplinAPIClient {\n  private readonly baseURL: string\n  private readonly token: string\n\n  constructor({ host = \"127.0.0.1\", port = 41184, token }: JoplinAPIClientConfig) {\n    this.baseURL = `http://${host}:${port}`\n    this.token = token\n  }\n\n  async serviceAvailable(): Promise<Either<Error, true>> {\n    try {\n      const response: AxiosResponse<string> = await axios.get(`${this.baseURL}/ping`, { timeout: 5_000 })\n      if (response.status === 200 && response.data === \"JoplinClipperServer\") {\n        return Right(true as const)\n      }\n      return Left(new Error(\"Unexpected response from Joplin ping\"))\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async getAllItems<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T[]>> {\n    let page = 1\n    const items: T[] = []\n\n    try {\n      while (true) {\n        const result = await this.get<JoplinAPIResponse<T>>(\n          path,\n          this.mergeRequestOptions(options, { query: { page } }),\n        )\n\n        const response = result.fold(\n          (err) => {\n            throw err\n          },\n          (data) => data,\n        )\n\n        if (!response || typeof response !== \"object\" || !Array.isArray(response.items)) {\n          return Left(new Error(`Unexpected response format from Joplin API for path: ${path}`))\n        }\n\n        items.push(...response.items)\n        page += 1\n\n        if (!response.has_more) break\n      }\n\n      return Right(items)\n    } catch (error: unknown) {\n      process.stderr.write(`Error in getAllItems for path ${path}: ${error}\\n`)\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.get(`${this.baseURL}${path}`, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async post<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.post(`${this.baseURL}${path}`, body, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async delete<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.delete(`${this.baseURL}${path}`, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async put<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.put(`${this.baseURL}${path}`, body, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  private requestOptions(options: RequestOptions = {}): RequestOptions {\n    return this.mergeRequestOptions(\n      {\n        query: { token: this.token },\n      },\n      options,\n    )\n  }\n\n  private mergeRequestOptions(options1: RequestOptions, options2: RequestOptions): RequestOptions {\n    return {\n      query: {\n        ...(options1.query || {}),\n        ...(options2.query || {}),\n      },\n      ...this.except(options1, \"query\"),\n      ...this.except(options2, \"query\"),\n    }\n  }\n\n  private except(obj: Record<string, unknown>, key: string): Record<string, unknown> {\n    const result = { ...obj }\n    delete result[key]\n    return result\n  }\n}\n\nexport default JoplinAPIClient\nexport type { JoplinAPIClientConfig, JoplinAPIResponse, RequestOptions }\n","import { type Either } from \"functype\"\n\nimport JoplinAPIClient from \"../joplin-api-client.js\"\n\ntype JoplinFolder = {\n  id: string\n  title: string\n  parent_id?: string\n}\n\ntype JoplinNote = {\n  id: string\n  title: string\n  body?: string\n  parent_id?: string\n  created_time: number\n  updated_time: number\n  is_todo: boolean\n  todo_completed?: boolean\n  todo_due?: number\n}\n\nabstract class BaseTool {\n  protected apiClient: JoplinAPIClient\n\n  constructor(apiClient: JoplinAPIClient) {\n    this.apiClient = apiClient\n  }\n\n  abstract call(...args: any[]): Promise<string>\n\n  protected formatError(error: any, context: string): string {\n    process.stderr.write(`${context} error: ${error}\\n`)\n    return `Error ${context.toLowerCase()}: ${error.message || \"Unknown error\"}`\n  }\n\n  protected validateId(id: string, type: \"note\" | \"notebook\"): string | null {\n    if (!id) {\n      return `Please provide a ${type} ID. Example: ${type === \"note\" ? \"read_note\" : \"read_notebook\"} ${type}_id=\"your-${type}-id\"`\n    }\n\n    if (id.length < 10 || !id.match(/[a-f0-9]/i)) {\n      const searchHint = type === \"note\" ? \"search_notes\" : \"list_notebooks\"\n      return `Error: \"${id}\" does not appear to be a valid ${type} ID. \\n\\n${type.charAt(0).toUpperCase() + type.slice(1)} IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse ${searchHint} to ${type === \"note\" ? \"find notes\" : \"see all available notebooks\"} and their IDs.`\n    }\n\n    return null\n  }\n\n  protected unwrap<T>(result: Either<Error, T>): T {\n    return result.fold(\n      (err) => {\n        throw err\n      },\n      (val) => val,\n    )\n  }\n\n  protected formatDate(timestamp: number): string {\n    return new Date(timestamp).toLocaleString()\n  }\n}\n\nexport default BaseTool\nexport type { JoplinFolder, JoplinNote }\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface CreateFolderOptions {\n  title: string\n  parent_id?: string | undefined\n}\n\ninterface CreateFolderResponse extends JoplinFolder {\n  created_time: number\n  updated_time: number\n}\n\nclass CreateFolder extends BaseTool {\n  async call(options: CreateFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder creation options. Example: create_folder {\"title\": \"My Notebook\"}'\n    }\n\n    // Validate required title\n    if (!options.title || typeof options.title !== \"string\" || options.title.trim() === \"\") {\n      return 'Please provide a title for the folder/notebook. Example: create_folder {\"title\": \"My Notebook\"}'\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n      return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n    }\n\n    try {\n      // Prepare the request body\n      const requestBody: CreateFolderOptions = {\n        title: options.title.trim(),\n      }\n\n      if (options.parent_id) {\n        requestBody.parent_id = options.parent_id\n      }\n\n      // Create the folder\n      const createdFolder = this.unwrap(await this.apiClient.post<CreateFolderResponse>(\"/folders\", requestBody))\n\n      // Validate response\n      if (!createdFolder || typeof createdFolder !== \"object\" || !createdFolder.id) {\n        return \"Error: Unexpected response format from Joplin API when creating folder\"\n      }\n\n      // Get parent notebook info if available\n      let parentInfo = \"Top level\"\n      if (createdFolder.parent_id) {\n        try {\n          const parentNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${createdFolder.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (parentNotebook && parentNotebook.title) {\n            parentInfo = `Inside \"${parentNotebook.title}\" (notebook_id: \"${createdFolder.parent_id}\")`\n          }\n        } catch {\n          // Continue even if we can't get parent info\n          parentInfo = `Parent notebook ID: ${createdFolder.parent_id}`\n        }\n      }\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully created notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Notebook Details:`)\n      resultLines.push(`   Title: \"${createdFolder.title}\"`)\n      resultLines.push(`   Notebook ID: ${createdFolder.id}`)\n      resultLines.push(`   Location: ${parentInfo}`)\n\n      const createdDate = this.formatDate(createdFolder.created_time)\n      resultLines.push(`   Created: ${createdDate}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - View notebook: read_notebook notebook_id=\"${createdFolder.id}\"`)\n      resultLines.push(`   - Create a note in it: create_note {\"title\": \"My Note\", \"parent_id\": \"${createdFolder.id}\"}`)\n      resultLines.push(`   - View all notebooks: list_notebooks`)\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        // Handle specific API errors\n        if (error.response.status === 400) {\n          return `Error creating notebook: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Parent notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n        }\n        if (error.response.status === 409) {\n          return `Error: A notebook with the title \"${options.title}\" might already exist in this location.\\n\\nTry a different title or check existing notebooks with list_notebooks.`\n        }\n      }\n      return this.formatError(error, \"creating notebook\")\n    }\n  }\n}\n\nexport default CreateFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface CreateNoteOptions {\n  title?: string | undefined\n  body?: string | undefined\n  body_html?: string | undefined\n  parent_id?: string | undefined\n  is_todo?: boolean | undefined\n  image_data_url?: string | undefined\n}\n\ntype CreateNoteResponse = JoplinNote\n\nclass CreateNote extends BaseTool {\n  async call(options: CreateNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note creation options. Example: create_note {\"title\": \"My Note\", \"body\": \"Note content\"}'\n    }\n\n    // Validate that we have at least a title or body\n    if (!options.title && !options.body && !options.body_html) {\n      return \"Please provide at least a title, body, or body_html for the note.\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n      return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n    }\n\n    try {\n      // Prepare the request body\n      const requestBody: CreateNoteOptions = {}\n\n      if (options.title) requestBody.title = options.title\n      if (options.body) requestBody.body = options.body\n      if (options.body_html) requestBody.body_html = options.body_html\n      if (options.parent_id) requestBody.parent_id = options.parent_id\n      if (options.is_todo !== undefined) requestBody.is_todo = options.is_todo\n      if (options.image_data_url) requestBody.image_data_url = options.image_data_url\n\n      // Create the note\n      const createdNote = this.unwrap(await this.apiClient.post<CreateNoteResponse>(\"/notes\", requestBody))\n\n      // Validate response\n      if (!createdNote || typeof createdNote !== \"object\" || !createdNote.id) {\n        return \"Error: Unexpected response format from Joplin API when creating note\"\n      }\n\n      // Get notebook info if available\n      let notebookInfo = \"Root level\"\n      if (createdNote.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${createdNote.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (notebook && notebook.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${createdNote.parent_id}\")`\n          }\n        } catch {\n          // Continue even if we can't get notebook info\n          notebookInfo = `Notebook ID: ${createdNote.parent_id}`\n        }\n      }\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully created note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Note Details:`)\n      resultLines.push(`   Title: \"${createdNote.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${createdNote.id}`)\n      resultLines.push(`   Location: ${notebookInfo}`)\n\n      if (createdNote.is_todo) {\n        resultLines.push(`   Type: Todo item`)\n      }\n\n      const createdDate = this.formatDate(createdNote.created_time)\n      resultLines.push(`   Created: ${createdDate}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - Read the note: read_note note_id=\"${createdNote.id}\"`)\n      if (createdNote.parent_id) {\n        resultLines.push(`   - View notebook: read_notebook notebook_id=\"${createdNote.parent_id}\"`)\n      }\n      resultLines.push(`   - Search for it: search_notes query=\"${createdNote.title}\"`)\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        // Handle specific API errors\n        if (error.response.status === 400) {\n          return `Error creating note: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n        }\n      }\n      return this.formatError(error, \"creating note\")\n    }\n  }\n}\n\nexport default CreateNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface DeleteFolderOptions {\n  folder_id: string\n  confirm?: boolean | undefined\n  force?: boolean | undefined\n}\n\ninterface FolderContents {\n  items: any[]\n}\n\nclass DeleteFolder extends BaseTool {\n  async call(options: DeleteFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    // Validate required folder_id\n    if (!options.folder_id) {\n      return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    const folderIdError = this.validateId(options.folder_id, \"notebook\")\n    if (folderIdError) {\n      return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n    }\n\n    // Require explicit confirmation for safety\n    if (!options.confirm) {\n      return `⚠️  This will permanently delete the notebook/folder!\\n\\nTo confirm deletion, use:\\ndelete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true}\\n\\n⚠️  This action cannot be undone!`\n    }\n\n    try {\n      // First, get the folder details to show what's being deleted\n      const folderToDelete = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      if (!folderToDelete || !folderToDelete.id) {\n        return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n      }\n\n      // Check if folder contains notes or subfolders\n      const [notes, subfolders] = await Promise.all([\n        this.apiClient\n          .get<FolderContents>(`/folders/${options.folder_id}/notes`, {\n            query: { fields: \"id,title\" },\n          })\n          .then((result) =>\n            result.fold(\n              () => ({ items: [] }),\n              (data) => data,\n            ),\n          ),\n        this.apiClient\n          .get<FolderContents>(\"/folders\", {\n            query: { fields: \"id,title,parent_id\" },\n          })\n          .then((result) =>\n            result.fold(\n              () => ({ items: [] }),\n              (response) => ({\n                items: response.items?.filter((folder: any) => folder.parent_id === options.folder_id) || [],\n              }),\n            ),\n          ),\n      ])\n\n      const noteCount = notes.items?.length || 0\n      const subfolderCount = subfolders.items?.length || 0\n      const totalContent = noteCount + subfolderCount\n\n      // Warn if folder is not empty and force is not specified\n      if (totalContent > 0 && !options.force) {\n        const resultLines: string[] = []\n        resultLines.push(`⚠️  Cannot delete non-empty notebook!`)\n        resultLines.push(\"\")\n        resultLines.push(`📁 Notebook: \"${folderToDelete.title}\"`)\n        resultLines.push(`   Contains: ${noteCount} notes and ${subfolderCount} subfolders`)\n\n        if (noteCount > 0) {\n          resultLines.push(\"\")\n          resultLines.push(`📝 Contains ${noteCount} notes:`)\n          notes.items.slice(0, 5).forEach((note: any) => {\n            resultLines.push(`   - ${note.title || \"Untitled\"}`)\n          })\n          if (noteCount > 5) {\n            resultLines.push(`   ... and ${noteCount - 5} more notes`)\n          }\n        }\n\n        if (subfolderCount > 0) {\n          resultLines.push(\"\")\n          resultLines.push(`📁 Contains ${subfolderCount} subfolders:`)\n          subfolders.items.slice(0, 5).forEach((folder: any) => {\n            resultLines.push(`   - ${folder.title}`)\n          })\n          if (subfolderCount > 5) {\n            resultLines.push(`   ... and ${subfolderCount - 5} more folders`)\n          }\n        }\n\n        resultLines.push(\"\")\n        resultLines.push(`💡 Options:`)\n        resultLines.push(`   1. Move or delete the contents first, then delete the folder`)\n        resultLines.push(`   2. Force delete (⚠️  DESTROYS ALL CONTENT):`)\n        resultLines.push(`      delete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true, \"force\": true}`)\n        resultLines.push(\"\")\n        resultLines.push(`⚠️  Force delete will permanently delete ALL ${totalContent} items inside!`)\n\n        return resultLines.join(\"\\n\")\n      }\n\n      // Get parent folder info if available\n      let parentInfo = \"Top level\"\n      if (folderToDelete.parent_id) {\n        try {\n          const parentFolder = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${folderToDelete.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (parentFolder?.title) {\n            parentInfo = `Inside \"${parentFolder.title}\" (notebook_id: \"${folderToDelete.parent_id}\")`\n          }\n        } catch {\n          parentInfo = `Parent ID: ${folderToDelete.parent_id}`\n        }\n      }\n\n      // Delete the folder\n      this.unwrap(await this.apiClient.delete(`/folders/${options.folder_id}`))\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`🗑️  Successfully deleted notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Deleted Notebook Details:`)\n      resultLines.push(`   Title: \"${folderToDelete.title}\"`)\n      resultLines.push(`   Folder ID: ${folderToDelete.id}`)\n      resultLines.push(`   Location: ${parentInfo}`)\n\n      if (totalContent > 0) {\n        resultLines.push(`   Deleted Content: ${noteCount} notes and ${subfolderCount} subfolders`)\n        resultLines.push(\"\")\n        resultLines.push(`⚠️  All ${totalContent} items inside have been permanently deleted!`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`⚠️  This notebook has been permanently deleted and cannot be recovered.`)\n\n      if (folderToDelete.parent_id) {\n        resultLines.push(\"\")\n        resultLines.push(`🔗 Related actions:`)\n        resultLines.push(`   - View parent notebook: read_notebook notebook_id=\"${folderToDelete.parent_id}\"`)\n        resultLines.push(`   - View all notebooks: list_notebooks`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n        }\n        if (error.response.status === 403) {\n          return `Permission denied: Cannot delete folder with ID \"${options.folder_id}\".\\n\\nThis might be a protected system folder.`\n        }\n        if (error.response.status === 409) {\n          return `Cannot delete folder: It may contain items that prevent deletion.\\n\\nTry moving or deleting the contents first, or use force option.`\n        }\n      }\n      return this.formatError(error, \"deleting folder\")\n    }\n  }\n}\n\nexport default DeleteFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface DeleteNoteOptions {\n  note_id: string\n  confirm?: boolean | undefined\n}\n\nclass DeleteNote extends BaseTool {\n  async call(options: DeleteNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    // Validate required note_id\n    if (!options.note_id) {\n      return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    const noteIdError = this.validateId(options.note_id, \"note\")\n    if (noteIdError) {\n      return noteIdError\n    }\n\n    // Require explicit confirmation for safety\n    if (!options.confirm) {\n      return `⚠️  This will permanently delete the note!\\n\\nTo confirm deletion, use:\\ndelete_note {\"note_id\": \"${options.note_id}\", \"confirm\": true}\\n\\n⚠️  This action cannot be undone!`\n    }\n\n    try {\n      // First, get the note details to show what's being deleted\n      const noteToDelete = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n          query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,created_time,updated_time\" },\n        }),\n      )\n\n      if (!noteToDelete || !noteToDelete.id) {\n        return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n      }\n\n      // Get notebook info if available\n      let notebookInfo = \"Root level\"\n      if (noteToDelete.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${noteToDelete.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (notebook?.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${noteToDelete.parent_id}\")`\n          }\n        } catch {\n          notebookInfo = `Notebook ID: ${noteToDelete.parent_id}`\n        }\n      }\n\n      // Delete the note\n      this.unwrap(await this.apiClient.delete(`/notes/${options.note_id}`))\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`🗑️  Successfully deleted note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Deleted Note Details:`)\n      resultLines.push(`   Title: \"${noteToDelete.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${noteToDelete.id}`)\n      resultLines.push(`   Location: ${notebookInfo}`)\n\n      if (noteToDelete.is_todo) {\n        const status = noteToDelete.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`   Type: Todo (${status})`)\n      } else {\n        resultLines.push(`   Type: Regular note`)\n      }\n\n      const createdDate = this.formatDate(noteToDelete.created_time)\n      const updatedDate = this.formatDate(noteToDelete.updated_time)\n      resultLines.push(`   Created: ${createdDate}`)\n      resultLines.push(`   Last Updated: ${updatedDate}`)\n\n      // Show content preview if available\n      if (noteToDelete.body) {\n        const preview = noteToDelete.body.substring(0, 100).replace(/\\n/g, \" \")\n        const truncated = noteToDelete.body.length > 100 ? \"...\" : \"\"\n        resultLines.push(`   Content Preview: ${preview}${truncated}`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`⚠️  This note has been permanently deleted and cannot be recovered.`)\n\n      if (noteToDelete.parent_id) {\n        resultLines.push(\"\")\n        resultLines.push(`🔗 Related actions:`)\n        resultLines.push(`   - View containing notebook: read_notebook notebook_id=\"${noteToDelete.parent_id}\"`)\n        resultLines.push(`   - Search for similar notes: search_notes query=\"${noteToDelete.title}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n        }\n        if (error.response.status === 403) {\n          return `Permission denied: Cannot delete note with ID \"${options.note_id}\".\\n\\nThis might be a protected system note.`\n        }\n      }\n      return this.formatError(error, \"deleting note\")\n    }\n  }\n}\n\nexport default DeleteNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface EditFolderOptions {\n  folder_id: string\n  title?: string | undefined\n  parent_id?: string | undefined\n}\n\ninterface EditFolderResponse extends JoplinFolder {\n  updated_time: number\n}\n\nclass EditFolder extends BaseTool {\n  async call(options: EditFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n    }\n\n    // Validate required folder_id\n    if (!options.folder_id) {\n      return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n    }\n\n    const folderIdError = this.validateId(options.folder_id, \"notebook\")\n    if (folderIdError) {\n      return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n    }\n\n    // Validate that we have at least one field to update\n    const updateFields = [\"title\", \"parent_id\"]\n    const hasUpdate = updateFields.some((field) => options[field as keyof EditFolderOptions] !== undefined)\n\n    if (!hasUpdate) {\n      return \"Please provide at least one field to update. Available fields: title, parent_id\"\n    }\n\n    // Validate title if provided\n    if (options.title !== undefined && (typeof options.title !== \"string\" || options.title.trim() === \"\")) {\n      return \"Title must be a non-empty string.\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n      if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n        return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n      }\n\n      // Prevent self-parenting\n      if (options.parent_id === options.folder_id) {\n        return \"Error: A folder cannot be its own parent.\"\n      }\n    }\n\n    try {\n      // First, get the current folder to show before/after comparison\n      const currentFolder = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      if (!currentFolder || !currentFolder.id) {\n        return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n      }\n\n      // Prepare the update body - only include fields that are being updated\n      const updateBody: Partial<EditFolderOptions> = {}\n\n      if (options.title !== undefined) updateBody.title = options.title.trim()\n      if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n\n      // Update the folder\n      const updatedFolder = this.unwrap(\n        await this.apiClient.put<EditFolderResponse>(`/folders/${options.folder_id}`, updateBody),\n      )\n\n      // Validate response\n      if (!updatedFolder || typeof updatedFolder !== \"object\" || !updatedFolder.id) {\n        return \"Error: Unexpected response format from Joplin API when updating folder\"\n      }\n\n      // Get parent folder info for both old and new locations if parent_id changed\n      let oldParentInfo = \"Top level\"\n      let newParentInfo = \"Top level\"\n\n      if (currentFolder.parent_id) {\n        try {\n          const oldParent = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${currentFolder.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (oldParent?.title) {\n            oldParentInfo = `Inside \"${oldParent.title}\"`\n          }\n        } catch {\n          oldParentInfo = `Parent ID: ${currentFolder.parent_id}`\n        }\n      }\n\n      if (updatedFolder.parent_id && updatedFolder.parent_id !== currentFolder.parent_id) {\n        try {\n          const newParent = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${updatedFolder.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (newParent?.title) {\n            newParentInfo = `Inside \"${newParent.title}\"`\n          }\n        } catch {\n          newParentInfo = `Parent ID: ${updatedFolder.parent_id}`\n        }\n      } else if (updatedFolder.parent_id) {\n        newParentInfo = oldParentInfo\n      }\n\n      // Format success response with before/after comparison\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully updated notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Notebook: \"${updatedFolder.title}\"`)\n      resultLines.push(`   Folder ID: ${updatedFolder.id}`)\n      resultLines.push(\"\")\n\n      // Show what changed\n      resultLines.push(`🔄 Changes made:`)\n\n      if (options.title !== undefined && currentFolder.title !== updatedFolder.title) {\n        resultLines.push(`   Title: \"${currentFolder.title}\" → \"${updatedFolder.title}\"`)\n      }\n\n      if (options.parent_id !== undefined && currentFolder.parent_id !== updatedFolder.parent_id) {\n        resultLines.push(`   Location: ${oldParentInfo} → ${newParentInfo}`)\n      }\n\n      if (updatedFolder.updated_time) {\n        const updatedTime = this.formatDate(updatedFolder.updated_time)\n        resultLines.push(`   Last Updated: ${updatedTime}`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - View notebook: read_notebook notebook_id=\"${updatedFolder.id}\"`)\n      resultLines.push(`   - View all notebooks: list_notebooks`)\n      if (updatedFolder.parent_id) {\n        resultLines.push(`   - View parent notebook: read_notebook notebook_id=\"${updatedFolder.parent_id}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          if (error.config?.url?.includes(`/folders/${options.folder_id}`)) {\n            return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n          }\n          if (options.parent_id) {\n            return `Error: Parent folder with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n          }\n        }\n        if (error.response.status === 400) {\n          return `Error updating folder: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 409) {\n          return `Error: A folder with the title \"${options.title}\" might already exist in this location.\\n\\nTry a different title or check existing folders with list_notebooks.`\n        }\n      }\n      return this.formatError(error, \"updating folder\")\n    }\n  }\n}\n\nexport default EditFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface EditNoteOptions {\n  note_id: string\n  title?: string | undefined\n  body?: string | undefined\n  body_html?: string | undefined\n  parent_id?: string | undefined\n  is_todo?: boolean | undefined\n  todo_completed?: boolean | undefined\n  todo_due?: number | undefined\n}\n\ntype EditNoteResponse = JoplinNote\n\nclass EditNote extends BaseTool {\n  async call(options: EditNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n    }\n\n    // Validate required note_id\n    if (!options.note_id) {\n      return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n    }\n\n    const noteIdError = this.validateId(options.note_id, \"note\")\n    if (noteIdError) {\n      return noteIdError\n    }\n\n    // Validate that we have at least one field to update\n    const updateFields = [\"title\", \"body\", \"body_html\", \"parent_id\", \"is_todo\", \"todo_completed\", \"todo_due\"]\n    const hasUpdate = updateFields.some((field) => options[field as keyof EditNoteOptions] !== undefined)\n\n    if (!hasUpdate) {\n      return \"Please provide at least one field to update. Available fields: title, body, body_html, parent_id, is_todo, todo_completed, todo_due\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n      if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n        return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n      }\n    }\n\n    try {\n      // First, get the current note to show before/after comparison\n      const currentNote = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n          query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,todo_due,updated_time\" },\n        }),\n      )\n\n      if (!currentNote || !currentNote.id) {\n        return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n      }\n\n      // Prepare the update body - only include fields that are being updated\n      const updateBody: Partial<EditNoteOptions> = {}\n\n      if (options.title !== undefined) updateBody.title = options.title\n      if (options.body !== undefined) updateBody.body = options.body\n      if (options.body_html !== undefined) updateBody.body_html = options.body_html\n      if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n      if (options.is_todo !== undefined) updateBody.is_todo = options.is_todo\n      if (options.todo_completed !== undefined) updateBody.todo_completed = options.todo_completed\n      if (options.todo_due !== undefined) updateBody.todo_due = options.todo_due\n\n      // Update the note\n      const updatedNote = this.unwrap(\n        await this.apiClient.put<EditNoteResponse>(`/notes/${options.note_id}`, updateBody),\n      )\n\n      // Validate response\n      if (!updatedNote || typeof updatedNote !== \"object\" || !updatedNote.id) {\n        return \"Error: Unexpected response format from Joplin API when updating note\"\n      }\n\n      // Get notebook info for both old and new locations if parent_id changed\n      let oldNotebookInfo = \"Root level\"\n      let newNotebookInfo = \"Root level\"\n\n      if (currentNote.parent_id) {\n        try {\n          const oldNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${currentNote.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (oldNotebook?.title) {\n            oldNotebookInfo = `\"${oldNotebook.title}\"`\n          }\n        } catch {\n          oldNotebookInfo = `Notebook ID: ${currentNote.parent_id}`\n        }\n      }\n\n      if (updatedNote.parent_id && updatedNote.parent_id !== currentNote.parent_id) {\n        try {\n          const newNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${updatedNote.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (newNotebook?.title) {\n            newNotebookInfo = `\"${newNotebook.title}\"`\n          }\n        } catch {\n          newNotebookInfo = `Notebook ID: ${updatedNote.parent_id}`\n        }\n      } else if (updatedNote.parent_id) {\n        newNotebookInfo = oldNotebookInfo\n      }\n\n      // Format success response with before/after comparison\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully updated note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Note: \"${updatedNote.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${updatedNote.id}`)\n      resultLines.push(\"\")\n\n      // Show what changed\n      resultLines.push(`🔄 Changes made:`)\n\n      if (options.title !== undefined && currentNote.title !== updatedNote.title) {\n        resultLines.push(`   Title: \"${currentNote.title}\" → \"${updatedNote.title}\"`)\n      }\n\n      if (options.parent_id !== undefined && currentNote.parent_id !== updatedNote.parent_id) {\n        resultLines.push(`   Location: ${oldNotebookInfo} → ${newNotebookInfo}`)\n      }\n\n      if (options.is_todo !== undefined && currentNote.is_todo !== updatedNote.is_todo) {\n        const oldType = currentNote.is_todo ? \"Todo\" : \"Regular note\"\n        const newType = updatedNote.is_todo ? \"Todo\" : \"Regular note\"\n        resultLines.push(`   Type: ${oldType} → ${newType}`)\n      }\n\n      if (options.todo_completed !== undefined && currentNote.todo_completed !== updatedNote.todo_completed) {\n        const oldStatus = currentNote.todo_completed ? \"Completed\" : \"Not completed\"\n        const newStatus = updatedNote.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`   Todo Status: ${oldStatus} → ${newStatus}`)\n      }\n\n      if (options.todo_due !== undefined) {\n        const oldDue = currentNote.todo_due ? this.formatDate(currentNote.todo_due) : \"No due date\"\n        const newDue = updatedNote.todo_due ? this.formatDate(updatedNote.todo_due) : \"No due date\"\n        if (oldDue !== newDue) {\n          resultLines.push(`   Due Date: ${oldDue} → ${newDue}`)\n        }\n      }\n\n      if (options.body !== undefined) {\n        resultLines.push(`   Content: Updated`)\n      }\n\n      if (options.body_html !== undefined) {\n        resultLines.push(`   HTML Content: Updated`)\n      }\n\n      const updatedTime = this.formatDate(updatedNote.updated_time)\n      resultLines.push(`   Last Updated: ${updatedTime}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - Read the note: read_note note_id=\"${updatedNote.id}\"`)\n      if (updatedNote.parent_id) {\n        resultLines.push(`   - View notebook: read_notebook notebook_id=\"${updatedNote.parent_id}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n        }\n        if (error.response.status === 400) {\n          return `Error updating note: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n        }\n      }\n      return this.formatError(error, \"updating note\")\n    }\n  }\n}\n\nexport default EditNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\nclass ListNotebooks extends BaseTool {\n  async call(): Promise<string> {\n    try {\n      const notebooks = this.unwrap(\n        await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      const notebooksByParentId: Record<string, JoplinFolder[]> = {}\n\n      notebooks.forEach((notebook) => {\n        const parentId = notebook.parent_id || \"\"\n        if (!notebooksByParentId[parentId]) {\n          notebooksByParentId[parentId] = []\n        }\n        notebooksByParentId[parentId].push(notebook)\n      })\n\n      // Add a header with instructions\n      const resultLines = [\n        \"Joplin Notebooks:\\n\",\n        \"NOTE: To read a notebook, use the notebook_id with the read_notebook command\\n\",\n        'Example: read_notebook notebook_id=\"your-notebook-id\"\\n\\n',\n      ]\n\n      // Add the notebook hierarchy\n      resultLines.push(\n        ...this.notebooksLines(notebooksByParentId[\"\"] || [], {\n          indent: 0,\n          notebooksByParentId,\n        }),\n      )\n\n      return resultLines.join(\"\")\n    } catch (error: unknown) {\n      return this.formatError(error, \"listing notebooks\")\n    }\n  }\n\n  private notebooksLines(\n    notebooks: JoplinFolder[],\n    {\n      indent = 0,\n      notebooksByParentId,\n    }: {\n      indent: number\n      notebooksByParentId: Record<string, JoplinFolder[]>\n    },\n  ): string[] {\n    const result: string[] = []\n    const indentSpaces = \" \".repeat(indent)\n\n    this.sortNotebooks(notebooks).forEach((notebook) => {\n      const id = notebook.id\n      result.push(`${indentSpaces}Notebook: \"${notebook.title}\" (notebook_id: \"${id}\")\\n`)\n\n      const childNotebooks = notebooksByParentId[id]\n      if (childNotebooks) {\n        result.push(\n          ...this.notebooksLines(childNotebooks, {\n            indent: indent + 2,\n            notebooksByParentId,\n          }),\n        )\n      }\n    })\n\n    return result\n  }\n\n  private sortNotebooks(notebooks: JoplinFolder[]): JoplinFolder[] {\n    // Ensure that notebooks starting with '[0]' are sorted first\n    const CHARACTER_BEFORE_A = String.fromCharCode(\"A\".charCodeAt(0) - 1)\n    return [...notebooks].sort((a, b) => {\n      const titleA = a.title.replace(\"[\", CHARACTER_BEFORE_A)\n      const titleB = b.title.replace(\"[\", CHARACTER_BEFORE_A)\n      return titleA.localeCompare(titleB)\n    })\n  }\n}\n\nexport default ListNotebooks\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\nclass ReadMultiNote extends BaseTool {\n  async call(noteIds: string[]): Promise<string> {\n    if (!noteIds || !Array.isArray(noteIds) || noteIds.length === 0) {\n      return 'Please provide an array of note IDs. Example: read_multinote note_ids=[\"id1\", \"id2\", \"id3\"]'\n    }\n\n    // Validate that all IDs look like valid note IDs\n    const invalidIds = noteIds.filter((id) => !id || id.length < 10 || !id.match(/[a-f0-9]/i))\n    if (invalidIds.length > 0) {\n      return `Error: Some IDs do not appear to be valid note IDs: ${invalidIds.join(\", \")}\\n\\nNote IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse search_notes to find notes and their IDs.`\n    }\n\n    const resultLines: string[] = []\n    const notFound: string[] = []\n    const errors: string[] = []\n    const successful: string[] = []\n\n    // Add a header\n    resultLines.push(`# Reading ${noteIds.length} notes\\n`)\n\n    // Process each note ID\n    for (let i = 0; i < noteIds.length; i++) {\n      const noteId = noteIds[i]\n      resultLines.push(`## Note ${i + 1} of ${noteIds.length} (ID: ${noteId})\\n`)\n\n      try {\n        // Get the note details with all relevant fields\n        const note = this.unwrap(\n          await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n            query: {\n              fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n            },\n          }),\n        )\n\n        // Validate note response\n        if (!note || typeof note !== \"object\" || !note.id) {\n          errors.push(noteId)\n          resultLines.push(`Error: Unexpected response format from Joplin API when fetching note ${noteId}\\n`)\n          continue\n        }\n\n        successful.push(noteId)\n\n        // Get the notebook info to show where this note is located\n        let notebookInfo = \"Unknown notebook\"\n        if (note.parent_id) {\n          try {\n            const notebook = this.unwrap(\n              await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n                query: { fields: \"id,title\" },\n              }),\n            )\n            if (notebook && notebook.title) {\n              notebookInfo = `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n            }\n          } catch (err: unknown) {\n            process.stderr.write(`Error fetching notebook info for note ${noteId}: ${err}\\n`)\n            // Continue even if we can't get the notebook info\n          }\n        }\n\n        // Add note metadata\n        resultLines.push(`### Note: \"${note.title}\"`)\n        resultLines.push(`Notebook: ${notebookInfo}`)\n\n        // Add todo status if applicable\n        if (note.is_todo) {\n          const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n          resultLines.push(`Status: ${status}`)\n\n          if (note.todo_due) {\n            const dueDate = this.formatDate(note.todo_due)\n            resultLines.push(`Due: ${dueDate}`)\n          }\n        }\n\n        // Add timestamps\n        const createdDate = this.formatDate(note.created_time)\n        const updatedDate = this.formatDate(note.updated_time)\n        resultLines.push(`Created: ${createdDate}`)\n        resultLines.push(`Updated: ${updatedDate}`)\n\n        // Add a separator before the note content\n        resultLines.push(\"\\n---\\n\")\n\n        // Add the note body\n        if (note.body) {\n          resultLines.push(note.body)\n        } else {\n          resultLines.push(\"(This note has no content)\")\n        }\n\n        // Add a separator after the note\n        resultLines.push(\"\\n---\\n\")\n      } catch (error: any) {\n        process.stderr.write(`Error reading note ${noteId}: ${error}\\n`)\n        if (error.response && error.response.status === 404) {\n          notFound.push(noteId)\n          resultLines.push(`Note with ID \"${noteId}\" not found.\\n`)\n        } else {\n          errors.push(noteId)\n          resultLines.push(`Error reading note: ${error.message || \"Unknown error\"}\\n`)\n        }\n      }\n    }\n\n    // Add a summary at the end\n    resultLines.push(\"# Summary\")\n    resultLines.push(`Total notes requested: ${noteIds.length}`)\n    resultLines.push(`Successfully retrieved: ${successful.length}`)\n\n    if (notFound.length > 0) {\n      resultLines.push(`Notes not found: ${notFound.length}`)\n      resultLines.push(`IDs not found: ${notFound.join(\", \")}`)\n    }\n\n    if (errors.length > 0) {\n      resultLines.push(`Errors encountered: ${errors.length}`)\n      resultLines.push(`IDs with errors: ${errors.join(\", \")}`)\n    }\n\n    return resultLines.join(\"\\n\")\n  }\n}\n\nexport default ReadMultiNote\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\nclass ReadNote extends BaseTool {\n  async call(noteId: string): Promise<string> {\n    const validationError = this.validateId(noteId, \"note\")\n    if (validationError) {\n      return validationError\n    }\n\n    try {\n      // Get the note details with all relevant fields\n      const note = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n          query: {\n            fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n          },\n        }),\n      )\n\n      // Validate note response\n      if (!note || typeof note !== \"object\" || !note.id) {\n        return `Error: Unexpected response format from Joplin API when fetching note`\n      }\n\n      // Get the notebook info to show where this note is located\n      let notebookInfo = \"Unknown notebook\"\n      if (note.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (notebook && notebook.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n          }\n        } catch (err: unknown) {\n          process.stderr.write(`Error fetching notebook info: ${err}\\n`)\n          // Continue even if we can't get the notebook info\n        }\n      }\n\n      // Format the note content\n      const resultLines: string[] = []\n\n      // Add note header with metadata\n      resultLines.push(`# Note: \"${note.title}\"`)\n      resultLines.push(`Note ID: ${note.id}`)\n      resultLines.push(`Notebook: ${notebookInfo}`)\n\n      // Add todo status if applicable\n      if (note.is_todo) {\n        const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`Status: ${status}`)\n\n        if (note.todo_due) {\n          const dueDate = this.formatDate(note.todo_due)\n          resultLines.push(`Due: ${dueDate}`)\n        }\n      }\n\n      // Add timestamps\n      const createdDate = this.formatDate(note.created_time)\n      const updatedDate = this.formatDate(note.updated_time)\n      resultLines.push(`Created: ${createdDate}`)\n      resultLines.push(`Updated: ${updatedDate}`)\n\n      // Add a separator before the note content\n      resultLines.push(\"\\n---\\n\")\n\n      // Add the note body\n      if (note.body) {\n        resultLines.push(note.body)\n      } else {\n        resultLines.push(\"(This note has no content)\")\n      }\n\n      // Add a footer with helpful commands\n      resultLines.push(\"\\n---\\n\")\n      resultLines.push(\"Related commands:\")\n      resultLines.push(`- To view the notebook containing this note: read_notebook notebook_id=\"${note.parent_id}\"`)\n      resultLines.push('- To search for more notes: search_notes query=\"your search term\"')\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response && error.response.status === 404) {\n        return `Note with ID \"${noteId}\" not found.\\n\\nThis might happen if:\\n1. The ID is incorrect\\n2. You're using a notebook ID instead of a note ID\\n3. The note has been deleted\\n\\nUse search_notes to find notes and their IDs.`\n      }\n      return (\n        this.formatError(error, \"reading note\") +\n        `\\n\\nMake sure you're using a valid note ID.\\nUse search_notes to find notes and their IDs.`\n      )\n    }\n  }\n}\n\nexport default ReadNote\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface NotebookNotesResponse {\n  items: JoplinNote[]\n}\n\nclass ReadNotebook extends BaseTool {\n  async call(notebookId: string): Promise<string> {\n    const validationError = this.validateId(notebookId, \"notebook\")\n    if (validationError) {\n      return validationError\n    }\n\n    try {\n      // First, get the notebook details\n      const notebook = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${notebookId}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      // Validate notebook response\n      if (!notebook || typeof notebook !== \"object\" || !notebook.id) {\n        return `Error: Unexpected response format from Joplin API when fetching notebook`\n      }\n\n      // Get all notes in this notebook\n      const notes = this.unwrap(\n        await this.apiClient.get<NotebookNotesResponse>(`/folders/${notebookId}/notes`, {\n          query: { fields: \"id,title,updated_time,is_todo,todo_completed\" },\n        }),\n      )\n\n      // Validate notes response\n      if (!notes || typeof notes !== \"object\") {\n        return `Error: Unexpected response format from Joplin API when fetching notes`\n      }\n\n      if (!notes.items || !Array.isArray(notes.items) || notes.items.length === 0) {\n        return `Notebook \"${notebook.title}\" (notebook_id: \"${notebook.id}\") is empty.\\n\\nTry another notebook ID or use list_notebooks to see all available notebooks.`\n      }\n\n      // Format the notebook contents\n      const resultLines: string[] = []\n      resultLines.push(`# Notebook: \"${notebook.title}\" (notebook_id: \"${notebook.id}\")`)\n      resultLines.push(`Contains ${notes.items.length} notes:\\n`)\n      resultLines.push(`NOTE: This is showing the contents of notebook \"${notebook.title}\", not a specific note.\\n`)\n\n      // If multiple notes were found, add a hint about read_multinote\n      if (notes.items.length > 1) {\n        const noteIds = notes.items.map((note) => note.id)\n        resultLines.push(`TIP: To read all ${notes.items.length} notes at once, use:\\n`)\n        resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n      }\n\n      // Sort notes by updated_time (newest first)\n      const sortedNotes = [...notes.items].sort((a, b) => b.updated_time - a.updated_time)\n\n      sortedNotes.forEach((note) => {\n        const updatedDate = this.formatDate(note.updated_time)\n\n        // Add checkbox for todos\n        if (note.is_todo) {\n          const checkboxStatus = note.todo_completed ? \"✅\" : \"☐\"\n          resultLines.push(`- ${checkboxStatus} Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        } else {\n          resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        }\n\n        resultLines.push(`  Updated: ${updatedDate}`)\n        resultLines.push(`  To read this note: read_note note_id=\"${note.id}\"`)\n        resultLines.push(\"\") // Empty line between notes\n      })\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response && error.response.status === 404) {\n        return `Notebook with ID \"${notebookId}\" not found.\\n\\nThis might happen if:\\n1. The ID is incorrect\\n2. You're using a note title instead of a notebook ID\\n3. The notebook has been deleted\\n\\nUse list_notebooks to see all available notebooks with their IDs.`\n      }\n      return (\n        this.formatError(error, \"reading notebook\") +\n        `\\n\\nMake sure you're using a valid notebook ID, not a note title.\\nUse list_notebooks to see all available notebooks with their IDs.`\n      )\n    }\n  }\n}\n\nexport default ReadNotebook\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface SearchResult {\n  items: JoplinNote[]\n}\n\nclass SearchNotes extends BaseTool {\n  async call(query: string): Promise<string> {\n    if (!query) {\n      return \"Please provide a search query.\"\n    }\n\n    try {\n      // Search for notes with the given query\n      const searchResults = this.unwrap(\n        await this.apiClient.get<SearchResult>(\"/search\", {\n          query: {\n            query,\n            fields: \"id,title,body,parent_id,updated_time\",\n          },\n        }),\n      )\n\n      // Handle case where the API doesn't return the expected structure\n      if (!searchResults || typeof searchResults !== \"object\") {\n        return `Error: Unexpected response format from Joplin API`\n      }\n\n      // Handle case where no items were found\n      if (!searchResults.items || !Array.isArray(searchResults.items) || searchResults.items.length === 0) {\n        return `No notes found matching query: \"${query}\"`\n      }\n\n      // Get all folders to be able to show notebook names\n      const folders = this.unwrap(\n        await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n          query: {\n            fields: \"id,title\",\n          },\n        }),\n      )\n\n      // Create a map of folder IDs to folder titles for quick lookup\n      const folderMap: Record<string, string> = {}\n      folders.forEach((folder) => {\n        folderMap[folder.id] = folder.title\n      })\n\n      // Format the search results\n      const resultLines: string[] = []\n      resultLines.push(`Found ${searchResults.items.length} notes matching query: \"${query}\"\\n`)\n      resultLines.push(`NOTE: To read a notebook, use the notebook ID (not the note title)\\n`)\n\n      // If multiple notes were found, add a hint about read_multinote\n      if (searchResults.items.length > 1) {\n        const noteIds = searchResults.items.map((note) => note.id)\n        resultLines.push(`TIP: To read all ${searchResults.items.length} notes at once, use:\\n`)\n        resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n      }\n\n      searchResults.items.forEach((note) => {\n        const notebookTitle = folderMap[note.parent_id || \"\"] || \"Unknown notebook\"\n        const notebookId = note.parent_id || \"unknown\"\n        const updatedDate = this.formatDate(note.updated_time)\n\n        resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        resultLines.push(`  Notebook: \"${notebookTitle}\" (notebook_id: \"${notebookId}\")`)\n        resultLines.push(`  Updated: ${updatedDate}`)\n\n        // Add a snippet of the note body if available\n        if (note.body) {\n          const snippet = note.body.substring(0, 100).replace(/\\n/g, \" \") + (note.body.length > 100 ? \"...\" : \"\")\n          resultLines.push(`  Snippet: ${snippet}`)\n        }\n\n        // Add hints for using related commands\n        resultLines.push(`  To read this note: read_note note_id=\"${note.id}\"`)\n        resultLines.push(`  To read this notebook: read_notebook notebook_id=\"${notebookId}\"`)\n\n        resultLines.push(\"\") // Empty line between notes\n      })\n\n      return resultLines.join(\"\\n\")\n    } catch (error: unknown) {\n      return this.formatError(error, \"searching notes\")\n    }\n  }\n}\n\nexport default SearchNotes\n","import { Either } from \"functype\"\n\nimport JoplinAPIClient from \"./lib/joplin-api-client.js\"\nimport type { JoplinSidecar } from \"./lib/joplin-sidecar.js\"\nimport {\n  CreateFolder,\n  CreateNote,\n  DeleteFolder,\n  DeleteNote,\n  EditFolder,\n  EditNote,\n  ListNotebooks,\n  ReadMultiNote,\n  ReadNote,\n  ReadNotebook,\n  SearchNotes,\n} from \"./lib/tools/index.js\"\n\nexport type JoplinServerConfig = {\n  host: string\n  port: number\n  token: string\n  sidecar?: JoplinSidecar\n}\n\nexport class JoplinServerManager {\n  private apiClient: JoplinAPIClient\n  private config: JoplinServerConfig\n  private connected: boolean = false\n  private tools: {\n    listNotebooks: ListNotebooks\n    searchNotes: SearchNotes\n    readNotebook: ReadNotebook\n    readNote: ReadNote\n    readMultiNote: ReadMultiNote\n    createNote: CreateNote\n    createFolder: CreateFolder\n    editNote: EditNote\n    editFolder: EditFolder\n    deleteNote: DeleteNote\n    deleteFolder: DeleteFolder\n  }\n\n  constructor(config: JoplinServerConfig) {\n    this.config = config\n    this.apiClient = new JoplinAPIClient({\n      host: config.host,\n      port: config.port,\n      token: config.token,\n    })\n\n    this.tools = {\n      listNotebooks: new ListNotebooks(this.apiClient),\n      searchNotes: new SearchNotes(this.apiClient),\n      readNotebook: new ReadNotebook(this.apiClient),\n      readNote: new ReadNote(this.apiClient),\n      readMultiNote: new ReadMultiNote(this.apiClient),\n      createNote: new CreateNote(this.apiClient),\n      createFolder: new CreateFolder(this.apiClient),\n      editNote: new EditNote(this.apiClient),\n      editFolder: new EditFolder(this.apiClient),\n      deleteNote: new DeleteNote(this.apiClient),\n      deleteFolder: new DeleteFolder(this.apiClient),\n    }\n  }\n\n  async ensureConnected(): Promise<void> {\n    if (this.connected) {\n      const result = await this.apiClient.serviceAvailable()\n      if (Either.isRight(result)) return\n      this.connected = false\n    }\n\n    const available = await this.apiClient.serviceAvailable()\n    if (Either.isRight(available)) {\n      this.connected = true\n      process.stderr.write(`Connected to Joplin at ${this.config.host}:${this.config.port}\\n`)\n      return\n    }\n\n    // If sidecar exists, try starting it\n    if (this.config.sidecar) {\n      process.stderr.write(\"Joplin not available, starting sidecar...\\n\")\n      const startResult = await this.config.sidecar.start()\n      if (Either.isLeft(startResult)) {\n        const error = startResult.fold(\n          (e) => e,\n          () => null as never,\n        )\n        throw new Error(`Sidecar failed [${error.code}]: ${error.message}`)\n      }\n      const retryAvailable = await this.apiClient.serviceAvailable()\n      if (Either.isRight(retryAvailable)) {\n        this.connected = true\n        process.stderr.write(`Connected to Joplin via sidecar at ${this.config.host}:${this.config.port}\\n`)\n        return\n      }\n    }\n\n    throw new Error(\n      `Joplin is not available at ${this.config.host}:${this.config.port}. ` +\n        `Please ensure Joplin is running or configure a sidecar.`,\n    )\n  }\n\n  // Tool execution methods\n  async listNotebooks(): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.listNotebooks.call()\n  }\n\n  async searchNotes(query: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.searchNotes.call(query)\n  }\n\n  async readNotebook(notebookId: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readNotebook.call(notebookId)\n  }\n\n  async readNote(noteId: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readNote.call(noteId)\n  }\n\n  async readMultiNote(noteIds: string[]): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readMultiNote.call(noteIds)\n  }\n\n  async createNote(params: {\n    title?: string | undefined\n    body?: string | undefined\n    body_html?: string | undefined\n    parent_id?: string | undefined\n    is_todo?: boolean | undefined\n    image_data_url?: string | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.createNote.call(params)\n  }\n\n  async createFolder(params: { title: string; parent_id?: string | undefined }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.createFolder.call(params)\n  }\n\n  async editNote(params: {\n    note_id: string\n    title?: string | undefined\n    body?: string | undefined\n    body_html?: string | undefined\n    parent_id?: string | undefined\n    is_todo?: boolean | undefined\n    todo_completed?: boolean | undefined\n    todo_due?: number | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.editNote.call(params)\n  }\n\n  async editFolder(params: {\n    folder_id: string\n    title?: string | undefined\n    parent_id?: string | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.editFolder.call(params)\n  }\n\n  async deleteNote(params: { note_id: string; confirm?: boolean | undefined }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.deleteNote.call(params)\n  }\n\n  async deleteFolder(params: {\n    folder_id: string\n    confirm?: boolean | undefined\n    force?: boolean | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.deleteFolder.call(params)\n  }\n\n  async sync(): Promise<string> {\n    await this.ensureConnected()\n    const desktopWarning = this.config.sidecar?.isDesktopDetected()\n      ? \"\\n\\nNote: Joplin Desktop is also running. The sidecar and Desktop use separate databases. \" +\n        \"Notes sync between them only if both are configured with the same sync target.\"\n      : \"\"\n    // Try triggering sync via the Joplin REST API (POST /services/sync)\n    const result = await this.apiClient.post<Record<string, unknown>>(\"/services/sync\", { action: \"start\" })\n    return result.fold(\n      (error) => {\n        const msg = error.message || String(error)\n        // 404 or \"No action API\" = Joplin instance doesn't expose sync as a REST service\n        // This is normal for Joplin Terminal CLI — it auto-syncs on its configured interval\n        if (msg.includes(\"404\") || msg.includes(\"No action API\") || msg.includes(\"No such service\")) {\n          return (\n            \"Sync is managed automatically by the Joplin server on its configured interval \" +\n            \"(default: every 5 minutes). On-demand sync is not available via the Joplin Terminal API.\" +\n            desktopWarning\n          )\n        }\n        return `Sync failed: ${msg}${desktopWarning}`\n      },\n      () => `Sync triggered successfully.${desktopWarning}`,\n    )\n  }\n}\n\nexport function initializeJoplinManager(config: JoplinServerConfig): JoplinServerManager {\n  return new JoplinServerManager(config)\n}\n","import { FastMCP } from \"fastmcp\"\nimport { readFileSync } from \"fs\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { z } from \"zod\"\n\nimport { initializeJoplinManager, JoplinServerManager } from \"./server-core.js\"\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"))\nconst VERSION = packageJson.version\n\nexport type FastMCPServerOptions = {\n  host: string\n  port: number\n  token: string\n  httpPort?: number\n  endpoint?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): { server: FastMCP; manager: JoplinServerManager } {\n  process.stderr.write(\"Initializing FastMCP server for Joplin...\\n\")\n\n  // Initialize Joplin manager\n  const manager = initializeJoplinManager({ host: options.host, port: options.port, token: options.token })\n\n  // Create FastMCP server\n  const server = new FastMCP({\n    name: \"joplin\",\n    version: VERSION,\n    health: {\n      enabled: true,\n      path: \"/health\",\n      status: 200,\n      message: JSON.stringify({\n        status: \"healthy\",\n        service: \"joplin-mcp-server\",\n        version: VERSION,\n        joplinPort: options.port,\n        timestamp: new Date().toISOString(),\n      }),\n    },\n  })\n\n  // Add list_notebooks tool\n  server.addTool({\n    name: \"list_notebooks\",\n    description: \"Retrieve the complete notebook hierarchy from Joplin\",\n    parameters: z.object({}),\n    execute: async () => {\n      return await manager.listNotebooks()\n    },\n  })\n\n  // Add search_notes tool\n  server.addTool({\n    name: \"search_notes\",\n    description: \"Search for notes in Joplin and return matching notebooks\",\n    parameters: z.object({\n      query: z.string().describe(\"Search query for notes\"),\n    }),\n    execute: async (args) => {\n      return await manager.searchNotes(args.query)\n    },\n  })\n\n  // Add read_notebook tool\n  server.addTool({\n    name: \"read_notebook\",\n    description: \"Read the contents of a specific notebook\",\n    parameters: z.object({\n      notebook_id: z.string().describe(\"ID of the notebook to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readNotebook(args.notebook_id)\n    },\n  })\n\n  // Add read_note tool\n  server.addTool({\n    name: \"read_note\",\n    description: \"Read the full content of a specific note\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readNote(args.note_id)\n    },\n  })\n\n  // Add read_multinote tool\n  server.addTool({\n    name: \"read_multinote\",\n    description: \"Read the full content of multiple notes at once\",\n    parameters: z.object({\n      note_ids: z.array(z.string()).describe(\"Array of note IDs to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readMultiNote(args.note_ids)\n    },\n  })\n\n  // Add create_note tool\n  server.addTool({\n    name: \"create_note\",\n    description: \"Create a new note in Joplin\",\n    parameters: z.object({\n      title: z.string().optional().describe(\"Note title\"),\n      body: z.string().optional().describe(\"Note content in Markdown\"),\n      body_html: z.string().optional().describe(\"Note content in HTML\"),\n      parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n      is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n      image_data_url: z.string().optional().describe(\"Base64 encoded image data URL\"),\n    }),\n    execute: async (args) => {\n      return await manager.createNote(args)\n    },\n  })\n\n  // Add create_folder tool\n  server.addTool({\n    name: \"create_folder\",\n    description: \"Create a new folder/notebook in Joplin\",\n    parameters: z.object({\n      title: z.string().describe(\"Notebook title\"),\n      parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n    }),\n    execute: async (args) => {\n      return await manager.createFolder(args)\n    },\n  })\n\n  // Add edit_note tool\n  server.addTool({\n    name: \"edit_note\",\n    description: \"Edit/update an existing note in Joplin\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to edit\"),\n      title: z.string().optional().describe(\"New note title\"),\n      body: z.string().optional().describe(\"New note content in Markdown\"),\n      body_html: z.string().optional().describe(\"New note content in HTML\"),\n      parent_id: z.string().optional().describe(\"New parent notebook ID\"),\n      is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n      todo_completed: z.boolean().optional().describe(\"Whether todo is completed\"),\n      todo_due: z.number().optional().describe(\"Todo due date (Unix timestamp)\"),\n    }),\n    execute: async (args) => {\n      return await manager.editNote(args)\n    },\n  })\n\n  // Add edit_folder tool\n  server.addTool({\n    name: \"edit_folder\",\n    description: \"Edit/update an existing folder/notebook in Joplin\",\n    parameters: z.object({\n      folder_id: z.string().describe(\"ID of the folder to edit\"),\n      title: z.string().optional().describe(\"New folder title\"),\n      parent_id: z.string().optional().describe(\"New parent folder ID\"),\n    }),\n    execute: async (args) => {\n      return await manager.editFolder(args)\n    },\n  })\n\n  // Add delete_note tool\n  server.addTool({\n    name: \"delete_note\",\n    description: \"Delete a note from Joplin (requires confirmation)\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to delete\"),\n      confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n    }),\n    execute: async (args) => {\n      return await manager.deleteNote(args)\n    },\n  })\n\n  // Add delete_folder tool\n  server.addTool({\n    name: \"delete_folder\",\n    description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n    parameters: z.object({\n      folder_id: z.string().describe(\"ID of the folder to delete\"),\n      confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n      force: z.boolean().optional().describe(\"Force delete even if folder has contents\"),\n    }),\n    execute: async (args) => {\n      return await manager.deleteFolder(args)\n    },\n  })\n\n  // Add sync tool\n  server.addTool({\n    name: \"sync\",\n    description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n    parameters: z.object({}),\n    execute: async () => {\n      return await manager.sync()\n    },\n  })\n\n  process.stderr.write(\"FastMCP server configured with 12 Joplin tools\\n\")\n  return { server, manager }\n}\n\nexport async function startFastMCPServer(options: FastMCPServerOptions): Promise<void> {\n  const { server } = createFastMCPServer(options)\n\n  process.stderr.write(`Configured for Joplin at ${options.host}:${options.port}\\n`)\n\n  const port = options.httpPort || 3000\n  const endpoint = options.endpoint || \"/mcp\"\n\n  await server.start({\n    transportType: \"httpStream\",\n    httpStream: {\n      port,\n      endpoint: endpoint as `/${string}`,\n    },\n  })\n\n  process.stderr.write(`FastMCP server running on http://0.0.0.0:${port}${endpoint}\\n`)\n}\n","#!/usr/bin/env node\n\ndeclare const __VERSION__: string\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { type CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\"\nimport fs from \"fs\"\nimport path from \"path\"\nimport { fileURLToPath } from \"url\"\n\nimport { DEFAULT_API_PORT, JoplinSidecar, type SyncTarget } from \"./lib/joplin-sidecar.js\"\nimport parseArgs from \"./lib/parse-args.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\nimport { startFastMCPServer } from \"./server-fastmcp.js\"\n\n// Parse command line arguments\nconst parsedArgs = parseArgs()\nconst { transport, httpPort, profileDir, syncTarget } = parsedArgs\n\nconst isHttpMode = transport === \"http\"\n\n// External mode: JOPLIN_HOST/JOPLIN_PORT set = connect directly, skip sidecar\nconst externalHost = process.env.JOPLIN_HOST\nconst externalPort = process.env.JOPLIN_PORT ? parseInt(process.env.JOPLIN_PORT, 10) : undefined\nconst externalMode = !!(externalHost || externalPort)\n\n// Token is required for external mode, auto-generated for sidecar mode\nif (!process.env.JOPLIN_TOKEN && externalMode) {\n  process.stderr.write(\n    \"Error: JOPLIN_TOKEN is required in external mode. Use --token <token> or set JOPLIN_TOKEN environment variable.\\n\",\n  )\n  process.exit(1)\n}\n\n// In sidecar mode, persist the auto-generated token so the config hash stays stable\n// across restarts, enabling config caching to skip redundant `joplin config` CLI calls.\nconst joplinToken = (() => {\n  if (process.env.JOPLIN_TOKEN) return process.env.JOPLIN_TOKEN\n  if (externalMode) return `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n  const tokenPath = path.join(profileDir, \".mcp-token\")\n  try {\n    const saved = fs.readFileSync(tokenPath, \"utf-8\").trim()\n    if (saved) return saved\n  } catch {\n    // No saved token yet\n  }\n  const token = `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n  try {\n    fs.mkdirSync(profileDir, { recursive: true })\n    fs.writeFileSync(tokenPath, token)\n  } catch {\n    // Non-critical — token still works, just won't be cached\n  }\n  return token\n})()\n\n// Main startup logic\nasync function main(): Promise<void> {\n  let host: string\n  let port: number\n  let sidecar: JoplinSidecar | undefined\n\n  if (externalMode) {\n    // External mode — connect to existing Joplin instance (e.g. Windows desktop from WSL)\n    host = externalHost || \"127.0.0.1\"\n    port = externalPort || DEFAULT_API_PORT\n    process.stderr.write(`External mode: connecting to Joplin at ${host}:${port}\\n`)\n  } else {\n    // Sidecar mode — spawn and manage Joplin Terminal\n    sidecar = new JoplinSidecar({\n      profileDir,\n      apiPort: DEFAULT_API_PORT,\n      apiToken: joplinToken,\n      syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n    })\n\n    // Phase 1: Resolve port (fast — a few HTTP probes).\n    // Must complete before getPort() so downstream gets the correct port.\n    const portResult = await sidecar.resolvePort()\n    portResult.fold(\n      (err) => process.stderr.write(`Warning: Port resolution failed: ${err.message}\\n`),\n      (p) => process.stderr.write(`Sidecar will use port ${p}\\n`),\n    )\n\n    // Phase 2: Fire-and-forget the slow startup (CLI config, spawn, wait).\n    // ensureConnected() in server-core.ts will await or retry on first tool call.\n    sidecar.start().then((result) => {\n      result.fold(\n        (err) => {\n          process.stderr.write(`Warning: Sidecar failed to start: ${err.message}\\n`)\n          process.stderr.write(\"Attempting to connect to existing Joplin instance...\\n\")\n        },\n        () => {\n          process.stderr.write(\"Joplin sidecar started successfully\\n\")\n        },\n      )\n    })\n\n    host = sidecar.getHost()\n    port = sidecar.getPort()\n\n    // Cleanup on exit\n    const cleanup = async () => {\n      await sidecar!.stop()\n      process.exit(0)\n    }\n    process.on(\"SIGINT\", () => void cleanup())\n    process.on(\"SIGTERM\", () => void cleanup())\n  }\n\n  if (isHttpMode) {\n    process.stderr.write(\"Starting HTTP transport mode with FastMCP...\\n\")\n    await startFastMCPServer({\n      host,\n      port,\n      token: joplinToken,\n      httpPort,\n      endpoint: \"/mcp\",\n    })\n  } else {\n    process.stderr.write(\"Starting stdio transport mode...\\n\")\n    await startStdioServer(host, port, joplinToken, sidecar)\n  }\n}\n\nmain().catch((error) => {\n  process.stderr.write(`Failed to start MCP server: ${error}\\n`)\n  process.exit(1)\n})\n\nasync function startStdioServer(host: string, port: number, token: string, sidecar?: JoplinSidecar): Promise<void> {\n  const manager = initializeJoplinManager({ host, port, token, sidecar })\n\n  const server = new Server(\n    {\n      name: \"joplin-mcp-server\",\n      version: __VERSION__,\n    },\n    {\n      capabilities: {\n        resources: {},\n        tools: {},\n        prompts: {},\n      },\n    },\n  )\n\n  // Register tool list handler\n  server.setRequestHandler(ListToolsRequestSchema, () => {\n    return {\n      tools: [\n        {\n          name: \"list_notebooks\",\n          description: \"Retrieve the complete notebook hierarchy from Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {},\n          },\n        },\n        {\n          name: \"search_notes\",\n          description: \"Search for notes in Joplin and return matching notebooks\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              query: { type: \"string\", description: \"Search query\" },\n            },\n            required: [\"query\"],\n          },\n        },\n        {\n          name: \"read_notebook\",\n          description: \"Read the contents of a specific notebook\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              notebook_id: { type: \"string\", description: \"ID of the notebook to read\" },\n            },\n            required: [\"notebook_id\"],\n          },\n        },\n        {\n          name: \"read_note\",\n          description: \"Read the full content of a specific note\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to read\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"read_multinote\",\n          description: \"Read the full content of multiple notes at once\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_ids: { type: \"array\", items: { type: \"string\" }, description: \"Array of note IDs to read\" },\n            },\n            required: [\"note_ids\"],\n          },\n        },\n        {\n          name: \"create_note\",\n          description: \"Create a new note in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              title: { type: \"string\", description: \"Note title\" },\n              body: { type: \"string\", description: \"Note content in Markdown\" },\n              body_html: { type: \"string\", description: \"Note content in HTML\" },\n              parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n              is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n              image_data_url: { type: \"string\", description: \"Base64 encoded image data URL\" },\n            },\n          },\n        },\n        {\n          name: \"create_folder\",\n          description: \"Create a new folder/notebook in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              title: { type: \"string\", description: \"Notebook title\" },\n              parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n            },\n            required: [\"title\"],\n          },\n        },\n        {\n          name: \"edit_note\",\n          description: \"Edit/update an existing note in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to edit\" },\n              title: { type: \"string\", description: \"New note title\" },\n              body: { type: \"string\", description: \"New note content in Markdown\" },\n              body_html: { type: \"string\", description: \"New note content in HTML\" },\n              parent_id: { type: \"string\", description: \"New parent notebook ID\" },\n              is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n              todo_completed: { type: \"boolean\", description: \"Whether todo is completed\" },\n              todo_due: { type: \"number\", description: \"Todo due date (Unix timestamp)\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"edit_folder\",\n          description: \"Edit/update an existing folder/notebook in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              folder_id: { type: \"string\", description: \"ID of the folder to edit\" },\n              title: { type: \"string\", description: \"New folder title\" },\n              parent_id: { type: \"string\", description: \"New parent folder ID\" },\n            },\n            required: [\"folder_id\"],\n          },\n        },\n        {\n          name: \"delete_note\",\n          description: \"Delete a note from Joplin (requires confirmation)\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to delete\" },\n              confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"delete_folder\",\n          description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              folder_id: { type: \"string\", description: \"ID of the folder to delete\" },\n              confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n              force: { type: \"boolean\", description: \"Force delete even if folder has contents\" },\n            },\n            required: [\"folder_id\"],\n          },\n        },\n        {\n          name: \"sync\",\n          description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n          inputSchema: {\n            type: \"object\",\n            properties: {},\n          },\n        },\n      ],\n    }\n  })\n\n  // Register tool call handler\n  server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n    const toolName = request.params.name\n    const args = request.params.arguments || {}\n\n    try {\n      switch (toolName) {\n        case \"list_notebooks\": {\n          const listResult = await manager.listNotebooks()\n          return { content: [{ type: \"text\", text: listResult }], isError: false }\n        }\n\n        case \"search_notes\": {\n          const searchResult = await manager.searchNotes(args.query as string)\n          return { content: [{ type: \"text\", text: searchResult }], isError: false }\n        }\n\n        case \"read_notebook\": {\n          const notebookResult = await manager.readNotebook(args.notebook_id as string)\n          return { content: [{ type: \"text\", text: notebookResult }], isError: false }\n        }\n\n        case \"read_note\": {\n          const noteResult = await manager.readNote(args.note_id as string)\n          return { content: [{ type: \"text\", text: noteResult }], isError: false }\n        }\n\n        case \"read_multinote\": {\n          const multiResult = await manager.readMultiNote(args.note_ids as string[])\n          return { content: [{ type: \"text\", text: multiResult }], isError: false }\n        }\n\n        case \"create_note\": {\n          const createNoteResult = await manager.createNote(\n            args as {\n              title?: string | undefined\n              body?: string | undefined\n              body_html?: string | undefined\n              parent_id?: string | undefined\n              is_todo?: boolean | undefined\n              image_data_url?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: createNoteResult }], isError: false }\n        }\n\n        case \"create_folder\": {\n          const createFolderResult = await manager.createFolder(\n            args as {\n              title: string\n              parent_id?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: createFolderResult }], isError: false }\n        }\n\n        case \"edit_note\": {\n          const editNoteResult = await manager.editNote(\n            args as {\n              note_id: string\n              title?: string | undefined\n              body?: string | undefined\n              body_html?: string | undefined\n              parent_id?: string | undefined\n              is_todo?: boolean | undefined\n              todo_completed?: boolean | undefined\n              todo_due?: number | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: editNoteResult }], isError: false }\n        }\n\n        case \"edit_folder\": {\n          const editFolderResult = await manager.editFolder(\n            args as {\n              folder_id: string\n              title?: string | undefined\n              parent_id?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: editFolderResult }], isError: false }\n        }\n\n        case \"delete_note\": {\n          const deleteNoteResult = await manager.deleteNote(\n            args as {\n              note_id: string\n              confirm?: boolean | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: deleteNoteResult }], isError: false }\n        }\n\n        case \"delete_folder\": {\n          const deleteFolderResult = await manager.deleteFolder(\n            args as {\n              folder_id: string\n              confirm?: boolean | undefined\n              force?: boolean | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: deleteFolderResult }], isError: false }\n        }\n\n        case \"sync\": {\n          const syncResult = await manager.sync()\n          return { content: [{ type: \"text\", text: syncResult }], isError: false }\n        }\n\n        default:\n          throw new Error(`Unknown tool: ${toolName}`)\n      }\n    } catch (error) {\n      const errorMessage = error instanceof Error ? error.message : String(error)\n      return {\n        content: [{ type: \"text\", text: `Error: ${errorMessage}` }],\n        isError: true,\n      }\n    }\n  })\n\n  // Create logs directory if it doesn't exist\n  const __filename = fileURLToPath(import.meta.url)\n  const __dirname = path.dirname(__filename)\n  const logsDir = path.join(__dirname, \"..\", \"logs\")\n\n  if (!fs.existsSync(logsDir)) {\n    fs.mkdirSync(logsDir, { recursive: true })\n  }\n\n  // Create a log file for this session\n  const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n  const logFile = path.join(logsDir, `mcp-server-${timestamp}.log`)\n\n  // Create a custom transport wrapper to log commands and responses\n  class LoggingTransport extends StdioServerTransport {\n    private commandCounter: number\n\n    constructor() {\n      super()\n      this.commandCounter = 0\n    }\n\n    async sendMessage(message: unknown): Promise<void> {\n      const logEntry = {\n        timestamp: new Date().toISOString(),\n        direction: \"RESPONSE\",\n        message,\n      }\n\n      fs.appendFileSync(logFile, JSON.stringify(logEntry) + \"\\n\")\n\n      const parent = Object.getPrototypeOf(Object.getPrototypeOf(this))\n      return parent.sendMessage.call(this, message)\n    }\n\n    async handleMessage(message: unknown): Promise<void> {\n      this.commandCounter++\n      const logEntry = {\n        timestamp: new Date().toISOString(),\n        direction: \"COMMAND\",\n        commandNumber: this.commandCounter,\n        message,\n      }\n\n      fs.appendFileSync(logFile, JSON.stringify(logEntry) + \"\\n\")\n\n      const parent = Object.getPrototypeOf(Object.getPrototypeOf(this))\n      return parent.handleMessage.call(this, message)\n    }\n  }\n\n  const stdioTransport = new LoggingTransport()\n\n  try {\n    await server.connect(stdioTransport)\n    process.stderr.write(\"MCP server started and ready to receive commands\\n\")\n  } catch (error: unknown) {\n    process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`)\n    process.exit(1)\n  }\n}\n"],"mappings":"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"}
Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��?�&���%��j�!

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highSC-005Suspicious CommandsMedium ConfidenceLine 0

Node.js child process spawning

Detected by automated pattern matching (rule SC-005) with medium confidence. May be a false positive.

    6: import path, { dirname, join, resolve } from "path";
    7: import { fileURLToPath } from "url";
>>> 8: import { exec, execFile, spawn } from "child_process";
    9: import crypto$1 from "crypto";
    10: import { Either, Left, Match, Option, Right } from "functype";
Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: J�b�'���ӭ�즊�

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��(������i�^>��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��(������i�^>��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��?�&���%��+y�^

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��?�&���%��+y�^

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��?�&���%��^��^

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��?�&���%��^��^

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: J�b�'���ӭ�즊�

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highSC-005Suspicious CommandsMedium ConfidenceLine 0

Node.js child process spawning

Detected by automated pattern matching (rule SC-005) with medium confidence. May be a false positive.

>>> 1: {"version":3,"file":"index.js","names":["crypto"],"sources":["../src/lib/joplin-sidecar.ts","../src/lib/parse-args.ts","../src/lib/joplin-api-client.ts","../src/lib/tools/base-tool.ts","../src/lib/tools/create-folder.ts","../src/lib/tools/create-note.ts","../src/lib/tools/delete-folder.ts","../src/lib/tools/delete-note.ts","../src/lib/tools/edit-folder.ts","../src/lib/tools/edit-note.ts","../src/lib/tools/list-notebooks.ts","../src/lib/tools/read-multi-note.ts","../src/lib/tools/read-note.ts","../src/lib/tools/read-notebook.ts","../src/lib/tools/search-notes.ts","../src/server-core.ts","../src/server-fastmcp.ts","../src/index.ts"],"sourcesContent":["import { type ChildProcess, exec, execFile, spawn } from \"child_process\"\nimport crypto from \"crypto\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { join } from \"path\"\nimport { promisify } from \"util\"\n\nconst execAsync = promisify(exec)\nconst execFileAsync = promisify(execFile)\n\nconst isWindows = process.platform === \"win32\"\nconst whichCmd = isWindows ? \"where\" : \"which\"\n\nexport const DEFAULT_API_PORT = 41184\nconst MAX_PORT_ATTEMPTS = 10\n\nexport type SyncTarget =\n  | { type: \"none\" }\n  | { type: \"filesystem\"; path: string }\n  | { type: \"joplin-cloud\"; email: string; password: string }\n  | { type: \"joplin-server\"; url: string; email: string; password: string }\n  | { type: \"webdav\"; url: string; username: string; password: string }\n  | { type: \"nextcloud\"; url: string; username: string; password: string }\n  | { type: \"s3\"; bucket: string; region: string; accessKey: string; secretKey: string }\n  | { type: \"dropbox\" }\n  | { type: \"onedrive\" }\n\nexport type SidecarConfig = {\n  profileDir: string\n  apiPort: number\n  apiToken: string\n  syncTarget?: SyncTarget\n  syncInterval?: number\n}\n\nexport type SidecarError = {\n  code:\n    | \"CLI_NOT_FOUND\"\n    | \"CONFIG_FAILED\"\n    | \"SPAWN_FAILED\"\n    | \"HEALTH_CHECK_FAILED\"\n    | \"STOP_FAILED\"\n    | \"SYNC_FAILED\"\n    | \"PORT_CONFLICT\"\n    | \"PORT_OCCUPIED\"\n    | \"PORT_EXHAUSTED\"\n  message: string\n  cause?: unknown\n}\n\nconst sidecarError = (code: SidecarError[\"code\"], message: string, cause?: unknown): SidecarError => ({\n  code,\n  message,\n  cause,\n})\n\nconst syncTargetId = (target: SyncTarget): number =>\n  Match(target.type)\n    .case(\"none\", () => 0)\n    .case(\"filesystem\", () => 2)\n    .case(\"webdav\", () => 6)\n    .case(\"nextcloud\", () => 5)\n    .case(\"dropbox\", () => 7)\n    .case(\"onedrive\", () => 3)\n    .case(\"s3\", () => 8)\n    .case(\"joplin-server\", () => 9)\n    .case(\"joplin-cloud\", () => 10)\n    .default(() => 0)\n\nconst fetchWithTimeout = async (url: string, timeoutMs: number = 5_000): Promise<Response> => {\n  const controller = new AbortController()\n  const timer = setTimeout(() => controller.abort(), timeoutMs)\n  try {\n    return await fetch(url, { signal: controller.signal })\n  } finally {\n    clearTimeout(timer)\n  }\n}\n\ntype PortProbeResult = \"free\" | \"joplin_ours\" | \"joplin_foreign\" | \"occupied_other\" | \"occupied_unresponsive\"\n\nconst isConnectionRefused = (err: unknown): boolean => {\n  const e = err as { cause?: { code?: string; errors?: Array<{ code?: string }> } }\n  if (e?.cause?.code === \"ECONNREFUSED\") return true\n  if (e?.cause?.errors?.some((inner) => inner?.code === \"ECONNREFUSED\")) return true\n  return false\n}\n\nconst probePort = async (port: number, token: string): Promise<PortProbeResult> => {\n  try {\n    const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`, 3_000)\n    const body = await pingResponse.text()\n    if (body !== \"JoplinClipperServer\") return \"occupied_other\"\n\n    // It's a Joplin server — check if the token matches\n    try {\n      const authResponse = await fetchWithTimeout(\n        `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n        3_000,\n      )\n      if (authResponse.ok) return \"joplin_ours\"\n      return \"joplin_foreign\"\n    } catch {\n      return \"joplin_foreign\"\n    }\n  } catch (err: unknown) {\n    // ECONNREFUSED = nothing listening on the port = genuinely free\n    if (isConnectionRefused(err)) return \"free\"\n    // Timeout or other error = port is occupied but not responding to HTTP\n    return \"occupied_unresponsive\"\n  }\n}\n\ntype PortResolution =\n  | { outcome: \"reuse_existing\"; port: number; desktopDetected: boolean }\n  | { outcome: \"free\"; port: number; desktopDetected: boolean }\n  | { outcome: \"exhausted\" }\n\nconst resolveAvailablePort = async (startPort: number, token: string): Promise<PortResolution> => {\n  let desktopDetected = false\n  for (let port = startPort; port < startPort + MAX_PORT_ATTEMPTS; port++) {\n    const status = await probePort(port, token)\n    if (status === \"free\") return { outcome: \"free\", port, desktopDetected }\n    if (status === \"joplin_ours\") return { outcome: \"reuse_existing\", port, desktopDetected }\n    if (status === \"joplin_foreign\") {\n      desktopDetected = true\n      process.stderr.write(`[joplin-sidecar] Port ${port}: Joplin Desktop detected (different token), skipping\\n`)\n    } else {\n      process.stderr.write(`[joplin-sidecar] Port ${port} occupied (${status}), trying next...\\n`)\n    }\n  }\n  return { outcome: \"exhausted\" }\n}\n\nconst findJoplinCli = async (): Promise<Either<SidecarError, string>> => {\n  // 1. User override via env var\n  const envCli = process.env.JOPLIN_CLI\n  if (envCli) {\n    if (fs.existsSync(envCli)) return Right(envCli)\n    return Left(sidecarError(\"CLI_NOT_FOUND\", `JOPLIN_CLI path not found: ${envCli}`))\n  }\n\n  // 2. Bundled in node_modules (if joplin is a dependency)\n  const localBin = join(process.cwd(), \"node_modules\", \".bin\", isWindows ? \"joplin.cmd\" : \"joplin\")\n  if (fs.existsSync(localBin)) return Right(localBin)\n\n  // 3. Global install\n  try {\n    const { stdout } = await execAsync(`${whichCmd} joplin`, { encoding: \"utf-8\", timeout: 10_000 })\n    const joplinPath = stdout.trim().split(\"\\n\")[0]\n    return Right(joplinPath)\n  } catch {\n    // not found\n  }\n\n  // 4. npx fallback (auto-downloads on first run)\n  try {\n    const { stdout } = await execAsync(`${whichCmd} npx`, { encoding: \"utf-8\", timeout: 10_000 })\n    const npxPath = stdout.trim().split(\"\\n\")[0]\n    process.stderr.write(\"[joplin-sidecar] No local joplin found, using npx (may download on first run)\\n\")\n    return Right(npxPath)\n  } catch {\n    // not found\n  }\n\n  return Left(\n    sidecarError(\n      \"CLI_NOT_FOUND\",\n      \"Joplin CLI not found. Install with: npm install -g joplin, or set JOPLIN_CLI=/path/to/joplin\",\n    ),\n  )\n}\n\nconst buildSettingsRecord = (config: SidecarConfig): Record<string, string> => {\n  const settings: Record<string, string> = {\n    \"api.token\": config.apiToken,\n    \"api.port\": String(config.apiPort),\n  }\n\n  const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n  settings[\"sync.target\"] = String(syncTargetId(syncTarget))\n\n  if (syncTarget.type === \"filesystem\") {\n    settings[\"sync.2.path\"] = syncTarget.path\n  } else if (syncTarget.type === \"webdav\") {\n    settings[\"sync.6.path\"] = syncTarget.url\n    settings[\"sync.6.username\"] = syncTarget.username\n    settings[\"sync.6.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"nextcloud\") {\n    settings[\"sync.5.path\"] = syncTarget.url\n    settings[\"sync.5.username\"] = syncTarget.username\n    settings[\"sync.5.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"s3\") {\n    settings[\"sync.8.path\"] = syncTarget.bucket\n    settings[\"sync.8.region\"] = syncTarget.region\n    settings[\"sync.8.username\"] = syncTarget.accessKey\n    settings[\"sync.8.password\"] = syncTarget.secretKey\n  } else if (syncTarget.type === \"joplin-server\") {\n    settings[\"sync.9.path\"] = syncTarget.url\n    settings[\"sync.9.username\"] = syncTarget.email\n    settings[\"sync.9.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"joplin-cloud\") {\n    settings[\"sync.10.username\"] = syncTarget.email\n    settings[\"sync.10.password\"] = syncTarget.password\n  }\n\n  const interval = Option(config.syncInterval).orElse(300)\n  settings[\"sync.interval\"] = String(interval)\n\n  return settings\n}\n\nconst runJoplinConfig = async (cli: string, profileDir: string, key: string, value: string): Promise<void> => {\n  const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n  const args = cli.endsWith(\"npx\")\n    ? [\"joplin\", \"config\", \"--profile\", profileDir, key, value]\n    : [\"config\", \"--profile\", profileDir, key, value]\n  await execFileAsync(cmd, args, { encoding: \"utf-8\", timeout: 30_000, shell: isWindows })\n}\n\nconst configureJoplin = async (cli: string, config: SidecarConfig): Promise<Either<SidecarError, void>> => {\n  const settings = buildSettingsRecord(config)\n  try {\n    fs.mkdirSync(config.profileDir, { recursive: true })\n    for (const [key, value] of Object.entries(settings)) {\n      await runJoplinConfig(cli, config.profileDir, key, value)\n    }\n    return Right(undefined as void)\n  } catch (e) {\n    return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to configure Joplin via CLI\", e))\n  }\n}\n\nconst spawnServer = (cli: string, config: SidecarConfig): Either<SidecarError, ChildProcess> => {\n  try {\n    const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n    const args = cli.endsWith(\"npx\")\n      ? [\"joplin\", \"server\", \"start\", \"--profile\", config.profileDir]\n      : [\"server\", \"start\", \"--profile\", config.profileDir]\n\n    const proc = spawn(cmd, args, {\n      stdio: [\"ignore\", \"pipe\", \"pipe\"],\n      detached: false,\n      shell: isWindows,\n    })\n\n    proc.stderr?.on(\"data\", (data: Buffer) => {\n      process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n    })\n\n    proc.stdout?.on(\"data\", (data: Buffer) => {\n      process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n    })\n\n    proc.on(\"error\", (err) => {\n      process.stderr.write(`[joplin-sidecar] Process error: ${err.message}\\n`)\n    })\n\n    return Right(proc as ChildProcess)\n  } catch (e) {\n    return Left(sidecarError(\"SPAWN_FAILED\", \"Failed to spawn Joplin server process\", e))\n  }\n}\n\nconst waitForReady = async (\n  port: number,\n  token: string,\n  proc: ChildProcess | null,\n  maxRetries: number = 30,\n  intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n  const deadline = Date.now() + 60_000\n\n  // Track if the spawned process exits early (e.g., port already taken)\n  let procExitCode: number | null = null\n  if (proc) {\n    proc.once(\"exit\", (code) => {\n      procExitCode = code\n    })\n  }\n\n  for (let i = 0; i < maxRetries; i++) {\n    if (Date.now() > deadline) break\n\n    if (procExitCode !== null) {\n      return Left(\n        sidecarError(\n          \"SPAWN_FAILED\",\n          `Joplin server process exited unexpectedly (code ${procExitCode}). ` +\n            `Port ${port} may already be in use by another Joplin instance or process.`,\n        ),\n      )\n    }\n\n    try {\n      const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`)\n      if (pingResponse.ok) {\n        // Verify auth with token\n        try {\n          const authResponse = await fetchWithTimeout(\n            `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n          )\n          if (authResponse.ok) {\n            process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n            return Right(true as const)\n          }\n          process.stderr.write(\n            `[joplin-sidecar] Ping OK but auth failed (status ${authResponse.status}), retrying...\\n`,\n          )\n        } catch {\n          process.stderr.write(\"[joplin-sidecar] Ping OK but auth check timed out, retrying...\\n\")\n        }\n      }\n    } catch {\n      // Not ready yet\n    }\n    await new Promise((resolve) => setTimeout(resolve, intervalMs))\n  }\n\n  return Left(sidecarError(\"HEALTH_CHECK_FAILED\", \"Joplin server did not become ready within 60s\"))\n}\n\nconst CONFIG_CACHE_FILE = \".mcp-configured\"\n\nconst computeConfigHash = (config: SidecarConfig): string => {\n  const hashData = {\n    apiPort: config.apiPort,\n    apiToken: config.apiToken,\n    syncTarget: config.syncTarget,\n    syncInterval: config.syncInterval,\n  }\n  return crypto.createHash(\"sha256\").update(JSON.stringify(hashData)).digest(\"hex\").slice(0, 16)\n}\n\nconst isConfigCached = (profileDir: string, hash: string): boolean => {\n  try {\n    const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n    if (!fs.existsSync(cachePath)) return false\n    const cached = JSON.parse(fs.readFileSync(cachePath, \"utf-8\"))\n    return cached.hash === hash\n  } catch {\n    return false\n  }\n}\n\nconst writeConfigCache = (profileDir: string, hash: string): void => {\n  try {\n    const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n    fs.writeFileSync(cachePath, JSON.stringify({ hash, timestamp: Date.now() }))\n  } catch {\n    // Non-critical — skip silently\n  }\n}\n\nexport class JoplinSidecar {\n  private config: SidecarConfig\n  private childProcess: ChildProcess | null = null\n  private startPromise: Promise<Either<SidecarError, ChildProcess>> | null = null\n  private portResolution: PortResolution | null = null\n\n  constructor(config: Partial<SidecarConfig> & { apiToken: string }) {\n    this.config = {\n      profileDir: config.profileDir ?? join(os.homedir(), \".config\", \"joplin-mcp\"),\n      apiPort: config.apiPort ?? DEFAULT_API_PORT,\n      apiToken: config.apiToken,\n      syncTarget: config.syncTarget,\n      syncInterval: config.syncInterval,\n    }\n  }\n\n  async resolvePort(): Promise<Either<SidecarError, number>> {\n    const resolution = await resolveAvailablePort(this.config.apiPort, this.config.apiToken)\n    this.portResolution = resolution\n    if (resolution.outcome === \"exhausted\") {\n      const lastPort = this.config.apiPort + MAX_PORT_ATTEMPTS - 1\n      return Left(\n        sidecarError(\n          \"PORT_EXHAUSTED\",\n          `All ports ${this.config.apiPort}-${lastPort} are occupied. Free a port or stop other Joplin instances.`,\n        ),\n      )\n    }\n    this.config.apiPort = resolution.port\n    if (resolution.desktopDetected) {\n      process.stderr.write(\n        \"[joplin-sidecar] WARNING: Joplin Desktop is running. The sidecar uses a separate database.\\n\" +\n          \"[joplin-sidecar] Notes will sync between them only if both are configured with the same sync target.\\n\",\n      )\n    }\n    return Right(resolution.port)\n  }\n\n  isDesktopDetected(): boolean {\n    return this.portResolution?.outcome !== \"exhausted\" && (this.portResolution?.desktopDetected ?? false)\n  }\n\n  async start(): Promise<Either<SidecarError, ChildProcess>> {\n    if (this.startPromise) return this.startPromise\n    this.startPromise = this.doStart()\n    return this.startPromise\n  }\n\n  private async doStart(): Promise<Either<SidecarError, ChildProcess>> {\n    // Step 1: Find CLI\n    const cliResult = await findJoplinCli()\n    if (Either.isLeft(cliResult)) {\n      return Left(\n        cliResult.fold(\n          (e) => e,\n          () => null as never,\n        ),\n      )\n    }\n    const cli = cliResult.fold(\n      () => \"\",\n      (v) => v,\n    )\n    process.stderr.write(`[joplin-sidecar] Found CLI: ${cli}\\n`)\n\n    // Step 2: Resolve port if not already done (defensive fallback)\n    if (!this.portResolution) {\n      const portResult = await this.resolvePort()\n      if (Either.isLeft(portResult)) {\n        return Left(\n          portResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n    }\n\n    // Step 3: If we found an existing instance with our token, reuse it\n    if (this.portResolution?.outcome === \"reuse_existing\") {\n      process.stderr.write(\n        `[joplin-sidecar] Existing Joplin server with matching token on port ${this.config.apiPort}, reusing\\n`,\n      )\n      return Right(this.childProcess ?? (null as unknown as ChildProcess))\n    }\n\n    // Step 4: Configure via CLI (skip if config is cached)\n    const configHash = computeConfigHash(this.config)\n    if (isConfigCached(this.config.profileDir, configHash)) {\n      process.stderr.write(\"[joplin-sidecar] Configuration cached, skipping config step\\n\")\n    } else {\n      const configResult = await configureJoplin(cli, this.config)\n      if (Either.isLeft(configResult)) {\n        return Left(\n          configResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n      writeConfigCache(this.config.profileDir, configHash)\n      process.stderr.write(\"[joplin-sidecar] Configuration applied via CLI\\n\")\n    }\n\n    // Step 5: Spawn server (port is free, skip if already spawned from a previous attempt)\n    if (!this.childProcess) {\n      const spawnResult = spawnServer(cli, this.config)\n      if (Either.isLeft(spawnResult)) {\n        return Left(\n          spawnResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n      const proc = spawnResult.fold(\n        () => null as unknown as ChildProcess,\n        (v) => v,\n      )\n      this.childProcess = proc\n      process.stderr.write(`[joplin-sidecar] Server spawned (pid: ${proc.pid})\\n`)\n    } else {\n      process.stderr.write(\n        `[joplin-sidecar] Server already spawned (pid: ${this.childProcess.pid}), waiting for ready\\n`,\n      )\n    }\n\n    // Step 6: Wait for ready\n    const readyResult = await waitForReady(this.config.apiPort, this.config.apiToken, this.childProcess)\n    if (Either.isLeft(readyResult)) {\n      return Left(\n        readyResult.fold(\n          (e) => e,\n          () => null as never,\n        ),\n      )\n    }\n\n    return Right(this.childProcess!)\n  }\n\n  async stop(): Promise<Either<Error, true>> {\n    // Reset startPromise to allow retry via start() after stop()\n    this.startPromise = null\n\n    if (!this.childProcess) return Right(true as const)\n\n    const proc = this.childProcess\n    try {\n      proc.kill(\"SIGTERM\")\n\n      await new Promise<void>((resolve) => {\n        const timeout = setTimeout(() => {\n          proc.kill(\"SIGKILL\")\n          resolve()\n        }, 5000)\n\n        proc.on(\"exit\", () => {\n          clearTimeout(timeout)\n          resolve()\n        })\n      })\n\n      this.childProcess = null\n      process.stderr.write(\"[joplin-sidecar] Server stopped\\n\")\n      return Right(true as const)\n    } catch (error) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async healthCheck(): Promise<Either<Error, true>> {\n    try {\n      const response = await fetchWithTimeout(`http://127.0.0.1:${this.config.apiPort}/ping`, 5_000)\n      if (!response.ok) return Left(new Error(`Health check failed: ${response.status}`))\n      return Right(true as const)\n    } catch (error) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  getPort(): number {\n    return this.config.apiPort\n  }\n\n  getHost(): string {\n    return \"127.0.0.1\"\n  }\n}\n","import fs from \"fs\"\nimport { Either, Left, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { join, resolve } from \"path\"\n\nimport type { SyncTarget } from \"./joplin-sidecar.js\"\n\nexport type ParsedArgs = {\n  remainingArgs: string[]\n  transport: \"stdio\" | \"http\"\n  httpPort: number\n  profileDir: string\n  syncTarget: Option<SyncTarget>\n}\n\nconst expandVars = (p: string): string =>\n  p\n    .replace(/\\$\\{(\\w+)\\}/g, (_, name) => process.env[name] ?? \"\")\n    .replace(/\\$(\\w+)/g, (_, name) => process.env[name] ?? \"\")\n\nconst isWsl = (() => {\n  try {\n    return fs.readFileSync(\"/proc/version\", \"utf-8\").toLowerCase().includes(\"microsoft\")\n  } catch {\n    return false\n  }\n})()\n\nconst isNonEmptyDir = (p: string): boolean => {\n  try {\n    const entries = fs.readdirSync(p)\n    return entries.length > 0\n  } catch {\n    return false\n  }\n}\n\nconst resolveWslPath = (linuxPath: string, relativeToHome: string): string => {\n  if (!isWsl) return linuxPath\n  if (fs.existsSync(linuxPath) && isNonEmptyDir(linuxPath)) return linuxPath\n  try {\n    const usersDir = \"/mnt/c/Users\"\n    const users = fs\n      .readdirSync(usersDir)\n      .filter((u) => ![\"Public\", \"Default\", \"Default User\", \"All Users\"].includes(u))\n    for (const user of users) {\n      const winPath = join(usersDir, user, relativeToHome)\n      if (isNonEmptyDir(winPath)) {\n        process.stderr.write(`[wsl] Path ${linuxPath} empty/missing, using Windows path: ${winPath}\\n`)\n        return winPath\n      }\n    }\n  } catch {\n    // /mnt/c not accessible — fall through\n  }\n  return linuxPath\n}\n\nconst expandPath = (p: string): string => {\n  const expanded = expandVars(p)\n  if (expanded.startsWith(\"~/\") || expanded === \"~\") {\n    const linuxPath = expanded.replace(\"~\", os.homedir())\n    const relativeToHome = expanded.slice(2) // strip ~/\n    return resolveWslPath(linuxPath, relativeToHome)\n  }\n  return expanded\n}\n\nconst extractArg = (args: string[], flag: string): Option<string> => {\n  const index = args.indexOf(flag)\n  if (index === -1) return Option.none()\n  const value = args[index + 1]\n  if (!value || value.startsWith(\"--\")) return Option.none()\n  args.splice(index, 2)\n  return Option(value)\n}\n\nexport const buildSyncTarget = (args: {\n  syncTarget: Option<string>\n  syncPath: Option<string>\n  syncUsername: Option<string>\n  syncPassword: Option<string>\n}): Either<string, SyncTarget> => {\n  const targetType = args.syncTarget.orElse(\"none\")\n\n  switch (targetType) {\n    case \"none\":\n      return Right({ type: \"none\" } as SyncTarget)\n\n    case \"filesystem\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for filesystem sync target\"),\n        (path) => Right({ type: \"filesystem\", path } as SyncTarget),\n      )\n\n    case \"webdav\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for webdav sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for webdav sync target\"),\n            (username) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for webdav sync target\"),\n                (password) => Right({ type: \"webdav\", url, username, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"nextcloud\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for nextcloud sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for nextcloud sync target\"),\n            (username) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for nextcloud sync target\"),\n                (password) => Right({ type: \"nextcloud\", url, username, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"joplin-cloud\":\n      return args.syncUsername.fold(\n        () => Left(\"--sync-username required for joplin-cloud sync target\"),\n        (email) =>\n          args.syncPassword.fold(\n            () => Left(\"--sync-password required for joplin-cloud sync target\"),\n            (password) => Right({ type: \"joplin-cloud\", email, password } as SyncTarget),\n          ),\n      )\n\n    case \"joplin-server\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for joplin-server sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for joplin-server sync target\"),\n            (email) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for joplin-server sync target\"),\n                (password) => Right({ type: \"joplin-server\", url, email, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"s3\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path (bucket) required for s3 sync target\"),\n        (bucket) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username (access key) required for s3 sync target\"),\n            (accessKey) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password (secret key) required for s3 sync target\"),\n                (secretKey) => Right({ type: \"s3\", bucket, region: \"us-east-1\", accessKey, secretKey } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"dropbox\":\n      return Right({ type: \"dropbox\" } as SyncTarget)\n\n    case \"onedrive\":\n      return Right({ type: \"onedrive\" } as SyncTarget)\n\n    default:\n      return Left(\n        `Unknown sync target: ${targetType}. Valid targets: none, filesystem, webdav, nextcloud, joplin-cloud, joplin-server, s3, dropbox, onedrive`,\n      )\n  }\n}\n\nfunction parseArgs(): ParsedArgs {\n  const args = process.argv.slice(2)\n  let transport: \"stdio\" | \"http\" = \"stdio\"\n  let httpPort = 3000\n\n  // Load environment variables without dotenv debug output (for MCP stdio compatibility)\n  const loadEnvFile = (envPath: string) => {\n    try {\n      if (fs.existsSync(envPath)) {\n        process.stderr.write(`Loading environment from: ${envPath}\\n`)\n        const envContent = fs.readFileSync(envPath, \"utf-8\")\n        const envLines = envContent.split(\"\\n\")\n        const loadedVars: string[] = []\n\n        for (const line of envLines) {\n          const trimmedLine = line.trim()\n          if (trimmedLine && !trimmedLine.startsWith(\"#\")) {\n            const [key, ...valueParts] = trimmedLine.split(\"=\")\n            if (key && valueParts.length > 0) {\n              const value = valueParts.join(\"=\").replace(/^[\"']|[\"']$/g, \"\")\n              if (!process.env[key.trim()]) {\n                process.env[key.trim()] = value\n                loadedVars.push(key.trim())\n              }\n            }\n          }\n        }\n\n        if (loadedVars.length > 0) {\n          process.stderr.write(`Loaded variables: ${loadedVars.join(\", \")}\\n`)\n        }\n      }\n    } catch (error: unknown) {\n      process.stderr.write(`Error loading environment file: ${error}\\n`)\n    }\n  }\n\n  // Handle --env-file\n  const envFile = extractArg(args, \"--env-file\")\n  envFile.fold(\n    () => loadEnvFile(\".env\"),\n    (file) => loadEnvFile(resolve(process.cwd(), file)),\n  )\n\n  // Handle --token\n  extractArg(args, \"--token\").fold(\n    () => {},\n    (token) => {\n      process.env.JOPLIN_TOKEN = token\n    },\n  )\n\n  // Handle --transport\n  extractArg(args, \"--transport\").fold(\n    () => {},\n    (value) => {\n      if (value !== \"stdio\" && value !== \"http\") {\n        process.stderr.write(\"Error: --transport must be either 'stdio' or 'http'\\n\")\n        process.exit(1)\n      }\n      transport = value as \"stdio\" | \"http\"\n    },\n  )\n\n  // Handle --http-port\n  extractArg(args, \"--http-port\").fold(\n    () => {},\n    (value) => {\n      const parsed = parseInt(value, 10)\n      if (isNaN(parsed) || parsed < 1 || parsed > 65535) {\n        process.stderr.write(\"Error: --http-port must be a valid port number (1-65535)\\n\")\n        process.exit(1)\n      }\n      httpPort = parsed\n    },\n  )\n\n  // Handle --profile\n  const profileDir = extractArg(args, \"--profile\")\n    .or(Option(process.env.JOPLIN_PROFILE))\n    .map(expandPath)\n    .orElse(`${os.homedir()}/.config/joplin-mcp`)\n\n  // Handle sync args\n  const syncTarget = extractArg(args, \"--sync-target\").or(Option(process.env.JOPLIN_SYNC_TARGET))\n  const syncPath = extractArg(args, \"--sync-path\").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath)\n  const syncUsername = extractArg(args, \"--sync-username\").or(Option(process.env.JOPLIN_SYNC_USERNAME))\n  const syncPassword = extractArg(args, \"--sync-password\").or(Option(process.env.JOPLIN_SYNC_PASSWORD))\n\n  // Build and validate sync target\n  const syncResult = buildSyncTarget({ syncTarget, syncPath, syncUsername, syncPassword })\n  if (Either.isLeft(syncResult)) {\n    const err = syncResult.fold(\n      (e) => e,\n      () => \"\",\n    )\n    process.stderr.write(`Error: ${err}\\n`)\n    process.exit(1)\n  }\n  const syncTargetValue = syncResult.fold(\n    () => ({ type: \"none\" }) as SyncTarget,\n    (v) => v,\n  )\n  const resolvedSyncTarget: Option<SyncTarget> =\n    syncTargetValue.type === \"none\" ? Option.none<SyncTarget>() : Option(syncTargetValue as SyncTarget)\n\n  // Handle --help\n  if (args.includes(\"--help\") || args.includes(\"-h\")) {\n    process.stderr.write(`\nJoplin MCP Server (Sidecar Mode)\n\nUSAGE:\n  joplin-mcp-server [OPTIONS]\n\nOPTIONS:\n  --env-file <file>          Load environment variables from file\n  --token <token>            Joplin API token\n  --transport <type>         Transport type: stdio (default) or http\n  --http-port <port>         HTTP server port (default: 3000, only with --transport http)\n  --profile <dir>            Joplin data directory (default: ~/.config/joplin-mcp)\n  --sync-target <type>       Sync target: none, filesystem, webdav, nextcloud,\n                             joplin-cloud, joplin-server, s3, dropbox, onedrive\n  --sync-path <url>          URL or path for sync target\n  --sync-username <user>     Username/email for sync\n  --sync-password <pass>     Password for sync\n  --help, -h                 Show this help message\n\nENVIRONMENT VARIABLES:\n  JOPLIN_TOKEN               Joplin API token (required)\n  JOPLIN_HOST                Connect to existing Joplin at this host (skips sidecar)\n  JOPLIN_PORT                Connect to existing Joplin on this port (skips sidecar)\n  JOPLIN_CLI                 Path to joplin CLI binary (overrides auto-detection)\n  JOPLIN_PROFILE             Joplin data directory\n  JOPLIN_SYNC_TARGET         Sync target type\n  JOPLIN_SYNC_PATH           Sync target URL/path\n  JOPLIN_SYNC_USERNAME       Sync username/email\n  JOPLIN_SYNC_PASSWORD       Sync password\n  LOG_LEVEL                  Log level: debug, info, warn, error (default: info)\n\nMODES:\n  Sidecar (default):\n    Spawns and manages its own Joplin Terminal process.\n    No Joplin desktop app or Web Clipper needed.\n    Uses an isolated profile at --profile path (default: ~/.config/joplin-mcp).\n\n  External (JOPLIN_HOST/JOPLIN_PORT set):\n    Connects directly to an existing Joplin instance.\n    Useful for WSL connecting to Windows Joplin desktop.\n\nEXAMPLES:\n  # Minimal - local notes, no sync\n  joplin-mcp-server --token my_token\n\n  # Joplin Cloud sync\n  joplin-mcp-server --token my_token \\\\\n    --sync-target joplin-cloud \\\\\n    --sync-username user@example.com --sync-password pass\n\n  # WebDAV sync\n  joplin-mcp-server --token my_token \\\\\n    --sync-target webdav \\\\\n    --sync-path https://dav.example.com/joplin \\\\\n    --sync-username user --sync-password pass\n\n  # Filesystem sync (Syncthing, NAS)\n  joplin-mcp-server --token my_token \\\\\n    --sync-target filesystem --sync-path /mnt/sync/joplin\n\n  # HTTP transport for web apps\n  joplin-mcp-server --token my_token --transport http --http-port 3000\n\n  # External mode - connect to existing Joplin (e.g. Windows desktop from WSL)\n  JOPLIN_HOST=172.x.x.x JOPLIN_PORT=41184 joplin-mcp-server --token my_token\n\nFind your Joplin token in: Tools > Options > Web Clipper\n`)\n    process.exit(0)\n  }\n\n  return {\n    remainingArgs: args,\n    transport,\n    httpPort,\n    profileDir,\n    syncTarget: resolvedSyncTarget,\n  }\n}\n\nexport default parseArgs\n","import axios, { type AxiosResponse } from \"axios\"\nimport { Either, Left, Right } from \"functype\"\n\ntype JoplinAPIClientConfig = {\n  host?: string\n  port?: number\n  token: string\n}\n\ntype JoplinAPIResponse<T = unknown> = {\n  items: T[]\n  has_more: boolean\n}\n\ntype RequestOptions = {\n  query?: Record<string, unknown>\n  [key: string]: unknown\n}\n\nclass JoplinAPIClient {\n  private readonly baseURL: string\n  private readonly token: string\n\n  constructor({ host = \"127.0.0.1\", port = 41184, token }: JoplinAPIClientConfig) {\n    this.baseURL = `http://${host}:${port}`\n    this.token = token\n  }\n\n  async serviceAvailable(): Promise<Either<Error, true>> {\n    try {\n      const response: AxiosResponse<string> = await axios.get(`${this.baseURL}/ping`, { timeout: 5_000 })\n      if (response.status === 200 && response.data === \"JoplinClipperServer\") {\n        return Right(true as const)\n      }\n      return Left(new Error(\"Unexpected response from Joplin ping\"))\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async getAllItems<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T[]>> {\n    let page = 1\n    const items: T[] = []\n\n    try {\n      while (true) {\n        const result = await this.get<JoplinAPIResponse<T>>(\n          path,\n          this.mergeRequestOptions(options, { query: { page } }),\n        )\n\n        const response = result.fold(\n          (err) => {\n            throw err\n          },\n          (data) => data,\n        )\n\n        if (!response || typeof response !== \"object\" || !Array.isArray(response.items)) {\n          return Left(new Error(`Unexpected response format from Joplin API for path: ${path}`))\n        }\n\n        items.push(...response.items)\n        page += 1\n\n        if (!response.has_more) break\n      }\n\n      return Right(items)\n    } catch (error: unknown) {\n      process.stderr.write(`Error in getAllItems for path ${path}: ${error}\\n`)\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.get(`${this.baseURL}${path}`, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async post<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.post(`${this.baseURL}${path}`, body, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async delete<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.delete(`${this.baseURL}${path}`, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async put<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.put(`${this.baseURL}${path}`, body, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  private requestOptions(options: RequestOptions = {}): RequestOptions {\n    return this.mergeRequestOptions(\n      {\n        query: { token: this.token },\n      },\n      options,\n    )\n  }\n\n  private mergeRequestOptions(options1: RequestOptions, options2: RequestOptions): RequestOptions {\n    return {\n      query: {\n        ...(options1.query || {}),\n        ...(options2.query || {}),\n      },\n      ...this.except(options1, \"query\"),\n      ...this.except(options2, \"query\"),\n    }\n  }\n\n  private except(obj: Record<string, unknown>, key: string): Record<string, unknown> {\n    const result = { ...obj }\n    delete result[key]\n    return result\n  }\n}\n\nexport default JoplinAPIClient\nexport type { JoplinAPIClientConfig, JoplinAPIResponse, RequestOptions }\n","import { type Either } from \"functype\"\n\nimport JoplinAPIClient from \"../joplin-api-client.js\"\n\ntype JoplinFolder = {\n  id: string\n  title: string\n  parent_id?: string\n}\n\ntype JoplinNote = {\n  id: string\n  title: string\n  body?: string\n  parent_id?: string\n  created_time: number\n  updated_time: number\n  is_todo: boolean\n  todo_completed?: boolean\n  todo_due?: number\n}\n\nabstract class BaseTool {\n  protected apiClient: JoplinAPIClient\n\n  constructor(apiClient: JoplinAPIClient) {\n    this.apiClient = apiClient\n  }\n\n  abstract call(...args: any[]): Promise<string>\n\n  protected formatError(error: any, context: string): string {\n    process.stderr.write(`${context} error: ${error}\\n`)\n    return `Error ${context.toLowerCase()}: ${error.message || \"Unknown error\"}`\n  }\n\n  protected validateId(id: string, type: \"note\" | \"notebook\"): string | null {\n    if (!id) {\n      return `Please provide a ${type} ID. Example: ${type === \"note\" ? \"read_note\" : \"read_notebook\"} ${type}_id=\"your-${type}-id\"`\n    }\n\n    if (id.length < 10 || !id.match(/[a-f0-9]/i)) {\n      const searchHint = type === \"note\" ? \"search_notes\" : \"list_notebooks\"\n      return `Error: \"${id}\" does not appear to be a valid ${type} ID. \\n\\n${type.charAt(0).toUpperCase() + type.slice(1)} IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse ${searchHint} to ${type === \"note\" ? \"find notes\" : \"see all available notebooks\"} and their IDs.`\n    }\n\n    return null\n  }\n\n  protected unwrap<T>(result: Either<Error, T>): T {\n    return result.fold(\n      (err) => {\n        throw err\n      },\n      (val) => val,\n    )\n  }\n\n  protected formatDate(timestamp: number): string {\n    return new Date(timestamp).toLocaleString()\n  }\n}\n\nexport default BaseTool\nexport type { JoplinFolder, JoplinNote }\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface CreateFolderOptions {\n  title: string\n  parent_id?: string | undefined\n}\n\ninterface CreateFolderResponse extends JoplinFolder {\n  created_time: number\n  updated_time: number\n}\n\nclass CreateFolder extends BaseTool {\n  async call(options: CreateFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder creation options. Example: create_folder {\"title\": \"My Notebook\"}'\n    }\n\n    // Validate required title\n    if (!options.title || typeof options.title !== \"string\" || options.title.trim() === \"\") {\n      return 'Please provide a title for the folder/notebook. Example: create_folder {\"title\": \"My Notebook\"}'\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n      return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n    }\n\n    try {\n      // Prepare the request body\n      const requestBody: CreateFolderOptions = {\n        title: options.title.trim(),\n      }\n\n      if (options.parent_id) {\n        requestBody.parent_id = options.parent_id\n      }\n\n      // Create the folder\n      const createdFolder = this.unwrap(await this.apiClient.post<CreateFolderResponse>(\"/folders\", requestBody))\n\n      // Validate response\n      if (!createdFolder || typeof createdFolder !== \"object\" || !createdFolder.id) {\n        return \"Error: Unexpected response format from Joplin API when creating folder\"\n      }\n\n      // Get parent notebook info if available\n      let parentInfo = \"Top level\"\n      if (createdFolder.parent_id) {\n        try {\n          const parentNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${createdFolder.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (parentNotebook && parentNotebook.title) {\n            parentInfo = `Inside \"${parentNotebook.title}\" (notebook_id: \"${createdFolder.parent_id}\")`\n          }\n        } catch {\n          // Continue even if we can't get parent info\n          parentInfo = `Parent notebook ID: ${createdFolder.parent_id}`\n        }\n      }\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully created notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Notebook Details:`)\n      resultLines.push(`   Title: \"${createdFolder.title}\"`)\n      resultLines.push(`   Notebook ID: ${createdFolder.id}`)\n      resultLines.push(`   Location: ${parentInfo}`)\n\n      const createdDate = this.formatDate(createdFolder.created_time)\n      resultLines.push(`   Created: ${createdDate}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - View notebook: read_notebook notebook_id=\"${createdFolder.id}\"`)\n      resultLines.push(`   - Create a note in it: create_note {\"title\": \"My Note\", \"parent_id\": \"${createdFolder.id}\"}`)\n      resultLines.push(`   - View all notebooks: list_notebooks`)\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        // Handle specific API errors\n        if (error.response.status === 400) {\n          return `Error creating notebook: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Parent notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n        }\n        if (error.response.status === 409) {\n          return `Error: A notebook with the title \"${options.title}\" might already exist in this location.\\n\\nTry a different title or check existing notebooks with list_notebooks.`\n        }\n      }\n      return this.formatError(error, \"creating notebook\")\n    }\n  }\n}\n\nexport default CreateFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface CreateNoteOptions {\n  title?: string | undefined\n  body?: string | undefined\n  body_html?: string | undefined\n  parent_id?: string | undefined\n  is_todo?: boolean | undefined\n  image_data_url?: string | undefined\n}\n\ntype CreateNoteResponse = JoplinNote\n\nclass CreateNote extends BaseTool {\n  async call(options: CreateNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note creation options. Example: create_note {\"title\": \"My Note\", \"body\": \"Note content\"}'\n    }\n\n    // Validate that we have at least a title or body\n    if (!options.title && !options.body && !options.body_html) {\n      return \"Please provide at least a title, body, or body_html for the note.\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n      return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n    }\n\n    try {\n      // Prepare the request body\n      const requestBody: CreateNoteOptions = {}\n\n      if (options.title) requestBody.title = options.title\n      if (options.body) requestBody.body = options.body\n      if (options.body_html) requestBody.body_html = options.body_html\n      if (options.parent_id) requestBody.parent_id = options.parent_id\n      if (options.is_todo !== undefined) requestBody.is_todo = options.is_todo\n      if (options.image_data_url) requestBody.image_data_url = options.image_data_url\n\n      // Create the note\n      const createdNote = this.unwrap(await this.apiClient.post<CreateNoteResponse>(\"/notes\", requestBody))\n\n      // Validate response\n      if (!createdNote || typeof createdNote !== \"object\" || !createdNote.id) {\n        return \"Error: Unexpected response format from Joplin API when creating note\"\n      }\n\n      // Get notebook info if available\n      let notebookInfo = \"Root level\"\n      if (createdNote.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${createdNote.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (notebook && notebook.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${createdNote.parent_id}\")`\n          }\n        } catch {\n          // Continue even if we can't get notebook info\n          notebookInfo = `Notebook ID: ${createdNote.parent_id}`\n        }\n      }\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully created note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Note Details:`)\n      resultLines.push(`   Title: \"${createdNote.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${createdNote.id}`)\n      resultLines.push(`   Location: ${notebookInfo}`)\n\n      if (createdNote.is_todo) {\n        resultLines.push(`   Type: Todo item`)\n      }\n\n      const createdDate = this.formatDate(createdNote.created_time)\n      resultLines.push(`   Created: ${createdDate}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - Read the note: read_note note_id=\"${createdNote.id}\"`)\n      if (createdNote.parent_id) {\n        resultLines.push(`   - View notebook: read_notebook notebook_id=\"${createdNote.parent_id}\"`)\n      }\n      resultLines.push(`   - Search for it: search_notes query=\"${createdNote.title}\"`)\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        // Handle specific API errors\n        if (error.response.status === 400) {\n          return `Error creating note: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n        }\n      }\n      return this.formatError(error, \"creating note\")\n    }\n  }\n}\n\nexport default CreateNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface DeleteFolderOptions {\n  folder_id: string\n  confirm?: boolean | undefined\n  force?: boolean | undefined\n}\n\ninterface FolderContents {\n  items: any[]\n}\n\nclass DeleteFolder extends BaseTool {\n  async call(options: DeleteFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    // Validate required folder_id\n    if (!options.folder_id) {\n      return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    const folderIdError = this.validateId(options.folder_id, \"notebook\")\n    if (folderIdError) {\n      return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n    }\n\n    // Require explicit confirmation for safety\n    if (!options.confirm) {\n      return `⚠️  This will permanently delete the notebook/folder!\\n\\nTo confirm deletion, use:\\ndelete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true}\\n\\n⚠️  This action cannot be undone!`\n    }\n\n    try {\n      // First, get the folder details to show what's being deleted\n      const folderToDelete = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      if (!folderToDelete || !folderToDelete.id) {\n        return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n      }\n\n      // Check if folder contains notes or subfolders\n      const [notes, subfolders] = await Promise.all([\n        this.apiClient\n          .get<FolderContents>(`/folders/${options.folder_id}/notes`, {\n            query: { fields: \"id,title\" },\n          })\n          .then((result) =>\n            result.fold(\n              () => ({ items: [] }),\n              (data) => data,\n            ),\n          ),\n        this.apiClient\n          .get<FolderContents>(\"/folders\", {\n            query: { fields: \"id,title,parent_id\" },\n          })\n          .then((result) =>\n            result.fold(\n              () => ({ items: [] }),\n              (response) => ({\n                items: response.items?.filter((folder: any) => folder.parent_id === options.folder_id) || [],\n              }),\n            ),\n          ),\n      ])\n\n      const noteCount = notes.items?.length || 0\n      const subfolderCount = subfolders.items?.length || 0\n      const totalContent = noteCount + subfolderCount\n\n      // Warn if folder is not empty and force is not specified\n      if (totalContent > 0 && !options.force) {\n        const resultLines: string[] = []\n        resultLines.push(`⚠️  Cannot delete non-empty notebook!`)\n        resultLines.push(\"\")\n        resultLines.push(`📁 Notebook: \"${folderToDelete.title}\"`)\n        resultLines.push(`   Contains: ${noteCount} notes and ${subfolderCount} subfolders`)\n\n        if (noteCount > 0) {\n          resultLines.push(\"\")\n          resultLines.push(`📝 Contains ${noteCount} notes:`)\n          notes.items.slice(0, 5).forEach((note: any) => {\n            resultLines.push(`   - ${note.title || \"Untitled\"}`)\n          })\n          if (noteCount > 5) {\n            resultLines.push(`   ... and ${noteCount - 5} more notes`)\n          }\n        }\n\n        if (subfolderCount > 0) {\n          resultLines.push(\"\")\n          resultLines.push(`📁 Contains ${subfolderCount} subfolders:`)\n          subfolders.items.slice(0, 5).forEach((folder: any) => {\n            resultLines.push(`   - ${folder.title}`)\n          })\n          if (subfolderCount > 5) {\n            resultLines.push(`   ... and ${subfolderCount - 5} more folders`)\n          }\n        }\n\n        resultLines.push(\"\")\n        resultLines.push(`💡 Options:`)\n        resultLines.push(`   1. Move or delete the contents first, then delete the folder`)\n        resultLines.push(`   2. Force delete (⚠️  DESTROYS ALL CONTENT):`)\n        resultLines.push(`      delete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true, \"force\": true}`)\n        resultLines.push(\"\")\n        resultLines.push(`⚠️  Force delete will permanently delete ALL ${totalContent} items inside!`)\n\n        return resultLines.join(\"\\n\")\n      }\n\n      // Get parent folder info if available\n      let parentInfo = \"Top level\"\n      if (folderToDelete.parent_id) {\n        try {\n          const parentFolder = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${folderToDelete.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (parentFolder?.title) {\n            parentInfo = `Inside \"${parentFolder.title}\" (notebook_id: \"${folderToDelete.parent_id}\")`\n          }\n        } catch {\n          parentInfo = `Parent ID: ${folderToDelete.parent_id}`\n        }\n      }\n\n      // Delete the folder\n      this.unwrap(await this.apiClient.delete(`/folders/${options.folder_id}`))\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`🗑️  Successfully deleted notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Deleted Notebook Details:`)\n      resultLines.push(`   Title: \"${folderToDelete.title}\"`)\n      resultLines.push(`   Folder ID: ${folderToDelete.id}`)\n      resultLines.push(`   Location: ${parentInfo}`)\n\n      if (totalContent > 0) {\n        resultLines.push(`   Deleted Content: ${noteCount} notes and ${subfolderCount} subfolders`)\n        resultLines.push(\"\")\n        resultLines.push(`⚠️  All ${totalContent} items inside have been permanently deleted!`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`⚠️  This notebook has been permanently deleted and cannot be recovered.`)\n\n      if (folderToDelete.parent_id) {\n        resultLines.push(\"\")\n        resultLines.push(`🔗 Related actions:`)\n        resultLines.push(`   - View parent notebook: read_notebook notebook_id=\"${folderToDelete.parent_id}\"`)\n        resultLines.push(`   - View all notebooks: list_notebooks`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n        }\n        if (error.response.status === 403) {\n          return `Permission denied: Cannot delete folder with ID \"${options.folder_id}\".\\n\\nThis might be a protected system folder.`\n        }\n        if (error.response.status === 409) {\n          return `Cannot delete folder: It may contain items that prevent deletion.\\n\\nTry moving or deleting the contents first, or use force option.`\n        }\n      }\n      return this.formatError(error, \"deleting folder\")\n    }\n  }\n}\n\nexport default DeleteFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface DeleteNoteOptions {\n  note_id: string\n  confirm?: boolean | undefined\n}\n\nclass DeleteNote extends BaseTool {\n  async call(options: DeleteNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    // Validate required note_id\n    if (!options.note_id) {\n      return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    const noteIdError = this.validateId(options.note_id, \"note\")\n    if (noteIdError) {\n      return noteIdError\n    }\n\n    // Require explicit confirmation for safety\n    if (!options.confirm) {\n      return `⚠️  This will permanently delete the note!\\n\\nTo confirm deletion, use:\\ndelete_note {\"note_id\": \"${options.note_id}\", \"confirm\": true}\\n\\n⚠️  This action cannot be undone!`\n    }\n\n    try {\n      // First, get the note details to show what's being deleted\n      const noteToDelete = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n          query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,created_time,updated_time\" },\n        }),\n      )\n\n      if (!noteToDelete || !noteToDelete.id) {\n        return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n      }\n\n      // Get notebook info if available\n      let notebookInfo = \"Root level\"\n      if (noteToDelete.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${noteToDelete.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (notebook?.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${noteToDelete.parent_id}\")`\n          }\n        } catch {\n          notebookInfo = `Notebook ID: ${noteToDelete.parent_id}`\n        }\n      }\n\n      // Delete the note\n      this.unwrap(await this.apiClient.delete(`/notes/${options.note_id}`))\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`🗑️  Successfully deleted note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Deleted Note Details:`)\n      resultLines.push(`   Title: \"${noteToDelete.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${noteToDelete.id}`)\n      resultLines.push(`   Location: ${notebookInfo}`)\n\n      if (noteToDelete.is_todo) {\n        const status = noteToDelete.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`   Type: Todo (${status})`)\n      } else {\n        resultLines.push(`   Type: Regular note`)\n      }\n\n      const createdDate = this.formatDate(noteToDelete.created_time)\n      const updatedDate = this.formatDate(noteToDelete.updated_time)\n      resultLines.push(`   Created: ${createdDate}`)\n      resultLines.push(`   Last Updated: ${updatedDate}`)\n\n      // Show content preview if available\n      if (noteToDelete.body) {\n        const preview = noteToDelete.body.substring(0, 100).replace(/\\n/g, \" \")\n        const truncated = noteToDelete.body.length > 100 ? \"...\" : \"\"\n        resultLines.push(`   Content Preview: ${preview}${truncated}`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`⚠️  This note has been permanently deleted and cannot be recovered.`)\n\n      if (noteToDelete.parent_id) {\n        resultLines.push(\"\")\n        resultLines.push(`🔗 Related actions:`)\n        resultLines.push(`   - View containing notebook: read_notebook notebook_id=\"${noteToDelete.parent_id}\"`)\n        resultLines.push(`   - Search for similar notes: search_notes query=\"${noteToDelete.title}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n        }\n        if (error.response.status === 403) {\n          return `Permission denied: Cannot delete note with ID \"${options.note_id}\".\\n\\nThis might be a protected system note.`\n        }\n      }\n      return this.formatError(error, \"deleting note\")\n    }\n  }\n}\n\nexport default DeleteNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface EditFolderOptions {\n  folder_id: string\n  title?: string | undefined\n  parent_id?: string | undefined\n}\n\ninterface EditFolderResponse extends JoplinFolder {\n  updated_time: number\n}\n\nclass EditFolder extends BaseTool {\n  async call(options: EditFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n    }\n\n    // Validate required folder_id\n    if (!options.folder_id) {\n      return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n    }\n\n    const folderIdError = this.validateId(options.folder_id, \"notebook\")\n    if (folderIdError) {\n      return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n    }\n\n    // Validate that we have at least one field to update\n    const updateFields = [\"title\", \"parent_id\"]\n    const hasUpdate = updateFields.some((field) => options[field as keyof EditFolderOptions] !== undefined)\n\n    if (!hasUpdate) {\n      return \"Please provide at least one field to update. Available fields: title, parent_id\"\n    }\n\n    // Validate title if provided\n    if (options.title !== undefined && (typeof options.title !== \"string\" || options.title.trim() === \"\")) {\n      return \"Title must be a non-empty string.\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n      if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n        return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n      }\n\n      // Prevent self-parenting\n      if (options.parent_id === options.folder_id) {\n        return \"Error: A folder cannot be its own parent.\"\n      }\n    }\n\n    try {\n      // First, get the current folder to show before/after comparison\n      const currentFolder = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      if (!currentFolder || !currentFolder.id) {\n        return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n      }\n\n      // Prepare the update body - only include fields that are being updated\n      const updateBody: Partial<EditFolderOptions> = {}\n\n      if (options.title !== undefined) updateBody.title = options.title.trim()\n      if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n\n      // Update the folder\n      const updatedFolder = this.unwrap(\n        await this.apiClient.put<EditFolderResponse>(`/folders/${options.folder_id}`, updateBody),\n      )\n\n      // Validate response\n      if (!updatedFolder || typeof updatedFolder !== \"object\" || !updatedFolder.id) {\n        return \"Error: Unexpected response format from Joplin API when updating folder\"\n      }\n\n      // Get parent folder info for both old and new locations if parent_id changed\n      let oldParentInfo = \"Top level\"\n      let newParentInfo = \"Top level\"\n\n      if (currentFolder.parent_id) {\n        try {\n          const oldParent = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${currentFolder.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (oldParent?.title) {\n            oldParentInfo = `Inside \"${oldParent.title}\"`\n          }\n        } catch {\n          oldParentInfo = `Parent ID: ${currentFolder.parent_id}`\n        }\n      }\n\n      if (updatedFolder.parent_id && updatedFolder.parent_id !== currentFolder.parent_id) {\n        try {\n          const newParent = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${updatedFolder.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (newParent?.title) {\n            newParentInfo = `Inside \"${newParent.title}\"`\n          }\n        } catch {\n          newParentInfo = `Parent ID: ${updatedFolder.parent_id}`\n        }\n      } else if (updatedFolder.parent_id) {\n        newParentInfo = oldParentInfo\n      }\n\n      // Format success response with before/after comparison\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully updated notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Notebook: \"${updatedFolder.title}\"`)\n      resultLines.push(`   Folder ID: ${updatedFolder.id}`)\n      resultLines.push(\"\")\n\n      // Show what changed\n      resultLines.push(`🔄 Changes made:`)\n\n      if (options.title !== undefined && currentFolder.title !== updatedFolder.title) {\n        resultLines.push(`   Title: \"${currentFolder.title}\" → \"${updatedFolder.title}\"`)\n      }\n\n      if (options.parent_id !== undefined && currentFolder.parent_id !== updatedFolder.parent_id) {\n        resultLines.push(`   Location: ${oldParentInfo} → ${newParentInfo}`)\n      }\n\n      if (updatedFolder.updated_time) {\n        const updatedTime = this.formatDate(updatedFolder.updated_time)\n        resultLines.push(`   Last Updated: ${updatedTime}`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - View notebook: read_notebook notebook_id=\"${updatedFolder.id}\"`)\n      resultLines.push(`   - View all notebooks: list_notebooks`)\n      if (updatedFolder.parent_id) {\n        resultLines.push(`   - View parent notebook: read_notebook notebook_id=\"${updatedFolder.parent_id}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          if (error.config?.url?.includes(`/folders/${options.folder_id}`)) {\n            return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n          }\n          if (options.parent_id) {\n            return `Error: Parent folder with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n          }\n        }\n        if (error.response.status === 400) {\n          return `Error updating folder: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 409) {\n          return `Error: A folder with the title \"${options.title}\" might already exist in this location.\\n\\nTry a different title or check existing folders with list_notebooks.`\n        }\n      }\n      return this.formatError(error, \"updating folder\")\n    }\n  }\n}\n\nexport default EditFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface EditNoteOptions {\n  note_id: string\n  title?: string | undefined\n  body?: string | undefined\n  body_html?: string | undefined\n  parent_id?: string | undefined\n  is_todo?: boolean | undefined\n  todo_completed?: boolean | undefined\n  todo_due?: number | undefined\n}\n\ntype EditNoteResponse = JoplinNote\n\nclass EditNote extends BaseTool {\n  async call(options: EditNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n    }\n\n    // Validate required note_id\n    if (!options.note_id) {\n      return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n    }\n\n    const noteIdError = this.validateId(options.note_id, \"note\")\n    if (noteIdError) {\n      return noteIdError\n    }\n\n    // Validate that we have at least one field to update\n    const updateFields = [\"title\", \"body\", \"body_html\", \"parent_id\", \"is_todo\", \"todo_completed\", \"todo_due\"]\n    const hasUpdate = updateFields.some((field) => options[field as keyof EditNoteOptions] !== undefined)\n\n    if (!hasUpdate) {\n      return \"Please provide at least one field to update. Available fields: title, body, body_html, parent_id, is_todo, todo_completed, todo_due\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n      if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n        return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n      }\n    }\n\n    try {\n      // First, get the current note to show before/after comparison\n      const currentNote = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n          query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,todo_due,updated_time\" },\n        }),\n      )\n\n      if (!currentNote || !currentNote.id) {\n        return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n      }\n\n      // Prepare the update body - only include fields that are being updated\n      const updateBody: Partial<EditNoteOptions> = {}\n\n      if (options.title !== undefined) updateBody.title = options.title\n      if (options.body !== undefined) updateBody.body = options.body\n      if (options.body_html !== undefined) updateBody.body_html = options.body_html\n      if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n      if (options.is_todo !== undefined) updateBody.is_todo = options.is_todo\n      if (options.todo_completed !== undefined) updateBody.todo_completed = options.todo_completed\n      if (options.todo_due !== undefined) updateBody.todo_due = options.todo_due\n\n      // Update the note\n      const updatedNote = this.unwrap(\n        await this.apiClient.put<EditNoteResponse>(`/notes/${options.note_id}`, updateBody),\n      )\n\n      // Validate response\n      if (!updatedNote || typeof updatedNote !== \"object\" || !updatedNote.id) {\n        return \"Error: Unexpected response format from Joplin API when updating note\"\n      }\n\n      // Get notebook info for both old and new locations if parent_id changed\n      let oldNotebookInfo = \"Root level\"\n      let newNotebookInfo = \"Root level\"\n\n      if (currentNote.parent_id) {\n        try {\n          const oldNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${currentNote.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (oldNotebook?.title) {\n            oldNotebookInfo = `\"${oldNotebook.title}\"`\n          }\n        } catch {\n          oldNotebookInfo = `Notebook ID: ${currentNote.parent_id}`\n        }\n      }\n\n      if (updatedNote.parent_id && updatedNote.parent_id !== currentNote.parent_id) {\n        try {\n          const newNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${updatedNote.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (newNotebook?.title) {\n            newNotebookInfo = `\"${newNotebook.title}\"`\n          }\n        } catch {\n          newNotebookInfo = `Notebook ID: ${updatedNote.parent_id}`\n        }\n      } else if (updatedNote.parent_id) {\n        newNotebookInfo = oldNotebookInfo\n      }\n\n      // Format success response with before/after comparison\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully updated note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Note: \"${updatedNote.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${updatedNote.id}`)\n      resultLines.push(\"\")\n\n      // Show what changed\n      resultLines.push(`🔄 Changes made:`)\n\n      if (options.title !== undefined && currentNote.title !== updatedNote.title) {\n        resultLines.push(`   Title: \"${currentNote.title}\" → \"${updatedNote.title}\"`)\n      }\n\n      if (options.parent_id !== undefined && currentNote.parent_id !== updatedNote.parent_id) {\n        resultLines.push(`   Location: ${oldNotebookInfo} → ${newNotebookInfo}`)\n      }\n\n      if (options.is_todo !== undefined && currentNote.is_todo !== updatedNote.is_todo) {\n        const oldType = currentNote.is_todo ? \"Todo\" : \"Regular note\"\n        const newType = updatedNote.is_todo ? \"Todo\" : \"Regular note\"\n        resultLines.push(`   Type: ${oldType} → ${newType}`)\n      }\n\n      if (options.todo_completed !== undefined && currentNote.todo_completed !== updatedNote.todo_completed) {\n        const oldStatus = currentNote.todo_completed ? \"Completed\" : \"Not completed\"\n        const newStatus = updatedNote.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`   Todo Status: ${oldStatus} → ${newStatus}`)\n      }\n\n      if (options.todo_due !== undefined) {\n        const oldDue = currentNote.todo_due ? this.formatDate(currentNote.todo_due) : \"No due date\"\n        const newDue = updatedNote.todo_due ? this.formatDate(updatedNote.todo_due) : \"No due date\"\n        if (oldDue !== newDue) {\n          resultLines.push(`   Due Date: ${oldDue} → ${newDue}`)\n        }\n      }\n\n      if (options.body !== undefined) {\n        resultLines.push(`   Content: Updated`)\n      }\n\n      if (options.body_html !== undefined) {\n        resultLines.push(`   HTML Content: Updated`)\n      }\n\n      const updatedTime = this.formatDate(updatedNote.updated_time)\n      resultLines.push(`   Last Updated: ${updatedTime}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - Read the note: read_note note_id=\"${updatedNote.id}\"`)\n      if (updatedNote.parent_id) {\n        resultLines.push(`   - View notebook: read_notebook notebook_id=\"${updatedNote.parent_id}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n        }\n        if (error.response.status === 400) {\n          return `Error updating note: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n        }\n      }\n      return this.formatError(error, \"updating note\")\n    }\n  }\n}\n\nexport default EditNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\nclass ListNotebooks extends BaseTool {\n  async call(): Promise<string> {\n    try {\n      const notebooks = this.unwrap(\n        await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      const notebooksByParentId: Record<string, JoplinFolder[]> = {}\n\n      notebooks.forEach((notebook) => {\n        const parentId = notebook.parent_id || \"\"\n        if (!notebooksByParentId[parentId]) {\n          notebooksByParentId[parentId] = []\n        }\n        notebooksByParentId[parentId].push(notebook)\n      })\n\n      // Add a header with instructions\n      const resultLines = [\n        \"Joplin Notebooks:\\n\",\n        \"NOTE: To read a notebook, use the notebook_id with the read_notebook command\\n\",\n        'Example: read_notebook notebook_id=\"your-notebook-id\"\\n\\n',\n      ]\n\n      // Add the notebook hierarchy\n      resultLines.push(\n        ...this.notebooksLines(notebooksByParentId[\"\"] || [], {\n          indent: 0,\n          notebooksByParentId,\n        }),\n      )\n\n      return resultLines.join(\"\")\n    } catch (error: unknown) {\n      return this.formatError(error, \"listing notebooks\")\n    }\n  }\n\n  private notebooksLines(\n    notebooks: JoplinFolder[],\n    {\n      indent = 0,\n      notebooksByParentId,\n    }: {\n      indent: number\n      notebooksByParentId: Record<string, JoplinFolder[]>\n    },\n  ): string[] {\n    const result: string[] = []\n    const indentSpaces = \" \".repeat(indent)\n\n    this.sortNotebooks(notebooks).forEach((notebook) => {\n      const id = notebook.id\n      result.push(`${indentSpaces}Notebook: \"${notebook.title}\" (notebook_id: \"${id}\")\\n`)\n\n      const childNotebooks = notebooksByParentId[id]\n      if (childNotebooks) {\n        result.push(\n          ...this.notebooksLines(childNotebooks, {\n            indent: indent + 2,\n            notebooksByParentId,\n          }),\n        )\n      }\n    })\n\n    return result\n  }\n\n  private sortNotebooks(notebooks: JoplinFolder[]): JoplinFolder[] {\n    // Ensure that notebooks starting with '[0]' are sorted first\n    const CHARACTER_BEFORE_A = String.fromCharCode(\"A\".charCodeAt(0) - 1)\n    return [...notebooks].sort((a, b) => {\n      const titleA = a.title.replace(\"[\", CHARACTER_BEFORE_A)\n      const titleB = b.title.replace(\"[\", CHARACTER_BEFORE_A)\n      return titleA.localeCompare(titleB)\n    })\n  }\n}\n\nexport default ListNotebooks\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\nclass ReadMultiNote extends BaseTool {\n  async call(noteIds: string[]): Promise<string> {\n    if (!noteIds || !Array.isArray(noteIds) || noteIds.length === 0) {\n      return 'Please provide an array of note IDs. Example: read_multinote note_ids=[\"id1\", \"id2\", \"id3\"]'\n    }\n\n    // Validate that all IDs look like valid note IDs\n    const invalidIds = noteIds.filter((id) => !id || id.length < 10 || !id.match(/[a-f0-9]/i))\n    if (invalidIds.length > 0) {\n      return `Error: Some IDs do not appear to be valid note IDs: ${invalidIds.join(\", \")}\\n\\nNote IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse search_notes to find notes and their IDs.`\n    }\n\n    const resultLines: string[] = []\n    const notFound: string[] = []\n    const errors: string[] = []\n    const successful: string[] = []\n\n    // Add a header\n    resultLines.push(`# Reading ${noteIds.length} notes\\n`)\n\n    // Process each note ID\n    for (let i = 0; i < noteIds.length; i++) {\n      const noteId = noteIds[i]\n      resultLines.push(`## Note ${i + 1} of ${noteIds.length} (ID: ${noteId})\\n`)\n\n      try {\n        // Get the note details with all relevant fields\n        const note = this.unwrap(\n          await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n            query: {\n              fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n            },\n          }),\n        )\n\n        // Validate note response\n        if (!note || typeof note !== \"object\" || !note.id) {\n          errors.push(noteId)\n          resultLines.push(`Error: Unexpected response format from Joplin API when fetching note ${noteId}\\n`)\n          continue\n        }\n\n        successful.push(noteId)\n\n        // Get the notebook info to show where this note is located\n        let notebookInfo = \"Unknown notebook\"\n        if (note.parent_id) {\n          try {\n            const notebook = this.unwrap(\n              await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n                query: { fields: \"id,title\" },\n              }),\n            )\n            if (notebook && notebook.title) {\n              notebookInfo = `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n            }\n          } catch (err: unknown) {\n            process.stderr.write(`Error fetching notebook info for note ${noteId}: ${err}\\n`)\n            // Continue even if we can't get the notebook info\n          }\n        }\n\n        // Add note metadata\n        resultLines.push(`### Note: \"${note.title}\"`)\n        resultLines.push(`Notebook: ${notebookInfo}`)\n\n        // Add todo status if applicable\n        if (note.is_todo) {\n          const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n          resultLines.push(`Status: ${status}`)\n\n          if (note.todo_due) {\n            const dueDate = this.formatDate(note.todo_due)\n            resultLines.push(`Due: ${dueDate}`)\n          }\n        }\n\n        // Add timestamps\n        const createdDate = this.formatDate(note.created_time)\n        const updatedDate = this.formatDate(note.updated_time)\n        resultLines.push(`Created: ${createdDate}`)\n        resultLines.push(`Updated: ${updatedDate}`)\n\n        // Add a separator before the note content\n        resultLines.push(\"\\n---\\n\")\n\n        // Add the note body\n        if (note.body) {\n          resultLines.push(note.body)\n        } else {\n          resultLines.push(\"(This note has no content)\")\n        }\n\n        // Add a separator after the note\n        resultLines.push(\"\\n---\\n\")\n      } catch (error: any) {\n        process.stderr.write(`Error reading note ${noteId}: ${error}\\n`)\n        if (error.response && error.response.status === 404) {\n          notFound.push(noteId)\n          resultLines.push(`Note with ID \"${noteId}\" not found.\\n`)\n        } else {\n          errors.push(noteId)\n          resultLines.push(`Error reading note: ${error.message || \"Unknown error\"}\\n`)\n        }\n      }\n    }\n\n    // Add a summary at the end\n    resultLines.push(\"# Summary\")\n    resultLines.push(`Total notes requested: ${noteIds.length}`)\n    resultLines.push(`Successfully retrieved: ${successful.length}`)\n\n    if (notFound.length > 0) {\n      resultLines.push(`Notes not found: ${notFound.length}`)\n      resultLines.push(`IDs not found: ${notFound.join(\", \")}`)\n    }\n\n    if (errors.length > 0) {\n      resultLines.push(`Errors encountered: ${errors.length}`)\n      resultLines.push(`IDs with errors: ${errors.join(\", \")}`)\n    }\n\n    return resultLines.join(\"\\n\")\n  }\n}\n\nexport default ReadMultiNote\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\nclass ReadNote extends BaseTool {\n  async call(noteId: string): Promise<string> {\n    const validationError = this.validateId(noteId, \"note\")\n    if (validationError) {\n      return validationError\n    }\n\n    try {\n      // Get the note details with all relevant fields\n      const note = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n          query: {\n            fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n          },\n        }),\n      )\n\n      // Validate note response\n      if (!note || typeof note !== \"object\" || !note.id) {\n        return `Error: Unexpected response format from Joplin API when fetching note`\n      }\n\n      // Get the notebook info to show where this note is located\n      let notebookInfo = \"Unknown notebook\"\n      if (note.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (notebook && notebook.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n          }\n        } catch (err: unknown) {\n          process.stderr.write(`Error fetching notebook info: ${err}\\n`)\n          // Continue even if we can't get the notebook info\n        }\n      }\n\n      // Format the note content\n      const resultLines: string[] = []\n\n      // Add note header with metadata\n      resultLines.push(`# Note: \"${note.title}\"`)\n      resultLines.push(`Note ID: ${note.id}`)\n      resultLines.push(`Notebook: ${notebookInfo}`)\n\n      // Add todo status if applicable\n      if (note.is_todo) {\n        const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`Status: ${status}`)\n\n        if (note.todo_due) {\n          const dueDate = this.formatDate(note.todo_due)\n          resultLines.push(`Due: ${dueDate}`)\n        }\n      }\n\n      // Add timestamps\n      const createdDate = this.formatDate(note.created_time)\n      const updatedDate = this.formatDate(note.updated_time)\n      resultLines.push(`Created: ${createdDate}`)\n      resultLines.push(`Updated: ${updatedDate}`)\n\n      // Add a separator before the note content\n      resultLines.push(\"\\n---\\n\")\n\n      // Add the note body\n      if (note.body) {\n        resultLines.push(note.body)\n      } else {\n        resultLines.push(\"(This note has no content)\")\n      }\n\n      // Add a footer with helpful commands\n      resultLines.push(\"\\n---\\n\")\n      resultLines.push(\"Related commands:\")\n      resultLines.push(`- To view the notebook containing this note: read_notebook notebook_id=\"${note.parent_id}\"`)\n      resultLines.push('- To search for more notes: search_notes query=\"your search term\"')\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response && error.response.status === 404) {\n        return `Note with ID \"${noteId}\" not found.\\n\\nThis might happen if:\\n1. The ID is incorrect\\n2. You're using a notebook ID instead of a note ID\\n3. The note has been deleted\\n\\nUse search_notes to find notes and their IDs.`\n      }\n      return (\n        this.formatError(error, \"reading note\") +\n        `\\n\\nMake sure you're using a valid note ID.\\nUse search_notes to find notes and their IDs.`\n      )\n    }\n  }\n}\n\nexport default ReadNote\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface NotebookNotesResponse {\n  items: JoplinNote[]\n}\n\nclass ReadNotebook extends BaseTool {\n  async call(notebookId: string): Promise<string> {\n    const validationError = this.validateId(notebookId, \"notebook\")\n    if (validationError) {\n      return validationError\n    }\n\n    try {\n      // First, get the notebook details\n      const notebook = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${notebookId}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      // Validate notebook response\n      if (!notebook || typeof notebook !== \"object\" || !notebook.id) {\n        return `Error: Unexpected response format from Joplin API when fetching notebook`\n      }\n\n      // Get all notes in this notebook\n      const notes = this.unwrap(\n        await this.apiClient.get<NotebookNotesResponse>(`/folders/${notebookId}/notes`, {\n          query: { fields: \"id,title,updated_time,is_todo,todo_completed\" },\n        }),\n      )\n\n      // Validate notes response\n      if (!notes || typeof notes !== \"object\") {\n        return `Error: Unexpected response format from Joplin API when fetching notes`\n      }\n\n      if (!notes.items || !Array.isArray(notes.items) || notes.items.length === 0) {\n        return `Notebook \"${notebook.title}\" (notebook_id: \"${notebook.id}\") is empty.\\n\\nTry another notebook ID or use list_notebooks to see all available notebooks.`\n      }\n\n      // Format the notebook contents\n      const resultLines: string[] = []\n      resultLines.push(`# Notebook: \"${notebook.title}\" (notebook_id: \"${notebook.id}\")`)\n      resultLines.push(`Contains ${notes.items.length} notes:\\n`)\n      resultLines.push(`NOTE: This is showing the contents of notebook \"${notebook.title}\", not a specific note.\\n`)\n\n      // If multiple notes were found, add a hint about read_multinote\n      if (notes.items.length > 1) {\n        const noteIds = notes.items.map((note) => note.id)\n        resultLines.push(`TIP: To read all ${notes.items.length} notes at once, use:\\n`)\n        resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n      }\n\n      // Sort notes by updated_time (newest first)\n      const sortedNotes = [...notes.items].sort((a, b) => b.updated_time - a.updated_time)\n\n      sortedNotes.forEach((note) => {\n        const updatedDate = this.formatDate(note.updated_time)\n\n        // Add checkbox for todos\n        if (note.is_todo) {\n          const checkboxStatus = note.todo_completed ? \"✅\" : \"☐\"\n          resultLines.push(`- ${checkboxStatus} Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        } else {\n          resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        }\n\n        resultLines.push(`  Updated: ${updatedDate}`)\n        resultLines.push(`  To read this note: read_note note_id=\"${note.id}\"`)\n        resultLines.push(\"\") // Empty line between notes\n      })\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response && error.response.status === 404) {\n        return `Notebook with ID \"${notebookId}\" not found.\\n\\nThis might happen if:\\n1. The ID is incorrect\\n2. You're using a note title instead of a notebook ID\\n3. The notebook has been deleted\\n\\nUse list_notebooks to see all available notebooks with their IDs.`\n      }\n      return (\n        this.formatError(error, \"reading notebook\") +\n        `\\n\\nMake sure you're using a valid notebook ID, not a note title.\\nUse list_notebooks to see all available notebooks with their IDs.`\n      )\n    }\n  }\n}\n\nexport default ReadNotebook\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface SearchResult {\n  items: JoplinNote[]\n}\n\nclass SearchNotes extends BaseTool {\n  async call(query: string): Promise<string> {\n    if (!query) {\n      return \"Please provide a search query.\"\n    }\n\n    try {\n      // Search for notes with the given query\n      const searchResults = this.unwrap(\n        await this.apiClient.get<SearchResult>(\"/search\", {\n          query: {\n            query,\n            fields: \"id,title,body,parent_id,updated_time\",\n          },\n        }),\n      )\n\n      // Handle case where the API doesn't return the expected structure\n      if (!searchResults || typeof searchResults !== \"object\") {\n        return `Error: Unexpected response format from Joplin API`\n      }\n\n      // Handle case where no items were found\n      if (!searchResults.items || !Array.isArray(searchResults.items) || searchResults.items.length === 0) {\n        return `No notes found matching query: \"${query}\"`\n      }\n\n      // Get all folders to be able to show notebook names\n      const folders = this.unwrap(\n        await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n          query: {\n            fields: \"id,title\",\n          },\n        }),\n      )\n\n      // Create a map of folder IDs to folder titles for quick lookup\n      const folderMap: Record<string, string> = {}\n      folders.forEach((folder) => {\n        folderMap[folder.id] = folder.title\n      })\n\n      // Format the search results\n      const resultLines: string[] = []\n      resultLines.push(`Found ${searchResults.items.length} notes matching query: \"${query}\"\\n`)\n      resultLines.push(`NOTE: To read a notebook, use the notebook ID (not the note title)\\n`)\n\n      // If multiple notes were found, add a hint about read_multinote\n      if (searchResults.items.length > 1) {\n        const noteIds = searchResults.items.map((note) => note.id)\n        resultLines.push(`TIP: To read all ${searchResults.items.length} notes at once, use:\\n`)\n        resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n      }\n\n      searchResults.items.forEach((note) => {\n        const notebookTitle = folderMap[note.parent_id || \"\"] || \"Unknown notebook\"\n        const notebookId = note.parent_id || \"unknown\"\n        const updatedDate = this.formatDate(note.updated_time)\n\n        resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        resultLines.push(`  Notebook: \"${notebookTitle}\" (notebook_id: \"${notebookId}\")`)\n        resultLines.push(`  Updated: ${updatedDate}`)\n\n        // Add a snippet of the note body if available\n        if (note.body) {\n          const snippet = note.body.substring(0, 100).replace(/\\n/g, \" \") + (note.body.length > 100 ? \"...\" : \"\")\n          resultLines.push(`  Snippet: ${snippet}`)\n        }\n\n        // Add hints for using related commands\n        resultLines.push(`  To read this note: read_note note_id=\"${note.id}\"`)\n        resultLines.push(`  To read this notebook: read_notebook notebook_id=\"${notebookId}\"`)\n\n        resultLines.push(\"\") // Empty line between notes\n      })\n\n      return resultLines.join(\"\\n\")\n    } catch (error: unknown) {\n      return this.formatError(error, \"searching notes\")\n    }\n  }\n}\n\nexport default SearchNotes\n","import { Either } from \"functype\"\n\nimport JoplinAPIClient from \"./lib/joplin-api-client.js\"\nimport type { JoplinSidecar } from \"./lib/joplin-sidecar.js\"\nimport {\n  CreateFolder,\n  CreateNote,\n  DeleteFolder,\n  DeleteNote,\n  EditFolder,\n  EditNote,\n  ListNotebooks,\n  ReadMultiNote,\n  ReadNote,\n  ReadNotebook,\n  SearchNotes,\n} from \"./lib/tools/index.js\"\n\nexport type JoplinServerConfig = {\n  host: string\n  port: number\n  token: string\n  sidecar?: JoplinSidecar\n}\n\nexport class JoplinServerManager {\n  private apiClient: JoplinAPIClient\n  private config: JoplinServerConfig\n  private connected: boolean = false\n  private tools: {\n    listNotebooks: ListNotebooks\n    searchNotes: SearchNotes\n    readNotebook: ReadNotebook\n    readNote: ReadNote\n    readMultiNote: ReadMultiNote\n    createNote: CreateNote\n    createFolder: CreateFolder\n    editNote: EditNote\n    editFolder: EditFolder\n    deleteNote: DeleteNote\n    deleteFolder: DeleteFolder\n  }\n\n  constructor(config: JoplinServerConfig) {\n    this.config = config\n    this.apiClient = new JoplinAPIClient({\n      host: config.host,\n      port: config.port,\n      token: config.token,\n    })\n\n    this.tools = {\n      listNotebooks: new ListNotebooks(this.apiClient),\n      searchNotes: new SearchNotes(this.apiClient),\n      readNotebook: new ReadNotebook(this.apiClient),\n      readNote: new ReadNote(this.apiClient),\n      readMultiNote: new ReadMultiNote(this.apiClient),\n      createNote: new CreateNote(this.apiClient),\n      createFolder: new CreateFolder(this.apiClient),\n      editNote: new EditNote(this.apiClient),\n      editFolder: new EditFolder(this.apiClient),\n      deleteNote: new DeleteNote(this.apiClient),\n      deleteFolder: new DeleteFolder(this.apiClient),\n    }\n  }\n\n  async ensureConnected(): Promise<void> {\n    if (this.connected) {\n      const result = await this.apiClient.serviceAvailable()\n      if (Either.isRight(result)) return\n      this.connected = false\n    }\n\n    const available = await this.apiClient.serviceAvailable()\n    if (Either.isRight(available)) {\n      this.connected = true\n      process.stderr.write(`Connected to Joplin at ${this.config.host}:${this.config.port}\\n`)\n      return\n    }\n\n    // If sidecar exists, try starting it\n    if (this.config.sidecar) {\n      process.stderr.write(\"Joplin not available, starting sidecar...\\n\")\n      const startResult = await this.config.sidecar.start()\n      if (Either.isLeft(startResult)) {\n        const error = startResult.fold(\n          (e) => e,\n          () => null as never,\n        )\n        throw new Error(`Sidecar failed [${error.code}]: ${error.message}`)\n      }\n      const retryAvailable = await this.apiClient.serviceAvailable()\n      if (Either.isRight(retryAvailable)) {\n        this.connected = true\n        process.stderr.write(`Connected to Joplin via sidecar at ${this.config.host}:${this.config.port}\\n`)\n        return\n      }\n    }\n\n    throw new Error(\n      `Joplin is not available at ${this.config.host}:${this.config.port}. ` +\n        `Please ensure Joplin is running or configure a sidecar.`,\n    )\n  }\n\n  // Tool execution methods\n  async listNotebooks(): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.listNotebooks.call()\n  }\n\n  async searchNotes(query: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.searchNotes.call(query)\n  }\n\n  async readNotebook(notebookId: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readNotebook.call(notebookId)\n  }\n\n  async readNote(noteId: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readNote.call(noteId)\n  }\n\n  async readMultiNote(noteIds: string[]): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readMultiNote.call(noteIds)\n  }\n\n  async createNote(params: {\n    title?: string | undefined\n    body?: string | undefined\n    body_html?: string | undefined\n    parent_id?: string | undefined\n    is_todo?: boolean | undefined\n    image_data_url?: string | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.createNote.call(params)\n  }\n\n  async createFolder(params: { title: string; parent_id?: string | undefined }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.createFolder.call(params)\n  }\n\n  async editNote(params: {\n    note_id: string\n    title?: string | undefined\n    body?: string | undefined\n    body_html?: string | undefined\n    parent_id?: string | undefined\n    is_todo?: boolean | undefined\n    todo_completed?: boolean | undefined\n    todo_due?: number | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.editNote.call(params)\n  }\n\n  async editFolder(params: {\n    folder_id: string\n    title?: string | undefined\n    parent_id?: string | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.editFolder.call(params)\n  }\n\n  async deleteNote(params: { note_id: string; confirm?: boolean | undefined }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.deleteNote.call(params)\n  }\n\n  async deleteFolder(params: {\n    folder_id: string\n    confirm?: boolean | undefined\n    force?: boolean | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.deleteFolder.call(params)\n  }\n\n  async sync(): Promise<string> {\n    await this.ensureConnected()\n    const desktopWarning = this.config.sidecar?.isDesktopDetected()\n      ? \"\\n\\nNote: Joplin Desktop is also running. The sidecar and Desktop use separate databases. \" +\n        \"Notes sync between them only if both are configured with the same sync target.\"\n      : \"\"\n    // Try triggering sync via the Joplin REST API (POST /services/sync)\n    const result = await this.apiClient.post<Record<string, unknown>>(\"/services/sync\", { action: \"start\" })\n    return result.fold(\n      (error) => {\n        const msg = error.message || String(error)\n        // 404 or \"No action API\" = Joplin instance doesn't expose sync as a REST service\n        // This is normal for Joplin Terminal CLI — it auto-syncs on its configured interval\n        if (msg.includes(\"404\") || msg.includes(\"No action API\") || msg.includes(\"No such service\")) {\n          return (\n            \"Sync is managed automatically by the Joplin server on its configured interval \" +\n            \"(default: every 5 minutes). On-demand sync is not available via the Joplin Terminal API.\" +\n            desktopWarning\n          )\n        }\n        return `Sync failed: ${msg}${desktopWarning}`\n      },\n      () => `Sync triggered successfully.${desktopWarning}`,\n    )\n  }\n}\n\nexport function initializeJoplinManager(config: JoplinServerConfig): JoplinServerManager {\n  return new JoplinServerManager(config)\n}\n","import { FastMCP } from \"fastmcp\"\nimport { readFileSync } from \"fs\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { z } from \"zod\"\n\nimport { initializeJoplinManager, JoplinServerManager } from \"./server-core.js\"\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"))\nconst VERSION = packageJson.version\n\nexport type FastMCPServerOptions = {\n  host: string\n  port: number\n  token: string\n  httpPort?: number\n  endpoint?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): { server: FastMCP; manager: JoplinServerManager } {\n  process.stderr.write(\"Initializing FastMCP server for Joplin...\\n\")\n\n  // Initialize Joplin manager\n  const manager = initializeJoplinManager({ host: options.host, port: options.port, token: options.token })\n\n  // Create FastMCP server\n  const server = new FastMCP({\n    name: \"joplin\",\n    version: VERSION,\n    health: {\n      enabled: true,\n      path: \"/health\",\n      status: 200,\n      message: JSON.stringify({\n        status: \"healthy\",\n        service: \"joplin-mcp-server\",\n        version: VERSION,\n        joplinPort: options.port,\n        timestamp: new Date().toISOString(),\n      }),\n    },\n  })\n\n  // Add list_notebooks tool\n  server.addTool({\n    name: \"list_notebooks\",\n    description: \"Retrieve the complete notebook hierarchy from Joplin\",\n    parameters: z.object({}),\n    execute: async () => {\n      return await manager.listNotebooks()\n    },\n  })\n\n  // Add search_notes tool\n  server.addTool({\n    name: \"search_notes\",\n    description: \"Search for notes in Joplin and return matching notebooks\",\n    parameters: z.object({\n      query: z.string().describe(\"Search query for notes\"),\n    }),\n    execute: async (args) => {\n      return await manager.searchNotes(args.query)\n    },\n  })\n\n  // Add read_notebook tool\n  server.addTool({\n    name: \"read_notebook\",\n    description: \"Read the contents of a specific notebook\",\n    parameters: z.object({\n      notebook_id: z.string().describe(\"ID of the notebook to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readNotebook(args.notebook_id)\n    },\n  })\n\n  // Add read_note tool\n  server.addTool({\n    name: \"read_note\",\n    description: \"Read the full content of a specific note\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readNote(args.note_id)\n    },\n  })\n\n  // Add read_multinote tool\n  server.addTool({\n    name: \"read_multinote\",\n    description: \"Read the full content of multiple notes at once\",\n    parameters: z.object({\n      note_ids: z.array(z.string()).describe(\"Array of note IDs to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readMultiNote(args.note_ids)\n    },\n  })\n\n  // Add create_note tool\n  server.addTool({\n    name: \"create_note\",\n    description: \"Create a new note in Joplin\",\n    parameters: z.object({\n      title: z.string().optional().describe(\"Note title\"),\n      body: z.string().optional().describe(\"Note content in Markdown\"),\n      body_html: z.string().optional().describe(\"Note content in HTML\"),\n      parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n      is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n      image_data_url: z.string().optional().describe(\"Base64 encoded image data URL\"),\n    }),\n    execute: async (args) => {\n      return await manager.createNote(args)\n    },\n  })\n\n  // Add create_folder tool\n  server.addTool({\n    name: \"create_folder\",\n    description: \"Create a new folder/notebook in Joplin\",\n    parameters: z.object({\n      title: z.string().describe(\"Notebook title\"),\n      parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n    }),\n    execute: async (args) => {\n      return await manager.createFolder(args)\n    },\n  })\n\n  // Add edit_note tool\n  server.addTool({\n    name: \"edit_note\",\n    description: \"Edit/update an existing note in Joplin\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to edit\"),\n      title: z.string().optional().describe(\"New note title\"),\n      body: z.string().optional().describe(\"New note content in Markdown\"),\n      body_html: z.string().optional().describe(\"New note content in HTML\"),\n      parent_id: z.string().optional().describe(\"New parent notebook ID\"),\n      is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n      todo_completed: z.boolean().optional().describe(\"Whether todo is completed\"),\n      todo_due: z.number().optional().describe(\"Todo due date (Unix timestamp)\"),\n    }),\n    execute: async (args) => {\n      return await manager.editNote(args)\n    },\n  })\n\n  // Add edit_folder tool\n  server.addTool({\n    name: \"edit_folder\",\n    description: \"Edit/update an existing folder/notebook in Joplin\",\n    parameters: z.object({\n      folder_id: z.string().describe(\"ID of the folder to edit\"),\n      title: z.string().optional().describe(\"New folder title\"),\n      parent_id: z.string().optional().describe(\"New parent folder ID\"),\n    }),\n    execute: async (args) => {\n      return await manager.editFolder(args)\n    },\n  })\n\n  // Add delete_note tool\n  server.addTool({\n    name: \"delete_note\",\n    description: \"Delete a note from Joplin (requires confirmation)\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to delete\"),\n      confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n    }),\n    execute: async (args) => {\n      return await manager.deleteNote(args)\n    },\n  })\n\n  // Add delete_folder tool\n  server.addTool({\n    name: \"delete_folder\",\n    description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n    parameters: z.object({\n      folder_id: z.string().describe(\"ID of the folder to delete\"),\n      confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n      force: z.boolean().optional().describe(\"Force delete even if folder has contents\"),\n    }),\n    execute: async (args) => {\n      return await manager.deleteFolder(args)\n    },\n  })\n\n  // Add sync tool\n  server.addTool({\n    name: \"sync\",\n    description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n    parameters: z.object({}),\n    execute: async () => {\n      return await manager.sync()\n    },\n  })\n\n  process.stderr.write(\"FastMCP server configured with 12 Joplin tools\\n\")\n  return { server, manager }\n}\n\nexport async function startFastMCPServer(options: FastMCPServerOptions): Promise<void> {\n  const { server } = createFastMCPServer(options)\n\n  process.stderr.write(`Configured for Joplin at ${options.host}:${options.port}\\n`)\n\n  const port = options.httpPort || 3000\n  const endpoint = options.endpoint || \"/mcp\"\n\n  await server.start({\n    transportType: \"httpStream\",\n    httpStream: {\n      port,\n      endpoint: endpoint as `/${string}`,\n    },\n  })\n\n  process.stderr.write(`FastMCP server running on http://0.0.0.0:${port}${endpoint}\\n`)\n}\n","#!/usr/bin/env node\n\ndeclare const __VERSION__: string\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { type CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\"\nimport fs from \"fs\"\nimport path from \"path\"\nimport { fileURLToPath } from \"url\"\n\nimport { DEFAULT_API_PORT, JoplinSidecar, type SyncTarget } from \"./lib/joplin-sidecar.js\"\nimport parseArgs from \"./lib/parse-args.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\nimport { startFastMCPServer } from \"./server-fastmcp.js\"\n\n// Parse command line arguments\nconst parsedArgs = parseArgs()\nconst { transport, httpPort, profileDir, syncTarget } = parsedArgs\n\nconst isHttpMode = transport === \"http\"\n\n// External mode: JOPLIN_HOST/JOPLIN_PORT set = connect directly, skip sidecar\nconst externalHost = process.env.JOPLIN_HOST\nconst externalPort = process.env.JOPLIN_PORT ? parseInt(process.env.JOPLIN_PORT, 10) : undefined\nconst externalMode = !!(externalHost || externalPort)\n\n// Token is required for external mode, auto-generated for sidecar mode\nif (!process.env.JOPLIN_TOKEN && externalMode) {\n  process.stderr.write(\n    \"Error: JOPLIN_TOKEN is required in external mode. Use --token <token> or set JOPLIN_TOKEN environment variable.\\n\",\n  )\n  process.exit(1)\n}\n\n// In sidecar mode, persist the auto-generated token so the config hash stays stable\n// across restarts, enabling config caching to skip redundant `joplin config` CLI calls.\nconst joplinToken = (() => {\n  if (process.env.JOPLIN_TOKEN) return process.env.JOPLIN_TOKEN\n  if (externalMode) return `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n  const tokenPath = path.join(profileDir, \".mcp-token\")\n  try {\n    const saved = fs.readFileSync(tokenPath, \"utf-8\").trim()\n    if (saved) return saved\n  } catch {\n    // No saved token yet\n  }\n  const token = `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n  try {\n    fs.mkdirSync(profileDir, { recursive: true })\n    fs.writeFileSync(tokenPath, token)\n  } catch {\n    // Non-critical — token still works, just won't be cached\n  }\n  return token\n})()\n\n// Main startup logic\nasync function main(): Promise<void> {\n  let host: string\n  let port: number\n  let sidecar: JoplinSidecar | undefined\n\n  if (externalMode) {\n    // External mode — connect to existing Joplin instance (e.g. Windows desktop from WSL)\n    host = externalHost || \"127.0.0.1\"\n    port = externalPort || DEFAULT_API_PORT\n    process.stderr.write(`External mode: connecting to Joplin at ${host}:${port}\\n`)\n  } else {\n    // Sidecar mode — spawn and manage Joplin Terminal\n    sidecar = new JoplinSidecar({\n      profileDir,\n      apiPort: DEFAULT_API_PORT,\n      apiToken: joplinToken,\n      syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n    })\n\n    // Phase 1: Resolve port (fast — a few HTTP probes).\n    // Must complete before getPort() so downstream gets the correct port.\n    const portResult = await sidecar.resolvePort()\n    portResult.fold(\n      (err) => process.stderr.write(`Warning: Port resolution failed: ${err.message}\\n`),\n      (p) => process.stderr.write(`Sidecar will use port ${p}\\n`),\n    )\n\n    // Phase 2: Fire-and-forget the slow startup (CLI config, spawn, wait).\n    // ensureConnected() in server-core.ts will await or retry on first tool call.\n    sidecar.start().then((result) => {\n      result.fold(\n        (err) => {\n          process.stderr.write(`Warning: Sidecar failed to start: ${err.message}\\n`)\n          process.stderr.write(\"Attempting to connect to existing Joplin instance...\\n\")\n        },\n        () => {\n          process.stderr.write(\"Joplin sidecar started successfully\\n\")\n        },\n      )\n    })\n\n    host = sidecar.getHost()\n    port = sidecar.getPort()\n\n    // Cleanup on exit\n    const cleanup = async () => {\n      await sidecar!.stop()\n      process.exit(0)\n    }\n    process.on(\"SIGINT\", () => void cleanup())\n    process.on(\"SIGTERM\", () => void cleanup())\n  }\n\n  if (isHttpMode) {\n    process.stderr.write(\"Starting HTTP transport mode with FastMCP...\\n\")\n    await startFastMCPServer({\n      host,\n      port,\n      token: joplinToken,\n      httpPort,\n      endpoint: \"/mcp\",\n    })\n  } else {\n    process.stderr.write(\"Starting stdio transport mode...\\n\")\n    await startStdioServer(host, port, joplinToken, sidecar)\n  }\n}\n\nmain().catch((error) => {\n  process.stderr.write(`Failed to start MCP server: ${error}\\n`)\n  process.exit(1)\n})\n\nasync function startStdioServer(host: string, port: number, token: string, sidecar?: JoplinSidecar): Promise<void> {\n  const manager = initializeJoplinManager({ host, port, token, sidecar })\n\n  const server = new Server(\n    {\n      name: \"joplin-mcp-server\",\n      version: __VERSION__,\n    },\n    {\n      capabilities: {\n        resources: {},\n        tools: {},\n        prompts: {},\n      },\n    },\n  )\n\n  // Register tool list handler\n  server.setRequestHandler(ListToolsRequestSchema, () => {\n    return {\n      tools: [\n        {\n          name: \"list_notebooks\",\n          description: \"Retrieve the complete notebook hierarchy from Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {},\n          },\n        },\n        {\n          name: \"search_notes\",\n          description: \"Search for notes in Joplin and return matching notebooks\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              query: { type: \"string\", description: \"Search query\" },\n            },\n            required: [\"query\"],\n          },\n        },\n        {\n          name: \"read_notebook\",\n          description: \"Read the contents of a specific notebook\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              notebook_id: { type: \"string\", description: \"ID of the notebook to read\" },\n            },\n            required: [\"notebook_id\"],\n          },\n        },\n        {\n          name: \"read_note\",\n          description: \"Read the full content of a specific note\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to read\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"read_multinote\",\n          description: \"Read the full content of multiple notes at once\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_ids: { type: \"array\", items: { type: \"string\" }, description: \"Array of note IDs to read\" },\n            },\n            required: [\"note_ids\"],\n          },\n        },\n        {\n          name: \"create_note\",\n          description: \"Create a new note in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              title: { type: \"string\", description: \"Note title\" },\n              body: { type: \"string\", description: \"Note content in Markdown\" },\n              body_html: { type: \"string\", description: \"Note content in HTML\" },\n              parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n              is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n              image_data_url: { type: \"string\", description: \"Base64 encoded image data URL\" },\n            },\n          },\n        },\n        {\n          name: \"create_folder\",\n          description: \"Create a new folder/notebook in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              title: { type: \"string\", description: \"Notebook title\" },\n              parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n            },\n            required: [\"title\"],\n          },\n        },\n        {\n          name: \"edit_note\",\n          description: \"Edit/update an existing note in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to edit\" },\n              title: { type: \"string\", description: \"New note title\" },\n              body: { type: \"string\", description: \"New note content in Markdown\" },\n              body_html: { type: \"string\", description: \"New note content in HTML\" },\n              parent_id: { type: \"string\", description: \"New parent notebook ID\" },\n              is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n              todo_completed: { type: \"boolean\", description: \"Whether todo is completed\" },\n              todo_due: { type: \"number\", description: \"Todo due date (Unix timestamp)\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"edit_folder\",\n          description: \"Edit/update an existing folder/notebook in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              folder_id: { type: \"string\", description: \"ID of the folder to edit\" },\n              title: { type: \"string\", description: \"New folder title\" },\n              parent_id: { type: \"string\", description: \"New parent folder ID\" },\n            },\n            required: [\"folder_id\"],\n          },\n        },\n        {\n          name: \"delete_note\",\n          description: \"Delete a note from Joplin (requires confirmation)\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to delete\" },\n              confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"delete_folder\",\n          description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              folder_id: { type: \"string\", description: \"ID of the folder to delete\" },\n              confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n              force: { type: \"boolean\", description: \"Force delete even if folder has contents\" },\n            },\n            required: [\"folder_id\"],\n          },\n        },\n        {\n          name: \"sync\",\n          description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n          inputSchema: {\n            type: \"object\",\n            properties: {},\n          },\n        },\n      ],\n    }\n  })\n\n  // Register tool call handler\n  server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n    const toolName = request.params.name\n    const args = request.params.arguments || {}\n\n    try {\n      switch (toolName) {\n        case \"list_notebooks\": {\n          const listResult = await manager.listNotebooks()\n          return { content: [{ type: \"text\", text: listResult }], isError: false }\n        }\n\n        case \"search_notes\": {\n          const searchResult = await manager.searchNotes(args.query as string)\n          return { content: [{ type: \"text\", text: searchResult }], isError: false }\n        }\n\n        case \"read_notebook\": {\n          const notebookResult = await manager.readNotebook(args.notebook_id as string)\n          return { content: [{ type: \"text\", text: notebookResult }], isError: false }\n        }\n\n        case \"read_note\": {\n          const noteResult = await manager.readNote(args.note_id as string)\n          return { content: [{ type: \"text\", text: noteResult }], isError: false }\n        }\n\n        case \"read_multinote\": {\n          const multiResult = await manager.readMultiNote(args.note_ids as string[])\n          return { content: [{ type: \"text\", text: multiResult }], isError: false }\n        }\n\n        case \"create_note\": {\n          const createNoteResult = await manager.createNote(\n            args as {\n              title?: string | undefined\n              body?: string | undefined\n              body_html?: string | undefined\n              parent_id?: string | undefined\n              is_todo?: boolean | undefined\n              image_data_url?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: createNoteResult }], isError: false }\n        }\n\n        case \"create_folder\": {\n          const createFolderResult = await manager.createFolder(\n            args as {\n              title: string\n              parent_id?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: createFolderResult }], isError: false }\n        }\n\n        case \"edit_note\": {\n          const editNoteResult = await manager.editNote(\n            args as {\n              note_id: string\n              title?: string | undefined\n              body?: string | undefined\n              body_html?: string | undefined\n              parent_id?: string | undefined\n              is_todo?: boolean | undefined\n              todo_completed?: boolean | undefined\n              todo_due?: number | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: editNoteResult }], isError: false }\n        }\n\n        case \"edit_folder\": {\n          const editFolderResult = await manager.editFolder(\n            args as {\n              folder_id: string\n              title?: string | undefined\n              parent_id?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: editFolderResult }], isError: false }\n        }\n\n        case \"delete_note\": {\n          const deleteNoteResult = await manager.deleteNote(\n            args as {\n              note_id: string\n              confirm?: boolean | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: deleteNoteResult }], isError: false }\n        }\n\n        case \"delete_folder\": {\n          const deleteFolderResult = await manager.deleteFolder(\n            args as {\n              folder_id: string\n              confirm?: boolean | undefined\n              force?: boolean | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: deleteFolderResult }], isError: false }\n        }\n\n        case \"sync\": {\n          const syncResult = await manager.sync()\n          return { content: [{ type: \"text\", text: syncResult }], isError: false }\n        }\n\n        default:\n          throw new Error(`Unknown tool: ${toolName}`)\n      }\n    } catch (error) {\n      const errorMessage = error instanceof Error ? error.message : String(error)\n      return {\n        content: [{ type: \"text\", text: `Error: ${errorMessage}` }],\n        isError: true,\n      }\n    }\n  })\n\n  // Create logs directory if it doesn't exist\n  const __filename = fileURLToPath(import.meta.url)\n  const __dirname = path.dirname(__filename)\n  const logsDir = path.join(__dirname, \"..\", \"logs\")\n\n  if (!fs.existsSync(logsDir)) {\n    fs.mkdirSync(logsDir, { recursive: true })\n  }\n\n  // Create a log file for this session\n  const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n  const logFile = path.join(logsDir, `mcp-server-${timestamp}.log`)\n\n  // Create a custom transport wrapper to log commands and responses\n  class LoggingTransport extends StdioServerTransport {\n    private commandCounter: number\n\n    constructor() {\n      super()\n      this.commandCounter = 0\n    }\n\n    async sendMessage(message: unknown): Promise<void> {\n      const logEntry = {\n        timestamp: new Date().toISOString(),\n        direction: \"RESPONSE\",\n        message,\n      }\n\n      fs.appendFileSync(logFile, JSON.stringify(logEntry) + \"\\n\")\n\n      const parent = Object.getPrototypeOf(Object.getPrototypeOf(this))\n      return parent.sendMessage.call(this, message)\n    }\n\n    async handleMessage(message: unknown): Promise<void> {\n      this.commandCounter++\n      const logEntry = {\n        timestamp: new Date().toISOString(),\n        direction: \"COMMAND\",\n        commandNumber: this.commandCounter,\n        message,\n      }\n\n      fs.appendFileSync(logFile, JSON.stringify(logEntry) + \"\\n\")\n\n      const parent = Object.getPrototypeOf(Object.getPrototypeOf(this))\n      return parent.handleMessage.call(this, message)\n    }\n  }\n\n  const stdioTransport = new LoggingTransport()\n\n  try {\n    await server.connect(stdioTransport)\n    process.stderr.write(\"MCP server started and ready to receive commands\\n\")\n  } catch (error: unknown) {\n    process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`)\n    process.exit(1)\n  }\n}\n"],"mappings":"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"}
Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��(������i�^>��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��(������i�^>��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ����h�׫E�)�{

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ����h�׫E�)�{

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �ƴko_����^5o�\���w~��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �-0#�z�ެ�m���

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �-0#�z�ެ�m���

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: �-0#�z�ެ�m���

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: J�b�'���ӭ�즊�

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: J�b�'���ӭ�즊�

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ��^��'��m��-��%��d

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
highDO-BASunknownMedium ConfidenceLine 0

Decoded base64 content: ߟt{�tw���ƚ�������_�m��m�og�ӟ^�}5��{���ݽ^o��}�4��xӏ;�xwן{޵�͜�]��n9�g4�m;�f�s�y��u�[{��

Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (5.1 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (5.2 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (5.1 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.6 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.6 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.6 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.6 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.5 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumOB-001ObfuscationMedium ConfidenceLine 0

Possible Base64-encoded payload (long encoded string)

Detected by automated pattern matching (rule OB-001) with medium confidence. May be a false positive.

    100:     ]
    101:   },
>>> 102:   "packageManager": "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a"
    103: }
Report false positive
mediumNS-003Network SuspiciousMedium ConfidenceLine 0

JavaScript fetch() call

Detected by automated pattern matching (rule NS-003) with medium confidence. May be a false positive.

>>> 1: {"version":3,"file":"index.js","names":["crypto"],"sources":["../src/lib/joplin-sidecar.ts","../src/lib/parse-args.ts","../src/lib/joplin-api-client.ts","../src/lib/tools/base-tool.ts","../src/lib/tools/create-folder.ts","../src/lib/tools/create-note.ts","../src/lib/tools/delete-folder.ts","../src/lib/tools/delete-note.ts","../src/lib/tools/edit-folder.ts","../src/lib/tools/edit-note.ts","../src/lib/tools/list-notebooks.ts","../src/lib/tools/read-multi-note.ts","../src/lib/tools/read-note.ts","../src/lib/tools/read-notebook.ts","../src/lib/tools/search-notes.ts","../src/server-core.ts","../src/server-fastmcp.ts","../src/index.ts"],"sourcesContent":["import { type ChildProcess, exec, execFile, spawn } from \"child_process\"\nimport crypto from \"crypto\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { join } from \"path\"\nimport { promisify } from \"util\"\n\nconst execAsync = promisify(exec)\nconst execFileAsync = promisify(execFile)\n\nconst isWindows = process.platform === \"win32\"\nconst whichCmd = isWindows ? \"where\" : \"which\"\n\nexport const DEFAULT_API_PORT = 41184\nconst MAX_PORT_ATTEMPTS = 10\n\nexport type SyncTarget =\n  | { type: \"none\" }\n  | { type: \"filesystem\"; path: string }\n  | { type: \"joplin-cloud\"; email: string; password: string }\n  | { type: \"joplin-server\"; url: string; email: string; password: string }\n  | { type: \"webdav\"; url: string; username: string; password: string }\n  | { type: \"nextcloud\"; url: string; username: string; password: string }\n  | { type: \"s3\"; bucket: string; region: string; accessKey: string; secretKey: string }\n  | { type: \"dropbox\" }\n  | { type: \"onedrive\" }\n\nexport type SidecarConfig = {\n  profileDir: string\n  apiPort: number\n  apiToken: string\n  syncTarget?: SyncTarget\n  syncInterval?: number\n}\n\nexport type SidecarError = {\n  code:\n    | \"CLI_NOT_FOUND\"\n    | \"CONFIG_FAILED\"\n    | \"SPAWN_FAILED\"\n    | \"HEALTH_CHECK_FAILED\"\n    | \"STOP_FAILED\"\n    | \"SYNC_FAILED\"\n    | \"PORT_CONFLICT\"\n    | \"PORT_OCCUPIED\"\n    | \"PORT_EXHAUSTED\"\n  message: string\n  cause?: unknown\n}\n\nconst sidecarError = (code: SidecarError[\"code\"], message: string, cause?: unknown): SidecarError => ({\n  code,\n  message,\n  cause,\n})\n\nconst syncTargetId = (target: SyncTarget): number =>\n  Match(target.type)\n    .case(\"none\", () => 0)\n    .case(\"filesystem\", () => 2)\n    .case(\"webdav\", () => 6)\n    .case(\"nextcloud\", () => 5)\n    .case(\"dropbox\", () => 7)\n    .case(\"onedrive\", () => 3)\n    .case(\"s3\", () => 8)\n    .case(\"joplin-server\", () => 9)\n    .case(\"joplin-cloud\", () => 10)\n    .default(() => 0)\n\nconst fetchWithTimeout = async (url: string, timeoutMs: number = 5_000): Promise<Response> => {\n  const controller = new AbortController()\n  const timer = setTimeout(() => controller.abort(), timeoutMs)\n  try {\n    return await fetch(url, { signal: controller.signal })\n  } finally {\n    clearTimeout(timer)\n  }\n}\n\ntype PortProbeResult = \"free\" | \"joplin_ours\" | \"joplin_foreign\" | \"occupied_other\" | \"occupied_unresponsive\"\n\nconst isConnectionRefused = (err: unknown): boolean => {\n  const e = err as { cause?: { code?: string; errors?: Array<{ code?: string }> } }\n  if (e?.cause?.code === \"ECONNREFUSED\") return true\n  if (e?.cause?.errors?.some((inner) => inner?.code === \"ECONNREFUSED\")) return true\n  return false\n}\n\nconst probePort = async (port: number, token: string): Promise<PortProbeResult> => {\n  try {\n    const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`, 3_000)\n    const body = await pingResponse.text()\n    if (body !== \"JoplinClipperServer\") return \"occupied_other\"\n\n    // It's a Joplin server — check if the token matches\n    try {\n      const authResponse = await fetchWithTimeout(\n        `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n        3_000,\n      )\n      if (authResponse.ok) return \"joplin_ours\"\n      return \"joplin_foreign\"\n    } catch {\n      return \"joplin_foreign\"\n    }\n  } catch (err: unknown) {\n    // ECONNREFUSED = nothing listening on the port = genuinely free\n    if (isConnectionRefused(err)) return \"free\"\n    // Timeout or other error = port is occupied but not responding to HTTP\n    return \"occupied_unresponsive\"\n  }\n}\n\ntype PortResolution =\n  | { outcome: \"reuse_existing\"; port: number; desktopDetected: boolean }\n  | { outcome: \"free\"; port: number; desktopDetected: boolean }\n  | { outcome: \"exhausted\" }\n\nconst resolveAvailablePort = async (startPort: number, token: string): Promise<PortResolution> => {\n  let desktopDetected = false\n  for (let port = startPort; port < startPort + MAX_PORT_ATTEMPTS; port++) {\n    const status = await probePort(port, token)\n    if (status === \"free\") return { outcome: \"free\", port, desktopDetected }\n    if (status === \"joplin_ours\") return { outcome: \"reuse_existing\", port, desktopDetected }\n    if (status === \"joplin_foreign\") {\n      desktopDetected = true\n      process.stderr.write(`[joplin-sidecar] Port ${port}: Joplin Desktop detected (different token), skipping\\n`)\n    } else {\n      process.stderr.write(`[joplin-sidecar] Port ${port} occupied (${status}), trying next...\\n`)\n    }\n  }\n  return { outcome: \"exhausted\" }\n}\n\nconst findJoplinCli = async (): Promise<Either<SidecarError, string>> => {\n  // 1. User override via env var\n  const envCli = process.env.JOPLIN_CLI\n  if (envCli) {\n    if (fs.existsSync(envCli)) return Right(envCli)\n    return Left(sidecarError(\"CLI_NOT_FOUND\", `JOPLIN_CLI path not found: ${envCli}`))\n  }\n\n  // 2. Bundled in node_modules (if joplin is a dependency)\n  const localBin = join(process.cwd(), \"node_modules\", \".bin\", isWindows ? \"joplin.cmd\" : \"joplin\")\n  if (fs.existsSync(localBin)) return Right(localBin)\n\n  // 3. Global install\n  try {\n    const { stdout } = await execAsync(`${whichCmd} joplin`, { encoding: \"utf-8\", timeout: 10_000 })\n    const joplinPath = stdout.trim().split(\"\\n\")[0]\n    return Right(joplinPath)\n  } catch {\n    // not found\n  }\n\n  // 4. npx fallback (auto-downloads on first run)\n  try {\n    const { stdout } = await execAsync(`${whichCmd} npx`, { encoding: \"utf-8\", timeout: 10_000 })\n    const npxPath = stdout.trim().split(\"\\n\")[0]\n    process.stderr.write(\"[joplin-sidecar] No local joplin found, using npx (may download on first run)\\n\")\n    return Right(npxPath)\n  } catch {\n    // not found\n  }\n\n  return Left(\n    sidecarError(\n      \"CLI_NOT_FOUND\",\n      \"Joplin CLI not found. Install with: npm install -g joplin, or set JOPLIN_CLI=/path/to/joplin\",\n    ),\n  )\n}\n\nconst buildSettingsRecord = (config: SidecarConfig): Record<string, string> => {\n  const settings: Record<string, string> = {\n    \"api.token\": config.apiToken,\n    \"api.port\": String(config.apiPort),\n  }\n\n  const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n  settings[\"sync.target\"] = String(syncTargetId(syncTarget))\n\n  if (syncTarget.type === \"filesystem\") {\n    settings[\"sync.2.path\"] = syncTarget.path\n  } else if (syncTarget.type === \"webdav\") {\n    settings[\"sync.6.path\"] = syncTarget.url\n    settings[\"sync.6.username\"] = syncTarget.username\n    settings[\"sync.6.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"nextcloud\") {\n    settings[\"sync.5.path\"] = syncTarget.url\n    settings[\"sync.5.username\"] = syncTarget.username\n    settings[\"sync.5.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"s3\") {\n    settings[\"sync.8.path\"] = syncTarget.bucket\n    settings[\"sync.8.region\"] = syncTarget.region\n    settings[\"sync.8.username\"] = syncTarget.accessKey\n    settings[\"sync.8.password\"] = syncTarget.secretKey\n  } else if (syncTarget.type === \"joplin-server\") {\n    settings[\"sync.9.path\"] = syncTarget.url\n    settings[\"sync.9.username\"] = syncTarget.email\n    settings[\"sync.9.password\"] = syncTarget.password\n  } else if (syncTarget.type === \"joplin-cloud\") {\n    settings[\"sync.10.username\"] = syncTarget.email\n    settings[\"sync.10.password\"] = syncTarget.password\n  }\n\n  const interval = Option(config.syncInterval).orElse(300)\n  settings[\"sync.interval\"] = String(interval)\n\n  return settings\n}\n\nconst runJoplinConfig = async (cli: string, profileDir: string, key: string, value: string): Promise<void> => {\n  const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n  const args = cli.endsWith(\"npx\")\n    ? [\"joplin\", \"config\", \"--profile\", profileDir, key, value]\n    : [\"config\", \"--profile\", profileDir, key, value]\n  await execFileAsync(cmd, args, { encoding: \"utf-8\", timeout: 30_000, shell: isWindows })\n}\n\nconst configureJoplin = async (cli: string, config: SidecarConfig): Promise<Either<SidecarError, void>> => {\n  const settings = buildSettingsRecord(config)\n  try {\n    fs.mkdirSync(config.profileDir, { recursive: true })\n    for (const [key, value] of Object.entries(settings)) {\n      await runJoplinConfig(cli, config.profileDir, key, value)\n    }\n    return Right(undefined as void)\n  } catch (e) {\n    return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to configure Joplin via CLI\", e))\n  }\n}\n\nconst spawnServer = (cli: string, config: SidecarConfig): Either<SidecarError, ChildProcess> => {\n  try {\n    const cmd = cli.endsWith(\"npx\") ? \"npx\" : cli\n    const args = cli.endsWith(\"npx\")\n      ? [\"joplin\", \"server\", \"start\", \"--profile\", config.profileDir]\n      : [\"server\", \"start\", \"--profile\", config.profileDir]\n\n    const proc = spawn(cmd, args, {\n      stdio: [\"ignore\", \"pipe\", \"pipe\"],\n      detached: false,\n      shell: isWindows,\n    })\n\n    proc.stderr?.on(\"data\", (data: Buffer) => {\n      process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n    })\n\n    proc.stdout?.on(\"data\", (data: Buffer) => {\n      process.stderr.write(`[joplin-sidecar] ${data.toString()}`)\n    })\n\n    proc.on(\"error\", (err) => {\n      process.stderr.write(`[joplin-sidecar] Process error: ${err.message}\\n`)\n    })\n\n    return Right(proc as ChildProcess)\n  } catch (e) {\n    return Left(sidecarError(\"SPAWN_FAILED\", \"Failed to spawn Joplin server process\", e))\n  }\n}\n\nconst waitForReady = async (\n  port: number,\n  token: string,\n  proc: ChildProcess | null,\n  maxRetries: number = 30,\n  intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n  const deadline = Date.now() + 60_000\n\n  // Track if the spawned process exits early (e.g., port already taken)\n  let procExitCode: number | null = null\n  if (proc) {\n    proc.once(\"exit\", (code) => {\n      procExitCode = code\n    })\n  }\n\n  for (let i = 0; i < maxRetries; i++) {\n    if (Date.now() > deadline) break\n\n    if (procExitCode !== null) {\n      return Left(\n        sidecarError(\n          \"SPAWN_FAILED\",\n          `Joplin server process exited unexpectedly (code ${procExitCode}). ` +\n            `Port ${port} may already be in use by another Joplin instance or process.`,\n        ),\n      )\n    }\n\n    try {\n      const pingResponse = await fetchWithTimeout(`http://127.0.0.1:${port}/ping`)\n      if (pingResponse.ok) {\n        // Verify auth with token\n        try {\n          const authResponse = await fetchWithTimeout(\n            `http://127.0.0.1:${port}/folders?token=${encodeURIComponent(token)}&limit=1`,\n          )\n          if (authResponse.ok) {\n            process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n            return Right(true as const)\n          }\n          process.stderr.write(\n            `[joplin-sidecar] Ping OK but auth failed (status ${authResponse.status}), retrying...\\n`,\n          )\n        } catch {\n          process.stderr.write(\"[joplin-sidecar] Ping OK but auth check timed out, retrying...\\n\")\n        }\n      }\n    } catch {\n      // Not ready yet\n    }\n    await new Promise((resolve) => setTimeout(resolve, intervalMs))\n  }\n\n  return Left(sidecarError(\"HEALTH_CHECK_FAILED\", \"Joplin server did not become ready within 60s\"))\n}\n\nconst CONFIG_CACHE_FILE = \".mcp-configured\"\n\nconst computeConfigHash = (config: SidecarConfig): string => {\n  const hashData = {\n    apiPort: config.apiPort,\n    apiToken: config.apiToken,\n    syncTarget: config.syncTarget,\n    syncInterval: config.syncInterval,\n  }\n  return crypto.createHash(\"sha256\").update(JSON.stringify(hashData)).digest(\"hex\").slice(0, 16)\n}\n\nconst isConfigCached = (profileDir: string, hash: string): boolean => {\n  try {\n    const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n    if (!fs.existsSync(cachePath)) return false\n    const cached = JSON.parse(fs.readFileSync(cachePath, \"utf-8\"))\n    return cached.hash === hash\n  } catch {\n    return false\n  }\n}\n\nconst writeConfigCache = (profileDir: string, hash: string): void => {\n  try {\n    const cachePath = join(profileDir, CONFIG_CACHE_FILE)\n    fs.writeFileSync(cachePath, JSON.stringify({ hash, timestamp: Date.now() }))\n  } catch {\n    // Non-critical — skip silently\n  }\n}\n\nexport class JoplinSidecar {\n  private config: SidecarConfig\n  private childProcess: ChildProcess | null = null\n  private startPromise: Promise<Either<SidecarError, ChildProcess>> | null = null\n  private portResolution: PortResolution | null = null\n\n  constructor(config: Partial<SidecarConfig> & { apiToken: string }) {\n    this.config = {\n      profileDir: config.profileDir ?? join(os.homedir(), \".config\", \"joplin-mcp\"),\n      apiPort: config.apiPort ?? DEFAULT_API_PORT,\n      apiToken: config.apiToken,\n      syncTarget: config.syncTarget,\n      syncInterval: config.syncInterval,\n    }\n  }\n\n  async resolvePort(): Promise<Either<SidecarError, number>> {\n    const resolution = await resolveAvailablePort(this.config.apiPort, this.config.apiToken)\n    this.portResolution = resolution\n    if (resolution.outcome === \"exhausted\") {\n      const lastPort = this.config.apiPort + MAX_PORT_ATTEMPTS - 1\n      return Left(\n        sidecarError(\n          \"PORT_EXHAUSTED\",\n          `All ports ${this.config.apiPort}-${lastPort} are occupied. Free a port or stop other Joplin instances.`,\n        ),\n      )\n    }\n    this.config.apiPort = resolution.port\n    if (resolution.desktopDetected) {\n      process.stderr.write(\n        \"[joplin-sidecar] WARNING: Joplin Desktop is running. The sidecar uses a separate database.\\n\" +\n          \"[joplin-sidecar] Notes will sync between them only if both are configured with the same sync target.\\n\",\n      )\n    }\n    return Right(resolution.port)\n  }\n\n  isDesktopDetected(): boolean {\n    return this.portResolution?.outcome !== \"exhausted\" && (this.portResolution?.desktopDetected ?? false)\n  }\n\n  async start(): Promise<Either<SidecarError, ChildProcess>> {\n    if (this.startPromise) return this.startPromise\n    this.startPromise = this.doStart()\n    return this.startPromise\n  }\n\n  private async doStart(): Promise<Either<SidecarError, ChildProcess>> {\n    // Step 1: Find CLI\n    const cliResult = await findJoplinCli()\n    if (Either.isLeft(cliResult)) {\n      return Left(\n        cliResult.fold(\n          (e) => e,\n          () => null as never,\n        ),\n      )\n    }\n    const cli = cliResult.fold(\n      () => \"\",\n      (v) => v,\n    )\n    process.stderr.write(`[joplin-sidecar] Found CLI: ${cli}\\n`)\n\n    // Step 2: Resolve port if not already done (defensive fallback)\n    if (!this.portResolution) {\n      const portResult = await this.resolvePort()\n      if (Either.isLeft(portResult)) {\n        return Left(\n          portResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n    }\n\n    // Step 3: If we found an existing instance with our token, reuse it\n    if (this.portResolution?.outcome === \"reuse_existing\") {\n      process.stderr.write(\n        `[joplin-sidecar] Existing Joplin server with matching token on port ${this.config.apiPort}, reusing\\n`,\n      )\n      return Right(this.childProcess ?? (null as unknown as ChildProcess))\n    }\n\n    // Step 4: Configure via CLI (skip if config is cached)\n    const configHash = computeConfigHash(this.config)\n    if (isConfigCached(this.config.profileDir, configHash)) {\n      process.stderr.write(\"[joplin-sidecar] Configuration cached, skipping config step\\n\")\n    } else {\n      const configResult = await configureJoplin(cli, this.config)\n      if (Either.isLeft(configResult)) {\n        return Left(\n          configResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n      writeConfigCache(this.config.profileDir, configHash)\n      process.stderr.write(\"[joplin-sidecar] Configuration applied via CLI\\n\")\n    }\n\n    // Step 5: Spawn server (port is free, skip if already spawned from a previous attempt)\n    if (!this.childProcess) {\n      const spawnResult = spawnServer(cli, this.config)\n      if (Either.isLeft(spawnResult)) {\n        return Left(\n          spawnResult.fold(\n            (e) => e,\n            () => null as never,\n          ),\n        )\n      }\n      const proc = spawnResult.fold(\n        () => null as unknown as ChildProcess,\n        (v) => v,\n      )\n      this.childProcess = proc\n      process.stderr.write(`[joplin-sidecar] Server spawned (pid: ${proc.pid})\\n`)\n    } else {\n      process.stderr.write(\n        `[joplin-sidecar] Server already spawned (pid: ${this.childProcess.pid}), waiting for ready\\n`,\n      )\n    }\n\n    // Step 6: Wait for ready\n    const readyResult = await waitForReady(this.config.apiPort, this.config.apiToken, this.childProcess)\n    if (Either.isLeft(readyResult)) {\n      return Left(\n        readyResult.fold(\n          (e) => e,\n          () => null as never,\n        ),\n      )\n    }\n\n    return Right(this.childProcess!)\n  }\n\n  async stop(): Promise<Either<Error, true>> {\n    // Reset startPromise to allow retry via start() after stop()\n    this.startPromise = null\n\n    if (!this.childProcess) return Right(true as const)\n\n    const proc = this.childProcess\n    try {\n      proc.kill(\"SIGTERM\")\n\n      await new Promise<void>((resolve) => {\n        const timeout = setTimeout(() => {\n          proc.kill(\"SIGKILL\")\n          resolve()\n        }, 5000)\n\n        proc.on(\"exit\", () => {\n          clearTimeout(timeout)\n          resolve()\n        })\n      })\n\n      this.childProcess = null\n      process.stderr.write(\"[joplin-sidecar] Server stopped\\n\")\n      return Right(true as const)\n    } catch (error) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async healthCheck(): Promise<Either<Error, true>> {\n    try {\n      const response = await fetchWithTimeout(`http://127.0.0.1:${this.config.apiPort}/ping`, 5_000)\n      if (!response.ok) return Left(new Error(`Health check failed: ${response.status}`))\n      return Right(true as const)\n    } catch (error) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  getPort(): number {\n    return this.config.apiPort\n  }\n\n  getHost(): string {\n    return \"127.0.0.1\"\n  }\n}\n","import fs from \"fs\"\nimport { Either, Left, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { join, resolve } from \"path\"\n\nimport type { SyncTarget } from \"./joplin-sidecar.js\"\n\nexport type ParsedArgs = {\n  remainingArgs: string[]\n  transport: \"stdio\" | \"http\"\n  httpPort: number\n  profileDir: string\n  syncTarget: Option<SyncTarget>\n}\n\nconst expandVars = (p: string): string =>\n  p\n    .replace(/\\$\\{(\\w+)\\}/g, (_, name) => process.env[name] ?? \"\")\n    .replace(/\\$(\\w+)/g, (_, name) => process.env[name] ?? \"\")\n\nconst isWsl = (() => {\n  try {\n    return fs.readFileSync(\"/proc/version\", \"utf-8\").toLowerCase().includes(\"microsoft\")\n  } catch {\n    return false\n  }\n})()\n\nconst isNonEmptyDir = (p: string): boolean => {\n  try {\n    const entries = fs.readdirSync(p)\n    return entries.length > 0\n  } catch {\n    return false\n  }\n}\n\nconst resolveWslPath = (linuxPath: string, relativeToHome: string): string => {\n  if (!isWsl) return linuxPath\n  if (fs.existsSync(linuxPath) && isNonEmptyDir(linuxPath)) return linuxPath\n  try {\n    const usersDir = \"/mnt/c/Users\"\n    const users = fs\n      .readdirSync(usersDir)\n      .filter((u) => ![\"Public\", \"Default\", \"Default User\", \"All Users\"].includes(u))\n    for (const user of users) {\n      const winPath = join(usersDir, user, relativeToHome)\n      if (isNonEmptyDir(winPath)) {\n        process.stderr.write(`[wsl] Path ${linuxPath} empty/missing, using Windows path: ${winPath}\\n`)\n        return winPath\n      }\n    }\n  } catch {\n    // /mnt/c not accessible — fall through\n  }\n  return linuxPath\n}\n\nconst expandPath = (p: string): string => {\n  const expanded = expandVars(p)\n  if (expanded.startsWith(\"~/\") || expanded === \"~\") {\n    const linuxPath = expanded.replace(\"~\", os.homedir())\n    const relativeToHome = expanded.slice(2) // strip ~/\n    return resolveWslPath(linuxPath, relativeToHome)\n  }\n  return expanded\n}\n\nconst extractArg = (args: string[], flag: string): Option<string> => {\n  const index = args.indexOf(flag)\n  if (index === -1) return Option.none()\n  const value = args[index + 1]\n  if (!value || value.startsWith(\"--\")) return Option.none()\n  args.splice(index, 2)\n  return Option(value)\n}\n\nexport const buildSyncTarget = (args: {\n  syncTarget: Option<string>\n  syncPath: Option<string>\n  syncUsername: Option<string>\n  syncPassword: Option<string>\n}): Either<string, SyncTarget> => {\n  const targetType = args.syncTarget.orElse(\"none\")\n\n  switch (targetType) {\n    case \"none\":\n      return Right({ type: \"none\" } as SyncTarget)\n\n    case \"filesystem\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for filesystem sync target\"),\n        (path) => Right({ type: \"filesystem\", path } as SyncTarget),\n      )\n\n    case \"webdav\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for webdav sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for webdav sync target\"),\n            (username) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for webdav sync target\"),\n                (password) => Right({ type: \"webdav\", url, username, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"nextcloud\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for nextcloud sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for nextcloud sync target\"),\n            (username) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for nextcloud sync target\"),\n                (password) => Right({ type: \"nextcloud\", url, username, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"joplin-cloud\":\n      return args.syncUsername.fold(\n        () => Left(\"--sync-username required for joplin-cloud sync target\"),\n        (email) =>\n          args.syncPassword.fold(\n            () => Left(\"--sync-password required for joplin-cloud sync target\"),\n            (password) => Right({ type: \"joplin-cloud\", email, password } as SyncTarget),\n          ),\n      )\n\n    case \"joplin-server\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path required for joplin-server sync target\"),\n        (url) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username required for joplin-server sync target\"),\n            (email) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password required for joplin-server sync target\"),\n                (password) => Right({ type: \"joplin-server\", url, email, password } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"s3\":\n      return args.syncPath.fold(\n        () => Left(\"--sync-path (bucket) required for s3 sync target\"),\n        (bucket) =>\n          args.syncUsername.fold(\n            () => Left(\"--sync-username (access key) required for s3 sync target\"),\n            (accessKey) =>\n              args.syncPassword.fold(\n                () => Left(\"--sync-password (secret key) required for s3 sync target\"),\n                (secretKey) => Right({ type: \"s3\", bucket, region: \"us-east-1\", accessKey, secretKey } as SyncTarget),\n              ),\n          ),\n      )\n\n    case \"dropbox\":\n      return Right({ type: \"dropbox\" } as SyncTarget)\n\n    case \"onedrive\":\n      return Right({ type: \"onedrive\" } as SyncTarget)\n\n    default:\n      return Left(\n        `Unknown sync target: ${targetType}. Valid targets: none, filesystem, webdav, nextcloud, joplin-cloud, joplin-server, s3, dropbox, onedrive`,\n      )\n  }\n}\n\nfunction parseArgs(): ParsedArgs {\n  const args = process.argv.slice(2)\n  let transport: \"stdio\" | \"http\" = \"stdio\"\n  let httpPort = 3000\n\n  // Load environment variables without dotenv debug output (for MCP stdio compatibility)\n  const loadEnvFile = (envPath: string) => {\n    try {\n      if (fs.existsSync(envPath)) {\n        process.stderr.write(`Loading environment from: ${envPath}\\n`)\n        const envContent = fs.readFileSync(envPath, \"utf-8\")\n        const envLines = envContent.split(\"\\n\")\n        const loadedVars: string[] = []\n\n        for (const line of envLines) {\n          const trimmedLine = line.trim()\n          if (trimmedLine && !trimmedLine.startsWith(\"#\")) {\n            const [key, ...valueParts] = trimmedLine.split(\"=\")\n            if (key && valueParts.length > 0) {\n              const value = valueParts.join(\"=\").replace(/^[\"']|[\"']$/g, \"\")\n              if (!process.env[key.trim()]) {\n                process.env[key.trim()] = value\n                loadedVars.push(key.trim())\n              }\n            }\n          }\n        }\n\n        if (loadedVars.length > 0) {\n          process.stderr.write(`Loaded variables: ${loadedVars.join(\", \")}\\n`)\n        }\n      }\n    } catch (error: unknown) {\n      process.stderr.write(`Error loading environment file: ${error}\\n`)\n    }\n  }\n\n  // Handle --env-file\n  const envFile = extractArg(args, \"--env-file\")\n  envFile.fold(\n    () => loadEnvFile(\".env\"),\n    (file) => loadEnvFile(resolve(process.cwd(), file)),\n  )\n\n  // Handle --token\n  extractArg(args, \"--token\").fold(\n    () => {},\n    (token) => {\n      process.env.JOPLIN_TOKEN = token\n    },\n  )\n\n  // Handle --transport\n  extractArg(args, \"--transport\").fold(\n    () => {},\n    (value) => {\n      if (value !== \"stdio\" && value !== \"http\") {\n        process.stderr.write(\"Error: --transport must be either 'stdio' or 'http'\\n\")\n        process.exit(1)\n      }\n      transport = value as \"stdio\" | \"http\"\n    },\n  )\n\n  // Handle --http-port\n  extractArg(args, \"--http-port\").fold(\n    () => {},\n    (value) => {\n      const parsed = parseInt(value, 10)\n      if (isNaN(parsed) || parsed < 1 || parsed > 65535) {\n        process.stderr.write(\"Error: --http-port must be a valid port number (1-65535)\\n\")\n        process.exit(1)\n      }\n      httpPort = parsed\n    },\n  )\n\n  // Handle --profile\n  const profileDir = extractArg(args, \"--profile\")\n    .or(Option(process.env.JOPLIN_PROFILE))\n    .map(expandPath)\n    .orElse(`${os.homedir()}/.config/joplin-mcp`)\n\n  // Handle sync args\n  const syncTarget = extractArg(args, \"--sync-target\").or(Option(process.env.JOPLIN_SYNC_TARGET))\n  const syncPath = extractArg(args, \"--sync-path\").or(Option(process.env.JOPLIN_SYNC_PATH)).map(expandPath)\n  const syncUsername = extractArg(args, \"--sync-username\").or(Option(process.env.JOPLIN_SYNC_USERNAME))\n  const syncPassword = extractArg(args, \"--sync-password\").or(Option(process.env.JOPLIN_SYNC_PASSWORD))\n\n  // Build and validate sync target\n  const syncResult = buildSyncTarget({ syncTarget, syncPath, syncUsername, syncPassword })\n  if (Either.isLeft(syncResult)) {\n    const err = syncResult.fold(\n      (e) => e,\n      () => \"\",\n    )\n    process.stderr.write(`Error: ${err}\\n`)\n    process.exit(1)\n  }\n  const syncTargetValue = syncResult.fold(\n    () => ({ type: \"none\" }) as SyncTarget,\n    (v) => v,\n  )\n  const resolvedSyncTarget: Option<SyncTarget> =\n    syncTargetValue.type === \"none\" ? Option.none<SyncTarget>() : Option(syncTargetValue as SyncTarget)\n\n  // Handle --help\n  if (args.includes(\"--help\") || args.includes(\"-h\")) {\n    process.stderr.write(`\nJoplin MCP Server (Sidecar Mode)\n\nUSAGE:\n  joplin-mcp-server [OPTIONS]\n\nOPTIONS:\n  --env-file <file>          Load environment variables from file\n  --token <token>            Joplin API token\n  --transport <type>         Transport type: stdio (default) or http\n  --http-port <port>         HTTP server port (default: 3000, only with --transport http)\n  --profile <dir>            Joplin data directory (default: ~/.config/joplin-mcp)\n  --sync-target <type>       Sync target: none, filesystem, webdav, nextcloud,\n                             joplin-cloud, joplin-server, s3, dropbox, onedrive\n  --sync-path <url>          URL or path for sync target\n  --sync-username <user>     Username/email for sync\n  --sync-password <pass>     Password for sync\n  --help, -h                 Show this help message\n\nENVIRONMENT VARIABLES:\n  JOPLIN_TOKEN               Joplin API token (required)\n  JOPLIN_HOST                Connect to existing Joplin at this host (skips sidecar)\n  JOPLIN_PORT                Connect to existing Joplin on this port (skips sidecar)\n  JOPLIN_CLI                 Path to joplin CLI binary (overrides auto-detection)\n  JOPLIN_PROFILE             Joplin data directory\n  JOPLIN_SYNC_TARGET         Sync target type\n  JOPLIN_SYNC_PATH           Sync target URL/path\n  JOPLIN_SYNC_USERNAME       Sync username/email\n  JOPLIN_SYNC_PASSWORD       Sync password\n  LOG_LEVEL                  Log level: debug, info, warn, error (default: info)\n\nMODES:\n  Sidecar (default):\n    Spawns and manages its own Joplin Terminal process.\n    No Joplin desktop app or Web Clipper needed.\n    Uses an isolated profile at --profile path (default: ~/.config/joplin-mcp).\n\n  External (JOPLIN_HOST/JOPLIN_PORT set):\n    Connects directly to an existing Joplin instance.\n    Useful for WSL connecting to Windows Joplin desktop.\n\nEXAMPLES:\n  # Minimal - local notes, no sync\n  joplin-mcp-server --token my_token\n\n  # Joplin Cloud sync\n  joplin-mcp-server --token my_token \\\\\n    --sync-target joplin-cloud \\\\\n    --sync-username user@example.com --sync-password pass\n\n  # WebDAV sync\n  joplin-mcp-server --token my_token \\\\\n    --sync-target webdav \\\\\n    --sync-path https://dav.example.com/joplin \\\\\n    --sync-username user --sync-password pass\n\n  # Filesystem sync (Syncthing, NAS)\n  joplin-mcp-server --token my_token \\\\\n    --sync-target filesystem --sync-path /mnt/sync/joplin\n\n  # HTTP transport for web apps\n  joplin-mcp-server --token my_token --transport http --http-port 3000\n\n  # External mode - connect to existing Joplin (e.g. Windows desktop from WSL)\n  JOPLIN_HOST=172.x.x.x JOPLIN_PORT=41184 joplin-mcp-server --token my_token\n\nFind your Joplin token in: Tools > Options > Web Clipper\n`)\n    process.exit(0)\n  }\n\n  return {\n    remainingArgs: args,\n    transport,\n    httpPort,\n    profileDir,\n    syncTarget: resolvedSyncTarget,\n  }\n}\n\nexport default parseArgs\n","import axios, { type AxiosResponse } from \"axios\"\nimport { Either, Left, Right } from \"functype\"\n\ntype JoplinAPIClientConfig = {\n  host?: string\n  port?: number\n  token: string\n}\n\ntype JoplinAPIResponse<T = unknown> = {\n  items: T[]\n  has_more: boolean\n}\n\ntype RequestOptions = {\n  query?: Record<string, unknown>\n  [key: string]: unknown\n}\n\nclass JoplinAPIClient {\n  private readonly baseURL: string\n  private readonly token: string\n\n  constructor({ host = \"127.0.0.1\", port = 41184, token }: JoplinAPIClientConfig) {\n    this.baseURL = `http://${host}:${port}`\n    this.token = token\n  }\n\n  async serviceAvailable(): Promise<Either<Error, true>> {\n    try {\n      const response: AxiosResponse<string> = await axios.get(`${this.baseURL}/ping`, { timeout: 5_000 })\n      if (response.status === 200 && response.data === \"JoplinClipperServer\") {\n        return Right(true as const)\n      }\n      return Left(new Error(\"Unexpected response from Joplin ping\"))\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async getAllItems<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T[]>> {\n    let page = 1\n    const items: T[] = []\n\n    try {\n      while (true) {\n        const result = await this.get<JoplinAPIResponse<T>>(\n          path,\n          this.mergeRequestOptions(options, { query: { page } }),\n        )\n\n        const response = result.fold(\n          (err) => {\n            throw err\n          },\n          (data) => data,\n        )\n\n        if (!response || typeof response !== \"object\" || !Array.isArray(response.items)) {\n          return Left(new Error(`Unexpected response format from Joplin API for path: ${path}`))\n        }\n\n        items.push(...response.items)\n        page += 1\n\n        if (!response.has_more) break\n      }\n\n      return Right(items)\n    } catch (error: unknown) {\n      process.stderr.write(`Error in getAllItems for path ${path}: ${error}\\n`)\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async get<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.get(`${this.baseURL}${path}`, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async post<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.post(`${this.baseURL}${path}`, body, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async delete<T = unknown>(path: string, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.delete(`${this.baseURL}${path}`, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  async put<T = unknown>(path: string, body: unknown, options: RequestOptions = {}): Promise<Either<Error, T>> {\n    try {\n      const { data }: AxiosResponse<T> = await axios.put(`${this.baseURL}${path}`, body, {\n        params: this.requestOptions(options).query,\n        timeout: 30_000,\n      })\n      return Right(data)\n    } catch (error: unknown) {\n      return Left(error instanceof Error ? error : new Error(String(error)))\n    }\n  }\n\n  private requestOptions(options: RequestOptions = {}): RequestOptions {\n    return this.mergeRequestOptions(\n      {\n        query: { token: this.token },\n      },\n      options,\n    )\n  }\n\n  private mergeRequestOptions(options1: RequestOptions, options2: RequestOptions): RequestOptions {\n    return {\n      query: {\n        ...(options1.query || {}),\n        ...(options2.query || {}),\n      },\n      ...this.except(options1, \"query\"),\n      ...this.except(options2, \"query\"),\n    }\n  }\n\n  private except(obj: Record<string, unknown>, key: string): Record<string, unknown> {\n    const result = { ...obj }\n    delete result[key]\n    return result\n  }\n}\n\nexport default JoplinAPIClient\nexport type { JoplinAPIClientConfig, JoplinAPIResponse, RequestOptions }\n","import { type Either } from \"functype\"\n\nimport JoplinAPIClient from \"../joplin-api-client.js\"\n\ntype JoplinFolder = {\n  id: string\n  title: string\n  parent_id?: string\n}\n\ntype JoplinNote = {\n  id: string\n  title: string\n  body?: string\n  parent_id?: string\n  created_time: number\n  updated_time: number\n  is_todo: boolean\n  todo_completed?: boolean\n  todo_due?: number\n}\n\nabstract class BaseTool {\n  protected apiClient: JoplinAPIClient\n\n  constructor(apiClient: JoplinAPIClient) {\n    this.apiClient = apiClient\n  }\n\n  abstract call(...args: any[]): Promise<string>\n\n  protected formatError(error: any, context: string): string {\n    process.stderr.write(`${context} error: ${error}\\n`)\n    return `Error ${context.toLowerCase()}: ${error.message || \"Unknown error\"}`\n  }\n\n  protected validateId(id: string, type: \"note\" | \"notebook\"): string | null {\n    if (!id) {\n      return `Please provide a ${type} ID. Example: ${type === \"note\" ? \"read_note\" : \"read_notebook\"} ${type}_id=\"your-${type}-id\"`\n    }\n\n    if (id.length < 10 || !id.match(/[a-f0-9]/i)) {\n      const searchHint = type === \"note\" ? \"search_notes\" : \"list_notebooks\"\n      return `Error: \"${id}\" does not appear to be a valid ${type} ID. \\n\\n${type.charAt(0).toUpperCase() + type.slice(1)} IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse ${searchHint} to ${type === \"note\" ? \"find notes\" : \"see all available notebooks\"} and their IDs.`\n    }\n\n    return null\n  }\n\n  protected unwrap<T>(result: Either<Error, T>): T {\n    return result.fold(\n      (err) => {\n        throw err\n      },\n      (val) => val,\n    )\n  }\n\n  protected formatDate(timestamp: number): string {\n    return new Date(timestamp).toLocaleString()\n  }\n}\n\nexport default BaseTool\nexport type { JoplinFolder, JoplinNote }\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface CreateFolderOptions {\n  title: string\n  parent_id?: string | undefined\n}\n\ninterface CreateFolderResponse extends JoplinFolder {\n  created_time: number\n  updated_time: number\n}\n\nclass CreateFolder extends BaseTool {\n  async call(options: CreateFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder creation options. Example: create_folder {\"title\": \"My Notebook\"}'\n    }\n\n    // Validate required title\n    if (!options.title || typeof options.title !== \"string\" || options.title.trim() === \"\") {\n      return 'Please provide a title for the folder/notebook. Example: create_folder {\"title\": \"My Notebook\"}'\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n      return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n    }\n\n    try {\n      // Prepare the request body\n      const requestBody: CreateFolderOptions = {\n        title: options.title.trim(),\n      }\n\n      if (options.parent_id) {\n        requestBody.parent_id = options.parent_id\n      }\n\n      // Create the folder\n      const createdFolder = this.unwrap(await this.apiClient.post<CreateFolderResponse>(\"/folders\", requestBody))\n\n      // Validate response\n      if (!createdFolder || typeof createdFolder !== \"object\" || !createdFolder.id) {\n        return \"Error: Unexpected response format from Joplin API when creating folder\"\n      }\n\n      // Get parent notebook info if available\n      let parentInfo = \"Top level\"\n      if (createdFolder.parent_id) {\n        try {\n          const parentNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${createdFolder.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (parentNotebook && parentNotebook.title) {\n            parentInfo = `Inside \"${parentNotebook.title}\" (notebook_id: \"${createdFolder.parent_id}\")`\n          }\n        } catch {\n          // Continue even if we can't get parent info\n          parentInfo = `Parent notebook ID: ${createdFolder.parent_id}`\n        }\n      }\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully created notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Notebook Details:`)\n      resultLines.push(`   Title: \"${createdFolder.title}\"`)\n      resultLines.push(`   Notebook ID: ${createdFolder.id}`)\n      resultLines.push(`   Location: ${parentInfo}`)\n\n      const createdDate = this.formatDate(createdFolder.created_time)\n      resultLines.push(`   Created: ${createdDate}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - View notebook: read_notebook notebook_id=\"${createdFolder.id}\"`)\n      resultLines.push(`   - Create a note in it: create_note {\"title\": \"My Note\", \"parent_id\": \"${createdFolder.id}\"}`)\n      resultLines.push(`   - View all notebooks: list_notebooks`)\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        // Handle specific API errors\n        if (error.response.status === 400) {\n          return `Error creating notebook: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Parent notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs, or omit parent_id to create a top-level notebook.`\n        }\n        if (error.response.status === 409) {\n          return `Error: A notebook with the title \"${options.title}\" might already exist in this location.\\n\\nTry a different title or check existing notebooks with list_notebooks.`\n        }\n      }\n      return this.formatError(error, \"creating notebook\")\n    }\n  }\n}\n\nexport default CreateFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface CreateNoteOptions {\n  title?: string | undefined\n  body?: string | undefined\n  body_html?: string | undefined\n  parent_id?: string | undefined\n  is_todo?: boolean | undefined\n  image_data_url?: string | undefined\n}\n\ntype CreateNoteResponse = JoplinNote\n\nclass CreateNote extends BaseTool {\n  async call(options: CreateNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note creation options. Example: create_note {\"title\": \"My Note\", \"body\": \"Note content\"}'\n    }\n\n    // Validate that we have at least a title or body\n    if (!options.title && !options.body && !options.body_html) {\n      return \"Please provide at least a title, body, or body_html for the note.\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id && (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i))) {\n      return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n    }\n\n    try {\n      // Prepare the request body\n      const requestBody: CreateNoteOptions = {}\n\n      if (options.title) requestBody.title = options.title\n      if (options.body) requestBody.body = options.body\n      if (options.body_html) requestBody.body_html = options.body_html\n      if (options.parent_id) requestBody.parent_id = options.parent_id\n      if (options.is_todo !== undefined) requestBody.is_todo = options.is_todo\n      if (options.image_data_url) requestBody.image_data_url = options.image_data_url\n\n      // Create the note\n      const createdNote = this.unwrap(await this.apiClient.post<CreateNoteResponse>(\"/notes\", requestBody))\n\n      // Validate response\n      if (!createdNote || typeof createdNote !== \"object\" || !createdNote.id) {\n        return \"Error: Unexpected response format from Joplin API when creating note\"\n      }\n\n      // Get notebook info if available\n      let notebookInfo = \"Root level\"\n      if (createdNote.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${createdNote.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (notebook && notebook.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${createdNote.parent_id}\")`\n          }\n        } catch {\n          // Continue even if we can't get notebook info\n          notebookInfo = `Notebook ID: ${createdNote.parent_id}`\n        }\n      }\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully created note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Note Details:`)\n      resultLines.push(`   Title: \"${createdNote.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${createdNote.id}`)\n      resultLines.push(`   Location: ${notebookInfo}`)\n\n      if (createdNote.is_todo) {\n        resultLines.push(`   Type: Todo item`)\n      }\n\n      const createdDate = this.formatDate(createdNote.created_time)\n      resultLines.push(`   Created: ${createdDate}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - Read the note: read_note note_id=\"${createdNote.id}\"`)\n      if (createdNote.parent_id) {\n        resultLines.push(`   - View notebook: read_notebook notebook_id=\"${createdNote.parent_id}\"`)\n      }\n      resultLines.push(`   - Search for it: search_notes query=\"${createdNote.title}\"`)\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        // Handle specific API errors\n        if (error.response.status === 400) {\n          return `Error creating note: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n        }\n      }\n      return this.formatError(error, \"creating note\")\n    }\n  }\n}\n\nexport default CreateNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface DeleteFolderOptions {\n  folder_id: string\n  confirm?: boolean | undefined\n  force?: boolean | undefined\n}\n\ninterface FolderContents {\n  items: any[]\n}\n\nclass DeleteFolder extends BaseTool {\n  async call(options: DeleteFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    // Validate required folder_id\n    if (!options.folder_id) {\n      return 'Please provide folder deletion options. Example: delete_folder {\"folder_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    const folderIdError = this.validateId(options.folder_id, \"notebook\")\n    if (folderIdError) {\n      return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n    }\n\n    // Require explicit confirmation for safety\n    if (!options.confirm) {\n      return `⚠️  This will permanently delete the notebook/folder!\\n\\nTo confirm deletion, use:\\ndelete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true}\\n\\n⚠️  This action cannot be undone!`\n    }\n\n    try {\n      // First, get the folder details to show what's being deleted\n      const folderToDelete = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      if (!folderToDelete || !folderToDelete.id) {\n        return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n      }\n\n      // Check if folder contains notes or subfolders\n      const [notes, subfolders] = await Promise.all([\n        this.apiClient\n          .get<FolderContents>(`/folders/${options.folder_id}/notes`, {\n            query: { fields: \"id,title\" },\n          })\n          .then((result) =>\n            result.fold(\n              () => ({ items: [] }),\n              (data) => data,\n            ),\n          ),\n        this.apiClient\n          .get<FolderContents>(\"/folders\", {\n            query: { fields: \"id,title,parent_id\" },\n          })\n          .then((result) =>\n            result.fold(\n              () => ({ items: [] }),\n              (response) => ({\n                items: response.items?.filter((folder: any) => folder.parent_id === options.folder_id) || [],\n              }),\n            ),\n          ),\n      ])\n\n      const noteCount = notes.items?.length || 0\n      const subfolderCount = subfolders.items?.length || 0\n      const totalContent = noteCount + subfolderCount\n\n      // Warn if folder is not empty and force is not specified\n      if (totalContent > 0 && !options.force) {\n        const resultLines: string[] = []\n        resultLines.push(`⚠️  Cannot delete non-empty notebook!`)\n        resultLines.push(\"\")\n        resultLines.push(`📁 Notebook: \"${folderToDelete.title}\"`)\n        resultLines.push(`   Contains: ${noteCount} notes and ${subfolderCount} subfolders`)\n\n        if (noteCount > 0) {\n          resultLines.push(\"\")\n          resultLines.push(`📝 Contains ${noteCount} notes:`)\n          notes.items.slice(0, 5).forEach((note: any) => {\n            resultLines.push(`   - ${note.title || \"Untitled\"}`)\n          })\n          if (noteCount > 5) {\n            resultLines.push(`   ... and ${noteCount - 5} more notes`)\n          }\n        }\n\n        if (subfolderCount > 0) {\n          resultLines.push(\"\")\n          resultLines.push(`📁 Contains ${subfolderCount} subfolders:`)\n          subfolders.items.slice(0, 5).forEach((folder: any) => {\n            resultLines.push(`   - ${folder.title}`)\n          })\n          if (subfolderCount > 5) {\n            resultLines.push(`   ... and ${subfolderCount - 5} more folders`)\n          }\n        }\n\n        resultLines.push(\"\")\n        resultLines.push(`💡 Options:`)\n        resultLines.push(`   1. Move or delete the contents first, then delete the folder`)\n        resultLines.push(`   2. Force delete (⚠️  DESTROYS ALL CONTENT):`)\n        resultLines.push(`      delete_folder {\"folder_id\": \"${options.folder_id}\", \"confirm\": true, \"force\": true}`)\n        resultLines.push(\"\")\n        resultLines.push(`⚠️  Force delete will permanently delete ALL ${totalContent} items inside!`)\n\n        return resultLines.join(\"\\n\")\n      }\n\n      // Get parent folder info if available\n      let parentInfo = \"Top level\"\n      if (folderToDelete.parent_id) {\n        try {\n          const parentFolder = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${folderToDelete.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (parentFolder?.title) {\n            parentInfo = `Inside \"${parentFolder.title}\" (notebook_id: \"${folderToDelete.parent_id}\")`\n          }\n        } catch {\n          parentInfo = `Parent ID: ${folderToDelete.parent_id}`\n        }\n      }\n\n      // Delete the folder\n      this.unwrap(await this.apiClient.delete(`/folders/${options.folder_id}`))\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`🗑️  Successfully deleted notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Deleted Notebook Details:`)\n      resultLines.push(`   Title: \"${folderToDelete.title}\"`)\n      resultLines.push(`   Folder ID: ${folderToDelete.id}`)\n      resultLines.push(`   Location: ${parentInfo}`)\n\n      if (totalContent > 0) {\n        resultLines.push(`   Deleted Content: ${noteCount} notes and ${subfolderCount} subfolders`)\n        resultLines.push(\"\")\n        resultLines.push(`⚠️  All ${totalContent} items inside have been permanently deleted!`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`⚠️  This notebook has been permanently deleted and cannot be recovered.`)\n\n      if (folderToDelete.parent_id) {\n        resultLines.push(\"\")\n        resultLines.push(`🔗 Related actions:`)\n        resultLines.push(`   - View parent notebook: read_notebook notebook_id=\"${folderToDelete.parent_id}\"`)\n        resultLines.push(`   - View all notebooks: list_notebooks`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n        }\n        if (error.response.status === 403) {\n          return `Permission denied: Cannot delete folder with ID \"${options.folder_id}\".\\n\\nThis might be a protected system folder.`\n        }\n        if (error.response.status === 409) {\n          return `Cannot delete folder: It may contain items that prevent deletion.\\n\\nTry moving or deleting the contents first, or use force option.`\n        }\n      }\n      return this.formatError(error, \"deleting folder\")\n    }\n  }\n}\n\nexport default DeleteFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface DeleteNoteOptions {\n  note_id: string\n  confirm?: boolean | undefined\n}\n\nclass DeleteNote extends BaseTool {\n  async call(options: DeleteNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    // Validate required note_id\n    if (!options.note_id) {\n      return 'Please provide note deletion options. Example: delete_note {\"note_id\": \"abc123\", \"confirm\": true}'\n    }\n\n    const noteIdError = this.validateId(options.note_id, \"note\")\n    if (noteIdError) {\n      return noteIdError\n    }\n\n    // Require explicit confirmation for safety\n    if (!options.confirm) {\n      return `⚠️  This will permanently delete the note!\\n\\nTo confirm deletion, use:\\ndelete_note {\"note_id\": \"${options.note_id}\", \"confirm\": true}\\n\\n⚠️  This action cannot be undone!`\n    }\n\n    try {\n      // First, get the note details to show what's being deleted\n      const noteToDelete = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n          query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,created_time,updated_time\" },\n        }),\n      )\n\n      if (!noteToDelete || !noteToDelete.id) {\n        return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n      }\n\n      // Get notebook info if available\n      let notebookInfo = \"Root level\"\n      if (noteToDelete.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${noteToDelete.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (notebook?.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${noteToDelete.parent_id}\")`\n          }\n        } catch {\n          notebookInfo = `Notebook ID: ${noteToDelete.parent_id}`\n        }\n      }\n\n      // Delete the note\n      this.unwrap(await this.apiClient.delete(`/notes/${options.note_id}`))\n\n      // Format success response\n      const resultLines: string[] = []\n      resultLines.push(`🗑️  Successfully deleted note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Deleted Note Details:`)\n      resultLines.push(`   Title: \"${noteToDelete.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${noteToDelete.id}`)\n      resultLines.push(`   Location: ${notebookInfo}`)\n\n      if (noteToDelete.is_todo) {\n        const status = noteToDelete.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`   Type: Todo (${status})`)\n      } else {\n        resultLines.push(`   Type: Regular note`)\n      }\n\n      const createdDate = this.formatDate(noteToDelete.created_time)\n      const updatedDate = this.formatDate(noteToDelete.updated_time)\n      resultLines.push(`   Created: ${createdDate}`)\n      resultLines.push(`   Last Updated: ${updatedDate}`)\n\n      // Show content preview if available\n      if (noteToDelete.body) {\n        const preview = noteToDelete.body.substring(0, 100).replace(/\\n/g, \" \")\n        const truncated = noteToDelete.body.length > 100 ? \"...\" : \"\"\n        resultLines.push(`   Content Preview: ${preview}${truncated}`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`⚠️  This note has been permanently deleted and cannot be recovered.`)\n\n      if (noteToDelete.parent_id) {\n        resultLines.push(\"\")\n        resultLines.push(`🔗 Related actions:`)\n        resultLines.push(`   - View containing notebook: read_notebook notebook_id=\"${noteToDelete.parent_id}\"`)\n        resultLines.push(`   - Search for similar notes: search_notes query=\"${noteToDelete.title}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n        }\n        if (error.response.status === 403) {\n          return `Permission denied: Cannot delete note with ID \"${options.note_id}\".\\n\\nThis might be a protected system note.`\n        }\n      }\n      return this.formatError(error, \"deleting note\")\n    }\n  }\n}\n\nexport default DeleteNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\ninterface EditFolderOptions {\n  folder_id: string\n  title?: string | undefined\n  parent_id?: string | undefined\n}\n\ninterface EditFolderResponse extends JoplinFolder {\n  updated_time: number\n}\n\nclass EditFolder extends BaseTool {\n  async call(options: EditFolderOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n    }\n\n    // Validate required folder_id\n    if (!options.folder_id) {\n      return 'Please provide folder edit options. Example: edit_folder {\"folder_id\": \"abc123\", \"title\": \"New Name\"}'\n    }\n\n    const folderIdError = this.validateId(options.folder_id, \"notebook\")\n    if (folderIdError) {\n      return folderIdError.replace(\"notebook ID\", \"folder ID\").replace(\"notebook_id\", \"folder_id\")\n    }\n\n    // Validate that we have at least one field to update\n    const updateFields = [\"title\", \"parent_id\"]\n    const hasUpdate = updateFields.some((field) => options[field as keyof EditFolderOptions] !== undefined)\n\n    if (!hasUpdate) {\n      return \"Please provide at least one field to update. Available fields: title, parent_id\"\n    }\n\n    // Validate title if provided\n    if (options.title !== undefined && (typeof options.title !== \"string\" || options.title.trim() === \"\")) {\n      return \"Title must be a non-empty string.\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n      if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n        return `Error: \"${options.parent_id}\" does not appear to be a valid parent notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n      }\n\n      // Prevent self-parenting\n      if (options.parent_id === options.folder_id) {\n        return \"Error: A folder cannot be its own parent.\"\n      }\n    }\n\n    try {\n      // First, get the current folder to show before/after comparison\n      const currentFolder = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${options.folder_id}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      if (!currentFolder || !currentFolder.id) {\n        return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n      }\n\n      // Prepare the update body - only include fields that are being updated\n      const updateBody: Partial<EditFolderOptions> = {}\n\n      if (options.title !== undefined) updateBody.title = options.title.trim()\n      if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n\n      // Update the folder\n      const updatedFolder = this.unwrap(\n        await this.apiClient.put<EditFolderResponse>(`/folders/${options.folder_id}`, updateBody),\n      )\n\n      // Validate response\n      if (!updatedFolder || typeof updatedFolder !== \"object\" || !updatedFolder.id) {\n        return \"Error: Unexpected response format from Joplin API when updating folder\"\n      }\n\n      // Get parent folder info for both old and new locations if parent_id changed\n      let oldParentInfo = \"Top level\"\n      let newParentInfo = \"Top level\"\n\n      if (currentFolder.parent_id) {\n        try {\n          const oldParent = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${currentFolder.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (oldParent?.title) {\n            oldParentInfo = `Inside \"${oldParent.title}\"`\n          }\n        } catch {\n          oldParentInfo = `Parent ID: ${currentFolder.parent_id}`\n        }\n      }\n\n      if (updatedFolder.parent_id && updatedFolder.parent_id !== currentFolder.parent_id) {\n        try {\n          const newParent = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${updatedFolder.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (newParent?.title) {\n            newParentInfo = `Inside \"${newParent.title}\"`\n          }\n        } catch {\n          newParentInfo = `Parent ID: ${updatedFolder.parent_id}`\n        }\n      } else if (updatedFolder.parent_id) {\n        newParentInfo = oldParentInfo\n      }\n\n      // Format success response with before/after comparison\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully updated notebook!`)\n      resultLines.push(\"\")\n      resultLines.push(`📁 Notebook: \"${updatedFolder.title}\"`)\n      resultLines.push(`   Folder ID: ${updatedFolder.id}`)\n      resultLines.push(\"\")\n\n      // Show what changed\n      resultLines.push(`🔄 Changes made:`)\n\n      if (options.title !== undefined && currentFolder.title !== updatedFolder.title) {\n        resultLines.push(`   Title: \"${currentFolder.title}\" → \"${updatedFolder.title}\"`)\n      }\n\n      if (options.parent_id !== undefined && currentFolder.parent_id !== updatedFolder.parent_id) {\n        resultLines.push(`   Location: ${oldParentInfo} → ${newParentInfo}`)\n      }\n\n      if (updatedFolder.updated_time) {\n        const updatedTime = this.formatDate(updatedFolder.updated_time)\n        resultLines.push(`   Last Updated: ${updatedTime}`)\n      }\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - View notebook: read_notebook notebook_id=\"${updatedFolder.id}\"`)\n      resultLines.push(`   - View all notebooks: list_notebooks`)\n      if (updatedFolder.parent_id) {\n        resultLines.push(`   - View parent notebook: read_notebook notebook_id=\"${updatedFolder.parent_id}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          if (error.config?.url?.includes(`/folders/${options.folder_id}`)) {\n            return `Folder with ID \"${options.folder_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n          }\n          if (options.parent_id) {\n            return `Error: Parent folder with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available folders and their IDs.`\n          }\n        }\n        if (error.response.status === 400) {\n          return `Error updating folder: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 409) {\n          return `Error: A folder with the title \"${options.title}\" might already exist in this location.\\n\\nTry a different title or check existing folders with list_notebooks.`\n        }\n      }\n      return this.formatError(error, \"updating folder\")\n    }\n  }\n}\n\nexport default EditFolder\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface EditNoteOptions {\n  note_id: string\n  title?: string | undefined\n  body?: string | undefined\n  body_html?: string | undefined\n  parent_id?: string | undefined\n  is_todo?: boolean | undefined\n  todo_completed?: boolean | undefined\n  todo_due?: number | undefined\n}\n\ntype EditNoteResponse = JoplinNote\n\nclass EditNote extends BaseTool {\n  async call(options: EditNoteOptions): Promise<string> {\n    if (!options || typeof options !== \"object\") {\n      return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n    }\n\n    // Validate required note_id\n    if (!options.note_id) {\n      return 'Please provide note edit options. Example: edit_note {\"note_id\": \"abc123\", \"title\": \"Updated Title\"}'\n    }\n\n    const noteIdError = this.validateId(options.note_id, \"note\")\n    if (noteIdError) {\n      return noteIdError\n    }\n\n    // Validate that we have at least one field to update\n    const updateFields = [\"title\", \"body\", \"body_html\", \"parent_id\", \"is_todo\", \"todo_completed\", \"todo_due\"]\n    const hasUpdate = updateFields.some((field) => options[field as keyof EditNoteOptions] !== undefined)\n\n    if (!hasUpdate) {\n      return \"Please provide at least one field to update. Available fields: title, body, body_html, parent_id, is_todo, todo_completed, todo_due\"\n    }\n\n    // Validate parent_id if provided\n    if (options.parent_id !== undefined && options.parent_id !== null && options.parent_id !== \"\") {\n      if (options.parent_id.length < 10 || !options.parent_id.match(/[a-f0-9]/i)) {\n        return `Error: \"${options.parent_id}\" does not appear to be a valid notebook ID.\\n\\nNotebook IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n      }\n    }\n\n    try {\n      // First, get the current note to show before/after comparison\n      const currentNote = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${options.note_id}`, {\n          query: { fields: \"id,title,body,parent_id,is_todo,todo_completed,todo_due,updated_time\" },\n        }),\n      )\n\n      if (!currentNote || !currentNote.id) {\n        return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n      }\n\n      // Prepare the update body - only include fields that are being updated\n      const updateBody: Partial<EditNoteOptions> = {}\n\n      if (options.title !== undefined) updateBody.title = options.title\n      if (options.body !== undefined) updateBody.body = options.body\n      if (options.body_html !== undefined) updateBody.body_html = options.body_html\n      if (options.parent_id !== undefined) updateBody.parent_id = options.parent_id\n      if (options.is_todo !== undefined) updateBody.is_todo = options.is_todo\n      if (options.todo_completed !== undefined) updateBody.todo_completed = options.todo_completed\n      if (options.todo_due !== undefined) updateBody.todo_due = options.todo_due\n\n      // Update the note\n      const updatedNote = this.unwrap(\n        await this.apiClient.put<EditNoteResponse>(`/notes/${options.note_id}`, updateBody),\n      )\n\n      // Validate response\n      if (!updatedNote || typeof updatedNote !== \"object\" || !updatedNote.id) {\n        return \"Error: Unexpected response format from Joplin API when updating note\"\n      }\n\n      // Get notebook info for both old and new locations if parent_id changed\n      let oldNotebookInfo = \"Root level\"\n      let newNotebookInfo = \"Root level\"\n\n      if (currentNote.parent_id) {\n        try {\n          const oldNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${currentNote.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (oldNotebook?.title) {\n            oldNotebookInfo = `\"${oldNotebook.title}\"`\n          }\n        } catch {\n          oldNotebookInfo = `Notebook ID: ${currentNote.parent_id}`\n        }\n      }\n\n      if (updatedNote.parent_id && updatedNote.parent_id !== currentNote.parent_id) {\n        try {\n          const newNotebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${updatedNote.parent_id}`, {\n              query: { fields: \"title\" },\n            }),\n          )\n          if (newNotebook?.title) {\n            newNotebookInfo = `\"${newNotebook.title}\"`\n          }\n        } catch {\n          newNotebookInfo = `Notebook ID: ${updatedNote.parent_id}`\n        }\n      } else if (updatedNote.parent_id) {\n        newNotebookInfo = oldNotebookInfo\n      }\n\n      // Format success response with before/after comparison\n      const resultLines: string[] = []\n      resultLines.push(`✅ Successfully updated note!`)\n      resultLines.push(\"\")\n      resultLines.push(`📝 Note: \"${updatedNote.title || \"Untitled\"}\"`)\n      resultLines.push(`   Note ID: ${updatedNote.id}`)\n      resultLines.push(\"\")\n\n      // Show what changed\n      resultLines.push(`🔄 Changes made:`)\n\n      if (options.title !== undefined && currentNote.title !== updatedNote.title) {\n        resultLines.push(`   Title: \"${currentNote.title}\" → \"${updatedNote.title}\"`)\n      }\n\n      if (options.parent_id !== undefined && currentNote.parent_id !== updatedNote.parent_id) {\n        resultLines.push(`   Location: ${oldNotebookInfo} → ${newNotebookInfo}`)\n      }\n\n      if (options.is_todo !== undefined && currentNote.is_todo !== updatedNote.is_todo) {\n        const oldType = currentNote.is_todo ? \"Todo\" : \"Regular note\"\n        const newType = updatedNote.is_todo ? \"Todo\" : \"Regular note\"\n        resultLines.push(`   Type: ${oldType} → ${newType}`)\n      }\n\n      if (options.todo_completed !== undefined && currentNote.todo_completed !== updatedNote.todo_completed) {\n        const oldStatus = currentNote.todo_completed ? \"Completed\" : \"Not completed\"\n        const newStatus = updatedNote.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`   Todo Status: ${oldStatus} → ${newStatus}`)\n      }\n\n      if (options.todo_due !== undefined) {\n        const oldDue = currentNote.todo_due ? this.formatDate(currentNote.todo_due) : \"No due date\"\n        const newDue = updatedNote.todo_due ? this.formatDate(updatedNote.todo_due) : \"No due date\"\n        if (oldDue !== newDue) {\n          resultLines.push(`   Due Date: ${oldDue} → ${newDue}`)\n        }\n      }\n\n      if (options.body !== undefined) {\n        resultLines.push(`   Content: Updated`)\n      }\n\n      if (options.body_html !== undefined) {\n        resultLines.push(`   HTML Content: Updated`)\n      }\n\n      const updatedTime = this.formatDate(updatedNote.updated_time)\n      resultLines.push(`   Last Updated: ${updatedTime}`)\n\n      resultLines.push(\"\")\n      resultLines.push(`🔗 Next steps:`)\n      resultLines.push(`   - Read the note: read_note note_id=\"${updatedNote.id}\"`)\n      if (updatedNote.parent_id) {\n        resultLines.push(`   - View notebook: read_notebook notebook_id=\"${updatedNote.parent_id}\"`)\n      }\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response) {\n        if (error.response.status === 404) {\n          return `Note with ID \"${options.note_id}\" not found.\\n\\nUse search_notes to find notes and their IDs.`\n        }\n        if (error.response.status === 400) {\n          return `Error updating note: Invalid request data.\\n\\nPlease check your input parameters. ${error.response.data?.error || \"\"}`\n        }\n        if (error.response.status === 404 && options.parent_id) {\n          return `Error: Notebook with ID \"${options.parent_id}\" not found.\\n\\nUse list_notebooks to see available notebooks and their IDs.`\n        }\n      }\n      return this.formatError(error, \"updating note\")\n    }\n  }\n}\n\nexport default EditNote\n","import BaseTool, { JoplinFolder } from \"./base-tool.js\"\n\nclass ListNotebooks extends BaseTool {\n  async call(): Promise<string> {\n    try {\n      const notebooks = this.unwrap(\n        await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      const notebooksByParentId: Record<string, JoplinFolder[]> = {}\n\n      notebooks.forEach((notebook) => {\n        const parentId = notebook.parent_id || \"\"\n        if (!notebooksByParentId[parentId]) {\n          notebooksByParentId[parentId] = []\n        }\n        notebooksByParentId[parentId].push(notebook)\n      })\n\n      // Add a header with instructions\n      const resultLines = [\n        \"Joplin Notebooks:\\n\",\n        \"NOTE: To read a notebook, use the notebook_id with the read_notebook command\\n\",\n        'Example: read_notebook notebook_id=\"your-notebook-id\"\\n\\n',\n      ]\n\n      // Add the notebook hierarchy\n      resultLines.push(\n        ...this.notebooksLines(notebooksByParentId[\"\"] || [], {\n          indent: 0,\n          notebooksByParentId,\n        }),\n      )\n\n      return resultLines.join(\"\")\n    } catch (error: unknown) {\n      return this.formatError(error, \"listing notebooks\")\n    }\n  }\n\n  private notebooksLines(\n    notebooks: JoplinFolder[],\n    {\n      indent = 0,\n      notebooksByParentId,\n    }: {\n      indent: number\n      notebooksByParentId: Record<string, JoplinFolder[]>\n    },\n  ): string[] {\n    const result: string[] = []\n    const indentSpaces = \" \".repeat(indent)\n\n    this.sortNotebooks(notebooks).forEach((notebook) => {\n      const id = notebook.id\n      result.push(`${indentSpaces}Notebook: \"${notebook.title}\" (notebook_id: \"${id}\")\\n`)\n\n      const childNotebooks = notebooksByParentId[id]\n      if (childNotebooks) {\n        result.push(\n          ...this.notebooksLines(childNotebooks, {\n            indent: indent + 2,\n            notebooksByParentId,\n          }),\n        )\n      }\n    })\n\n    return result\n  }\n\n  private sortNotebooks(notebooks: JoplinFolder[]): JoplinFolder[] {\n    // Ensure that notebooks starting with '[0]' are sorted first\n    const CHARACTER_BEFORE_A = String.fromCharCode(\"A\".charCodeAt(0) - 1)\n    return [...notebooks].sort((a, b) => {\n      const titleA = a.title.replace(\"[\", CHARACTER_BEFORE_A)\n      const titleB = b.title.replace(\"[\", CHARACTER_BEFORE_A)\n      return titleA.localeCompare(titleB)\n    })\n  }\n}\n\nexport default ListNotebooks\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\nclass ReadMultiNote extends BaseTool {\n  async call(noteIds: string[]): Promise<string> {\n    if (!noteIds || !Array.isArray(noteIds) || noteIds.length === 0) {\n      return 'Please provide an array of note IDs. Example: read_multinote note_ids=[\"id1\", \"id2\", \"id3\"]'\n    }\n\n    // Validate that all IDs look like valid note IDs\n    const invalidIds = noteIds.filter((id) => !id || id.length < 10 || !id.match(/[a-f0-9]/i))\n    if (invalidIds.length > 0) {\n      return `Error: Some IDs do not appear to be valid note IDs: ${invalidIds.join(\", \")}\\n\\nNote IDs are long alphanumeric strings like \"58a0a29f68bc4141b49c99f5d367638a\".\\n\\nUse search_notes to find notes and their IDs.`\n    }\n\n    const resultLines: string[] = []\n    const notFound: string[] = []\n    const errors: string[] = []\n    const successful: string[] = []\n\n    // Add a header\n    resultLines.push(`# Reading ${noteIds.length} notes\\n`)\n\n    // Process each note ID\n    for (let i = 0; i < noteIds.length; i++) {\n      const noteId = noteIds[i]\n      resultLines.push(`## Note ${i + 1} of ${noteIds.length} (ID: ${noteId})\\n`)\n\n      try {\n        // Get the note details with all relevant fields\n        const note = this.unwrap(\n          await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n            query: {\n              fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n            },\n          }),\n        )\n\n        // Validate note response\n        if (!note || typeof note !== \"object\" || !note.id) {\n          errors.push(noteId)\n          resultLines.push(`Error: Unexpected response format from Joplin API when fetching note ${noteId}\\n`)\n          continue\n        }\n\n        successful.push(noteId)\n\n        // Get the notebook info to show where this note is located\n        let notebookInfo = \"Unknown notebook\"\n        if (note.parent_id) {\n          try {\n            const notebook = this.unwrap(\n              await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n                query: { fields: \"id,title\" },\n              }),\n            )\n            if (notebook && notebook.title) {\n              notebookInfo = `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n            }\n          } catch (err: unknown) {\n            process.stderr.write(`Error fetching notebook info for note ${noteId}: ${err}\\n`)\n            // Continue even if we can't get the notebook info\n          }\n        }\n\n        // Add note metadata\n        resultLines.push(`### Note: \"${note.title}\"`)\n        resultLines.push(`Notebook: ${notebookInfo}`)\n\n        // Add todo status if applicable\n        if (note.is_todo) {\n          const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n          resultLines.push(`Status: ${status}`)\n\n          if (note.todo_due) {\n            const dueDate = this.formatDate(note.todo_due)\n            resultLines.push(`Due: ${dueDate}`)\n          }\n        }\n\n        // Add timestamps\n        const createdDate = this.formatDate(note.created_time)\n        const updatedDate = this.formatDate(note.updated_time)\n        resultLines.push(`Created: ${createdDate}`)\n        resultLines.push(`Updated: ${updatedDate}`)\n\n        // Add a separator before the note content\n        resultLines.push(\"\\n---\\n\")\n\n        // Add the note body\n        if (note.body) {\n          resultLines.push(note.body)\n        } else {\n          resultLines.push(\"(This note has no content)\")\n        }\n\n        // Add a separator after the note\n        resultLines.push(\"\\n---\\n\")\n      } catch (error: any) {\n        process.stderr.write(`Error reading note ${noteId}: ${error}\\n`)\n        if (error.response && error.response.status === 404) {\n          notFound.push(noteId)\n          resultLines.push(`Note with ID \"${noteId}\" not found.\\n`)\n        } else {\n          errors.push(noteId)\n          resultLines.push(`Error reading note: ${error.message || \"Unknown error\"}\\n`)\n        }\n      }\n    }\n\n    // Add a summary at the end\n    resultLines.push(\"# Summary\")\n    resultLines.push(`Total notes requested: ${noteIds.length}`)\n    resultLines.push(`Successfully retrieved: ${successful.length}`)\n\n    if (notFound.length > 0) {\n      resultLines.push(`Notes not found: ${notFound.length}`)\n      resultLines.push(`IDs not found: ${notFound.join(\", \")}`)\n    }\n\n    if (errors.length > 0) {\n      resultLines.push(`Errors encountered: ${errors.length}`)\n      resultLines.push(`IDs with errors: ${errors.join(\", \")}`)\n    }\n\n    return resultLines.join(\"\\n\")\n  }\n}\n\nexport default ReadMultiNote\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\nclass ReadNote extends BaseTool {\n  async call(noteId: string): Promise<string> {\n    const validationError = this.validateId(noteId, \"note\")\n    if (validationError) {\n      return validationError\n    }\n\n    try {\n      // Get the note details with all relevant fields\n      const note = this.unwrap(\n        await this.apiClient.get<JoplinNote>(`/notes/${noteId}`, {\n          query: {\n            fields: \"id,title,body,parent_id,created_time,updated_time,is_todo,todo_completed,todo_due\",\n          },\n        }),\n      )\n\n      // Validate note response\n      if (!note || typeof note !== \"object\" || !note.id) {\n        return `Error: Unexpected response format from Joplin API when fetching note`\n      }\n\n      // Get the notebook info to show where this note is located\n      let notebookInfo = \"Unknown notebook\"\n      if (note.parent_id) {\n        try {\n          const notebook = this.unwrap(\n            await this.apiClient.get<JoplinFolder>(`/folders/${note.parent_id}`, {\n              query: { fields: \"id,title\" },\n            }),\n          )\n          if (notebook && notebook.title) {\n            notebookInfo = `\"${notebook.title}\" (notebook_id: \"${note.parent_id}\")`\n          }\n        } catch (err: unknown) {\n          process.stderr.write(`Error fetching notebook info: ${err}\\n`)\n          // Continue even if we can't get the notebook info\n        }\n      }\n\n      // Format the note content\n      const resultLines: string[] = []\n\n      // Add note header with metadata\n      resultLines.push(`# Note: \"${note.title}\"`)\n      resultLines.push(`Note ID: ${note.id}`)\n      resultLines.push(`Notebook: ${notebookInfo}`)\n\n      // Add todo status if applicable\n      if (note.is_todo) {\n        const status = note.todo_completed ? \"Completed\" : \"Not completed\"\n        resultLines.push(`Status: ${status}`)\n\n        if (note.todo_due) {\n          const dueDate = this.formatDate(note.todo_due)\n          resultLines.push(`Due: ${dueDate}`)\n        }\n      }\n\n      // Add timestamps\n      const createdDate = this.formatDate(note.created_time)\n      const updatedDate = this.formatDate(note.updated_time)\n      resultLines.push(`Created: ${createdDate}`)\n      resultLines.push(`Updated: ${updatedDate}`)\n\n      // Add a separator before the note content\n      resultLines.push(\"\\n---\\n\")\n\n      // Add the note body\n      if (note.body) {\n        resultLines.push(note.body)\n      } else {\n        resultLines.push(\"(This note has no content)\")\n      }\n\n      // Add a footer with helpful commands\n      resultLines.push(\"\\n---\\n\")\n      resultLines.push(\"Related commands:\")\n      resultLines.push(`- To view the notebook containing this note: read_notebook notebook_id=\"${note.parent_id}\"`)\n      resultLines.push('- To search for more notes: search_notes query=\"your search term\"')\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response && error.response.status === 404) {\n        return `Note with ID \"${noteId}\" not found.\\n\\nThis might happen if:\\n1. The ID is incorrect\\n2. You're using a notebook ID instead of a note ID\\n3. The note has been deleted\\n\\nUse search_notes to find notes and their IDs.`\n      }\n      return (\n        this.formatError(error, \"reading note\") +\n        `\\n\\nMake sure you're using a valid note ID.\\nUse search_notes to find notes and their IDs.`\n      )\n    }\n  }\n}\n\nexport default ReadNote\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface NotebookNotesResponse {\n  items: JoplinNote[]\n}\n\nclass ReadNotebook extends BaseTool {\n  async call(notebookId: string): Promise<string> {\n    const validationError = this.validateId(notebookId, \"notebook\")\n    if (validationError) {\n      return validationError\n    }\n\n    try {\n      // First, get the notebook details\n      const notebook = this.unwrap(\n        await this.apiClient.get<JoplinFolder>(`/folders/${notebookId}`, {\n          query: { fields: \"id,title,parent_id\" },\n        }),\n      )\n\n      // Validate notebook response\n      if (!notebook || typeof notebook !== \"object\" || !notebook.id) {\n        return `Error: Unexpected response format from Joplin API when fetching notebook`\n      }\n\n      // Get all notes in this notebook\n      const notes = this.unwrap(\n        await this.apiClient.get<NotebookNotesResponse>(`/folders/${notebookId}/notes`, {\n          query: { fields: \"id,title,updated_time,is_todo,todo_completed\" },\n        }),\n      )\n\n      // Validate notes response\n      if (!notes || typeof notes !== \"object\") {\n        return `Error: Unexpected response format from Joplin API when fetching notes`\n      }\n\n      if (!notes.items || !Array.isArray(notes.items) || notes.items.length === 0) {\n        return `Notebook \"${notebook.title}\" (notebook_id: \"${notebook.id}\") is empty.\\n\\nTry another notebook ID or use list_notebooks to see all available notebooks.`\n      }\n\n      // Format the notebook contents\n      const resultLines: string[] = []\n      resultLines.push(`# Notebook: \"${notebook.title}\" (notebook_id: \"${notebook.id}\")`)\n      resultLines.push(`Contains ${notes.items.length} notes:\\n`)\n      resultLines.push(`NOTE: This is showing the contents of notebook \"${notebook.title}\", not a specific note.\\n`)\n\n      // If multiple notes were found, add a hint about read_multinote\n      if (notes.items.length > 1) {\n        const noteIds = notes.items.map((note) => note.id)\n        resultLines.push(`TIP: To read all ${notes.items.length} notes at once, use:\\n`)\n        resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n      }\n\n      // Sort notes by updated_time (newest first)\n      const sortedNotes = [...notes.items].sort((a, b) => b.updated_time - a.updated_time)\n\n      sortedNotes.forEach((note) => {\n        const updatedDate = this.formatDate(note.updated_time)\n\n        // Add checkbox for todos\n        if (note.is_todo) {\n          const checkboxStatus = note.todo_completed ? \"✅\" : \"☐\"\n          resultLines.push(`- ${checkboxStatus} Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        } else {\n          resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        }\n\n        resultLines.push(`  Updated: ${updatedDate}`)\n        resultLines.push(`  To read this note: read_note note_id=\"${note.id}\"`)\n        resultLines.push(\"\") // Empty line between notes\n      })\n\n      return resultLines.join(\"\\n\")\n    } catch (error: any) {\n      if (error.response && error.response.status === 404) {\n        return `Notebook with ID \"${notebookId}\" not found.\\n\\nThis might happen if:\\n1. The ID is incorrect\\n2. You're using a note title instead of a notebook ID\\n3. The notebook has been deleted\\n\\nUse list_notebooks to see all available notebooks with their IDs.`\n      }\n      return (\n        this.formatError(error, \"reading notebook\") +\n        `\\n\\nMake sure you're using a valid notebook ID, not a note title.\\nUse list_notebooks to see all available notebooks with their IDs.`\n      )\n    }\n  }\n}\n\nexport default ReadNotebook\n","import BaseTool, { JoplinFolder, JoplinNote } from \"./base-tool.js\"\n\ninterface SearchResult {\n  items: JoplinNote[]\n}\n\nclass SearchNotes extends BaseTool {\n  async call(query: string): Promise<string> {\n    if (!query) {\n      return \"Please provide a search query.\"\n    }\n\n    try {\n      // Search for notes with the given query\n      const searchResults = this.unwrap(\n        await this.apiClient.get<SearchResult>(\"/search\", {\n          query: {\n            query,\n            fields: \"id,title,body,parent_id,updated_time\",\n          },\n        }),\n      )\n\n      // Handle case where the API doesn't return the expected structure\n      if (!searchResults || typeof searchResults !== \"object\") {\n        return `Error: Unexpected response format from Joplin API`\n      }\n\n      // Handle case where no items were found\n      if (!searchResults.items || !Array.isArray(searchResults.items) || searchResults.items.length === 0) {\n        return `No notes found matching query: \"${query}\"`\n      }\n\n      // Get all folders to be able to show notebook names\n      const folders = this.unwrap(\n        await this.apiClient.getAllItems<JoplinFolder>(\"/folders\", {\n          query: {\n            fields: \"id,title\",\n          },\n        }),\n      )\n\n      // Create a map of folder IDs to folder titles for quick lookup\n      const folderMap: Record<string, string> = {}\n      folders.forEach((folder) => {\n        folderMap[folder.id] = folder.title\n      })\n\n      // Format the search results\n      const resultLines: string[] = []\n      resultLines.push(`Found ${searchResults.items.length} notes matching query: \"${query}\"\\n`)\n      resultLines.push(`NOTE: To read a notebook, use the notebook ID (not the note title)\\n`)\n\n      // If multiple notes were found, add a hint about read_multinote\n      if (searchResults.items.length > 1) {\n        const noteIds = searchResults.items.map((note) => note.id)\n        resultLines.push(`TIP: To read all ${searchResults.items.length} notes at once, use:\\n`)\n        resultLines.push(`read_multinote note_ids=${JSON.stringify(noteIds)}\\n`)\n      }\n\n      searchResults.items.forEach((note) => {\n        const notebookTitle = folderMap[note.parent_id || \"\"] || \"Unknown notebook\"\n        const notebookId = note.parent_id || \"unknown\"\n        const updatedDate = this.formatDate(note.updated_time)\n\n        resultLines.push(`- Note: \"${note.title}\" (note_id: \"${note.id}\")`)\n        resultLines.push(`  Notebook: \"${notebookTitle}\" (notebook_id: \"${notebookId}\")`)\n        resultLines.push(`  Updated: ${updatedDate}`)\n\n        // Add a snippet of the note body if available\n        if (note.body) {\n          const snippet = note.body.substring(0, 100).replace(/\\n/g, \" \") + (note.body.length > 100 ? \"...\" : \"\")\n          resultLines.push(`  Snippet: ${snippet}`)\n        }\n\n        // Add hints for using related commands\n        resultLines.push(`  To read this note: read_note note_id=\"${note.id}\"`)\n        resultLines.push(`  To read this notebook: read_notebook notebook_id=\"${notebookId}\"`)\n\n        resultLines.push(\"\") // Empty line between notes\n      })\n\n      return resultLines.join(\"\\n\")\n    } catch (error: unknown) {\n      return this.formatError(error, \"searching notes\")\n    }\n  }\n}\n\nexport default SearchNotes\n","import { Either } from \"functype\"\n\nimport JoplinAPIClient from \"./lib/joplin-api-client.js\"\nimport type { JoplinSidecar } from \"./lib/joplin-sidecar.js\"\nimport {\n  CreateFolder,\n  CreateNote,\n  DeleteFolder,\n  DeleteNote,\n  EditFolder,\n  EditNote,\n  ListNotebooks,\n  ReadMultiNote,\n  ReadNote,\n  ReadNotebook,\n  SearchNotes,\n} from \"./lib/tools/index.js\"\n\nexport type JoplinServerConfig = {\n  host: string\n  port: number\n  token: string\n  sidecar?: JoplinSidecar\n}\n\nexport class JoplinServerManager {\n  private apiClient: JoplinAPIClient\n  private config: JoplinServerConfig\n  private connected: boolean = false\n  private tools: {\n    listNotebooks: ListNotebooks\n    searchNotes: SearchNotes\n    readNotebook: ReadNotebook\n    readNote: ReadNote\n    readMultiNote: ReadMultiNote\n    createNote: CreateNote\n    createFolder: CreateFolder\n    editNote: EditNote\n    editFolder: EditFolder\n    deleteNote: DeleteNote\n    deleteFolder: DeleteFolder\n  }\n\n  constructor(config: JoplinServerConfig) {\n    this.config = config\n    this.apiClient = new JoplinAPIClient({\n      host: config.host,\n      port: config.port,\n      token: config.token,\n    })\n\n    this.tools = {\n      listNotebooks: new ListNotebooks(this.apiClient),\n      searchNotes: new SearchNotes(this.apiClient),\n      readNotebook: new ReadNotebook(this.apiClient),\n      readNote: new ReadNote(this.apiClient),\n      readMultiNote: new ReadMultiNote(this.apiClient),\n      createNote: new CreateNote(this.apiClient),\n      createFolder: new CreateFolder(this.apiClient),\n      editNote: new EditNote(this.apiClient),\n      editFolder: new EditFolder(this.apiClient),\n      deleteNote: new DeleteNote(this.apiClient),\n      deleteFolder: new DeleteFolder(this.apiClient),\n    }\n  }\n\n  async ensureConnected(): Promise<void> {\n    if (this.connected) {\n      const result = await this.apiClient.serviceAvailable()\n      if (Either.isRight(result)) return\n      this.connected = false\n    }\n\n    const available = await this.apiClient.serviceAvailable()\n    if (Either.isRight(available)) {\n      this.connected = true\n      process.stderr.write(`Connected to Joplin at ${this.config.host}:${this.config.port}\\n`)\n      return\n    }\n\n    // If sidecar exists, try starting it\n    if (this.config.sidecar) {\n      process.stderr.write(\"Joplin not available, starting sidecar...\\n\")\n      const startResult = await this.config.sidecar.start()\n      if (Either.isLeft(startResult)) {\n        const error = startResult.fold(\n          (e) => e,\n          () => null as never,\n        )\n        throw new Error(`Sidecar failed [${error.code}]: ${error.message}`)\n      }\n      const retryAvailable = await this.apiClient.serviceAvailable()\n      if (Either.isRight(retryAvailable)) {\n        this.connected = true\n        process.stderr.write(`Connected to Joplin via sidecar at ${this.config.host}:${this.config.port}\\n`)\n        return\n      }\n    }\n\n    throw new Error(\n      `Joplin is not available at ${this.config.host}:${this.config.port}. ` +\n        `Please ensure Joplin is running or configure a sidecar.`,\n    )\n  }\n\n  // Tool execution methods\n  async listNotebooks(): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.listNotebooks.call()\n  }\n\n  async searchNotes(query: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.searchNotes.call(query)\n  }\n\n  async readNotebook(notebookId: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readNotebook.call(notebookId)\n  }\n\n  async readNote(noteId: string): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readNote.call(noteId)\n  }\n\n  async readMultiNote(noteIds: string[]): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.readMultiNote.call(noteIds)\n  }\n\n  async createNote(params: {\n    title?: string | undefined\n    body?: string | undefined\n    body_html?: string | undefined\n    parent_id?: string | undefined\n    is_todo?: boolean | undefined\n    image_data_url?: string | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.createNote.call(params)\n  }\n\n  async createFolder(params: { title: string; parent_id?: string | undefined }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.createFolder.call(params)\n  }\n\n  async editNote(params: {\n    note_id: string\n    title?: string | undefined\n    body?: string | undefined\n    body_html?: string | undefined\n    parent_id?: string | undefined\n    is_todo?: boolean | undefined\n    todo_completed?: boolean | undefined\n    todo_due?: number | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.editNote.call(params)\n  }\n\n  async editFolder(params: {\n    folder_id: string\n    title?: string | undefined\n    parent_id?: string | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.editFolder.call(params)\n  }\n\n  async deleteNote(params: { note_id: string; confirm?: boolean | undefined }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.deleteNote.call(params)\n  }\n\n  async deleteFolder(params: {\n    folder_id: string\n    confirm?: boolean | undefined\n    force?: boolean | undefined\n  }): Promise<string> {\n    await this.ensureConnected()\n    return await this.tools.deleteFolder.call(params)\n  }\n\n  async sync(): Promise<string> {\n    await this.ensureConnected()\n    const desktopWarning = this.config.sidecar?.isDesktopDetected()\n      ? \"\\n\\nNote: Joplin Desktop is also running. The sidecar and Desktop use separate databases. \" +\n        \"Notes sync between them only if both are configured with the same sync target.\"\n      : \"\"\n    // Try triggering sync via the Joplin REST API (POST /services/sync)\n    const result = await this.apiClient.post<Record<string, unknown>>(\"/services/sync\", { action: \"start\" })\n    return result.fold(\n      (error) => {\n        const msg = error.message || String(error)\n        // 404 or \"No action API\" = Joplin instance doesn't expose sync as a REST service\n        // This is normal for Joplin Terminal CLI — it auto-syncs on its configured interval\n        if (msg.includes(\"404\") || msg.includes(\"No action API\") || msg.includes(\"No such service\")) {\n          return (\n            \"Sync is managed automatically by the Joplin server on its configured interval \" +\n            \"(default: every 5 minutes). On-demand sync is not available via the Joplin Terminal API.\" +\n            desktopWarning\n          )\n        }\n        return `Sync failed: ${msg}${desktopWarning}`\n      },\n      () => `Sync triggered successfully.${desktopWarning}`,\n    )\n  }\n}\n\nexport function initializeJoplinManager(config: JoplinServerConfig): JoplinServerManager {\n  return new JoplinServerManager(config)\n}\n","import { FastMCP } from \"fastmcp\"\nimport { readFileSync } from \"fs\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\nimport { z } from \"zod\"\n\nimport { initializeJoplinManager, JoplinServerManager } from \"./server-core.js\"\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\nconst packageJson = JSON.parse(readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"))\nconst VERSION = packageJson.version\n\nexport type FastMCPServerOptions = {\n  host: string\n  port: number\n  token: string\n  httpPort?: number\n  endpoint?: string\n}\n\nexport function createFastMCPServer(options: FastMCPServerOptions): { server: FastMCP; manager: JoplinServerManager } {\n  process.stderr.write(\"Initializing FastMCP server for Joplin...\\n\")\n\n  // Initialize Joplin manager\n  const manager = initializeJoplinManager({ host: options.host, port: options.port, token: options.token })\n\n  // Create FastMCP server\n  const server = new FastMCP({\n    name: \"joplin\",\n    version: VERSION,\n    health: {\n      enabled: true,\n      path: \"/health\",\n      status: 200,\n      message: JSON.stringify({\n        status: \"healthy\",\n        service: \"joplin-mcp-server\",\n        version: VERSION,\n        joplinPort: options.port,\n        timestamp: new Date().toISOString(),\n      }),\n    },\n  })\n\n  // Add list_notebooks tool\n  server.addTool({\n    name: \"list_notebooks\",\n    description: \"Retrieve the complete notebook hierarchy from Joplin\",\n    parameters: z.object({}),\n    execute: async () => {\n      return await manager.listNotebooks()\n    },\n  })\n\n  // Add search_notes tool\n  server.addTool({\n    name: \"search_notes\",\n    description: \"Search for notes in Joplin and return matching notebooks\",\n    parameters: z.object({\n      query: z.string().describe(\"Search query for notes\"),\n    }),\n    execute: async (args) => {\n      return await manager.searchNotes(args.query)\n    },\n  })\n\n  // Add read_notebook tool\n  server.addTool({\n    name: \"read_notebook\",\n    description: \"Read the contents of a specific notebook\",\n    parameters: z.object({\n      notebook_id: z.string().describe(\"ID of the notebook to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readNotebook(args.notebook_id)\n    },\n  })\n\n  // Add read_note tool\n  server.addTool({\n    name: \"read_note\",\n    description: \"Read the full content of a specific note\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readNote(args.note_id)\n    },\n  })\n\n  // Add read_multinote tool\n  server.addTool({\n    name: \"read_multinote\",\n    description: \"Read the full content of multiple notes at once\",\n    parameters: z.object({\n      note_ids: z.array(z.string()).describe(\"Array of note IDs to read\"),\n    }),\n    execute: async (args) => {\n      return await manager.readMultiNote(args.note_ids)\n    },\n  })\n\n  // Add create_note tool\n  server.addTool({\n    name: \"create_note\",\n    description: \"Create a new note in Joplin\",\n    parameters: z.object({\n      title: z.string().optional().describe(\"Note title\"),\n      body: z.string().optional().describe(\"Note content in Markdown\"),\n      body_html: z.string().optional().describe(\"Note content in HTML\"),\n      parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n      is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n      image_data_url: z.string().optional().describe(\"Base64 encoded image data URL\"),\n    }),\n    execute: async (args) => {\n      return await manager.createNote(args)\n    },\n  })\n\n  // Add create_folder tool\n  server.addTool({\n    name: \"create_folder\",\n    description: \"Create a new folder/notebook in Joplin\",\n    parameters: z.object({\n      title: z.string().describe(\"Notebook title\"),\n      parent_id: z.string().optional().describe(\"ID of parent notebook\"),\n    }),\n    execute: async (args) => {\n      return await manager.createFolder(args)\n    },\n  })\n\n  // Add edit_note tool\n  server.addTool({\n    name: \"edit_note\",\n    description: \"Edit/update an existing note in Joplin\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to edit\"),\n      title: z.string().optional().describe(\"New note title\"),\n      body: z.string().optional().describe(\"New note content in Markdown\"),\n      body_html: z.string().optional().describe(\"New note content in HTML\"),\n      parent_id: z.string().optional().describe(\"New parent notebook ID\"),\n      is_todo: z.boolean().optional().describe(\"Whether this is a todo note\"),\n      todo_completed: z.boolean().optional().describe(\"Whether todo is completed\"),\n      todo_due: z.number().optional().describe(\"Todo due date (Unix timestamp)\"),\n    }),\n    execute: async (args) => {\n      return await manager.editNote(args)\n    },\n  })\n\n  // Add edit_folder tool\n  server.addTool({\n    name: \"edit_folder\",\n    description: \"Edit/update an existing folder/notebook in Joplin\",\n    parameters: z.object({\n      folder_id: z.string().describe(\"ID of the folder to edit\"),\n      title: z.string().optional().describe(\"New folder title\"),\n      parent_id: z.string().optional().describe(\"New parent folder ID\"),\n    }),\n    execute: async (args) => {\n      return await manager.editFolder(args)\n    },\n  })\n\n  // Add delete_note tool\n  server.addTool({\n    name: \"delete_note\",\n    description: \"Delete a note from Joplin (requires confirmation)\",\n    parameters: z.object({\n      note_id: z.string().describe(\"ID of the note to delete\"),\n      confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n    }),\n    execute: async (args) => {\n      return await manager.deleteNote(args)\n    },\n  })\n\n  // Add delete_folder tool\n  server.addTool({\n    name: \"delete_folder\",\n    description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n    parameters: z.object({\n      folder_id: z.string().describe(\"ID of the folder to delete\"),\n      confirm: z.boolean().optional().describe(\"Confirmation flag\"),\n      force: z.boolean().optional().describe(\"Force delete even if folder has contents\"),\n    }),\n    execute: async (args) => {\n      return await manager.deleteFolder(args)\n    },\n  })\n\n  // Add sync tool\n  server.addTool({\n    name: \"sync\",\n    description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n    parameters: z.object({}),\n    execute: async () => {\n      return await manager.sync()\n    },\n  })\n\n  process.stderr.write(\"FastMCP server configured with 12 Joplin tools\\n\")\n  return { server, manager }\n}\n\nexport async function startFastMCPServer(options: FastMCPServerOptions): Promise<void> {\n  const { server } = createFastMCPServer(options)\n\n  process.stderr.write(`Configured for Joplin at ${options.host}:${options.port}\\n`)\n\n  const port = options.httpPort || 3000\n  const endpoint = options.endpoint || \"/mcp\"\n\n  await server.start({\n    transportType: \"httpStream\",\n    httpStream: {\n      port,\n      endpoint: endpoint as `/${string}`,\n    },\n  })\n\n  process.stderr.write(`FastMCP server running on http://0.0.0.0:${port}${endpoint}\\n`)\n}\n","#!/usr/bin/env node\n\ndeclare const __VERSION__: string\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { type CallToolRequest, CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\"\nimport fs from \"fs\"\nimport path from \"path\"\nimport { fileURLToPath } from \"url\"\n\nimport { DEFAULT_API_PORT, JoplinSidecar, type SyncTarget } from \"./lib/joplin-sidecar.js\"\nimport parseArgs from \"./lib/parse-args.js\"\nimport { initializeJoplinManager } from \"./server-core.js\"\nimport { startFastMCPServer } from \"./server-fastmcp.js\"\n\n// Parse command line arguments\nconst parsedArgs = parseArgs()\nconst { transport, httpPort, profileDir, syncTarget } = parsedArgs\n\nconst isHttpMode = transport === \"http\"\n\n// External mode: JOPLIN_HOST/JOPLIN_PORT set = connect directly, skip sidecar\nconst externalHost = process.env.JOPLIN_HOST\nconst externalPort = process.env.JOPLIN_PORT ? parseInt(process.env.JOPLIN_PORT, 10) : undefined\nconst externalMode = !!(externalHost || externalPort)\n\n// Token is required for external mode, auto-generated for sidecar mode\nif (!process.env.JOPLIN_TOKEN && externalMode) {\n  process.stderr.write(\n    \"Error: JOPLIN_TOKEN is required in external mode. Use --token <token> or set JOPLIN_TOKEN environment variable.\\n\",\n  )\n  process.exit(1)\n}\n\n// In sidecar mode, persist the auto-generated token so the config hash stays stable\n// across restarts, enabling config caching to skip redundant `joplin config` CLI calls.\nconst joplinToken = (() => {\n  if (process.env.JOPLIN_TOKEN) return process.env.JOPLIN_TOKEN\n  if (externalMode) return `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n  const tokenPath = path.join(profileDir, \".mcp-token\")\n  try {\n    const saved = fs.readFileSync(tokenPath, \"utf-8\").trim()\n    if (saved) return saved\n  } catch {\n    // No saved token yet\n  }\n  const token = `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\n  try {\n    fs.mkdirSync(profileDir, { recursive: true })\n    fs.writeFileSync(tokenPath, token)\n  } catch {\n    // Non-critical — token still works, just won't be cached\n  }\n  return token\n})()\n\n// Main startup logic\nasync function main(): Promise<void> {\n  let host: string\n  let port: number\n  let sidecar: JoplinSidecar | undefined\n\n  if (externalMode) {\n    // External mode — connect to existing Joplin instance (e.g. Windows desktop from WSL)\n    host = externalHost || \"127.0.0.1\"\n    port = externalPort || DEFAULT_API_PORT\n    process.stderr.write(`External mode: connecting to Joplin at ${host}:${port}\\n`)\n  } else {\n    // Sidecar mode — spawn and manage Joplin Terminal\n    sidecar = new JoplinSidecar({\n      profileDir,\n      apiPort: DEFAULT_API_PORT,\n      apiToken: joplinToken,\n      syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n    })\n\n    // Phase 1: Resolve port (fast — a few HTTP probes).\n    // Must complete before getPort() so downstream gets the correct port.\n    const portResult = await sidecar.resolvePort()\n    portResult.fold(\n      (err) => process.stderr.write(`Warning: Port resolution failed: ${err.message}\\n`),\n      (p) => process.stderr.write(`Sidecar will use port ${p}\\n`),\n    )\n\n    // Phase 2: Fire-and-forget the slow startup (CLI config, spawn, wait).\n    // ensureConnected() in server-core.ts will await or retry on first tool call.\n    sidecar.start().then((result) => {\n      result.fold(\n        (err) => {\n          process.stderr.write(`Warning: Sidecar failed to start: ${err.message}\\n`)\n          process.stderr.write(\"Attempting to connect to existing Joplin instance...\\n\")\n        },\n        () => {\n          process.stderr.write(\"Joplin sidecar started successfully\\n\")\n        },\n      )\n    })\n\n    host = sidecar.getHost()\n    port = sidecar.getPort()\n\n    // Cleanup on exit\n    const cleanup = async () => {\n      await sidecar!.stop()\n      process.exit(0)\n    }\n    process.on(\"SIGINT\", () => void cleanup())\n    process.on(\"SIGTERM\", () => void cleanup())\n  }\n\n  if (isHttpMode) {\n    process.stderr.write(\"Starting HTTP transport mode with FastMCP...\\n\")\n    await startFastMCPServer({\n      host,\n      port,\n      token: joplinToken,\n      httpPort,\n      endpoint: \"/mcp\",\n    })\n  } else {\n    process.stderr.write(\"Starting stdio transport mode...\\n\")\n    await startStdioServer(host, port, joplinToken, sidecar)\n  }\n}\n\nmain().catch((error) => {\n  process.stderr.write(`Failed to start MCP server: ${error}\\n`)\n  process.exit(1)\n})\n\nasync function startStdioServer(host: string, port: number, token: string, sidecar?: JoplinSidecar): Promise<void> {\n  const manager = initializeJoplinManager({ host, port, token, sidecar })\n\n  const server = new Server(\n    {\n      name: \"joplin-mcp-server\",\n      version: __VERSION__,\n    },\n    {\n      capabilities: {\n        resources: {},\n        tools: {},\n        prompts: {},\n      },\n    },\n  )\n\n  // Register tool list handler\n  server.setRequestHandler(ListToolsRequestSchema, () => {\n    return {\n      tools: [\n        {\n          name: \"list_notebooks\",\n          description: \"Retrieve the complete notebook hierarchy from Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {},\n          },\n        },\n        {\n          name: \"search_notes\",\n          description: \"Search for notes in Joplin and return matching notebooks\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              query: { type: \"string\", description: \"Search query\" },\n            },\n            required: [\"query\"],\n          },\n        },\n        {\n          name: \"read_notebook\",\n          description: \"Read the contents of a specific notebook\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              notebook_id: { type: \"string\", description: \"ID of the notebook to read\" },\n            },\n            required: [\"notebook_id\"],\n          },\n        },\n        {\n          name: \"read_note\",\n          description: \"Read the full content of a specific note\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to read\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"read_multinote\",\n          description: \"Read the full content of multiple notes at once\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_ids: { type: \"array\", items: { type: \"string\" }, description: \"Array of note IDs to read\" },\n            },\n            required: [\"note_ids\"],\n          },\n        },\n        {\n          name: \"create_note\",\n          description: \"Create a new note in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              title: { type: \"string\", description: \"Note title\" },\n              body: { type: \"string\", description: \"Note content in Markdown\" },\n              body_html: { type: \"string\", description: \"Note content in HTML\" },\n              parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n              is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n              image_data_url: { type: \"string\", description: \"Base64 encoded image data URL\" },\n            },\n          },\n        },\n        {\n          name: \"create_folder\",\n          description: \"Create a new folder/notebook in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              title: { type: \"string\", description: \"Notebook title\" },\n              parent_id: { type: \"string\", description: \"ID of parent notebook\" },\n            },\n            required: [\"title\"],\n          },\n        },\n        {\n          name: \"edit_note\",\n          description: \"Edit/update an existing note in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to edit\" },\n              title: { type: \"string\", description: \"New note title\" },\n              body: { type: \"string\", description: \"New note content in Markdown\" },\n              body_html: { type: \"string\", description: \"New note content in HTML\" },\n              parent_id: { type: \"string\", description: \"New parent notebook ID\" },\n              is_todo: { type: \"boolean\", description: \"Whether this is a todo note\" },\n              todo_completed: { type: \"boolean\", description: \"Whether todo is completed\" },\n              todo_due: { type: \"number\", description: \"Todo due date (Unix timestamp)\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"edit_folder\",\n          description: \"Edit/update an existing folder/notebook in Joplin\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              folder_id: { type: \"string\", description: \"ID of the folder to edit\" },\n              title: { type: \"string\", description: \"New folder title\" },\n              parent_id: { type: \"string\", description: \"New parent folder ID\" },\n            },\n            required: [\"folder_id\"],\n          },\n        },\n        {\n          name: \"delete_note\",\n          description: \"Delete a note from Joplin (requires confirmation)\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              note_id: { type: \"string\", description: \"ID of the note to delete\" },\n              confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n            },\n            required: [\"note_id\"],\n          },\n        },\n        {\n          name: \"delete_folder\",\n          description: \"Delete a folder/notebook from Joplin (requires confirmation)\",\n          inputSchema: {\n            type: \"object\",\n            properties: {\n              folder_id: { type: \"string\", description: \"ID of the folder to delete\" },\n              confirm: { type: \"boolean\", description: \"Confirmation flag\" },\n              force: { type: \"boolean\", description: \"Force delete even if folder has contents\" },\n            },\n            required: [\"folder_id\"],\n          },\n        },\n        {\n          name: \"sync\",\n          description: \"Trigger a Joplin sync to push/pull changes with the configured sync target\",\n          inputSchema: {\n            type: \"object\",\n            properties: {},\n          },\n        },\n      ],\n    }\n  })\n\n  // Register tool call handler\n  server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n    const toolName = request.params.name\n    const args = request.params.arguments || {}\n\n    try {\n      switch (toolName) {\n        case \"list_notebooks\": {\n          const listResult = await manager.listNotebooks()\n          return { content: [{ type: \"text\", text: listResult }], isError: false }\n        }\n\n        case \"search_notes\": {\n          const searchResult = await manager.searchNotes(args.query as string)\n          return { content: [{ type: \"text\", text: searchResult }], isError: false }\n        }\n\n        case \"read_notebook\": {\n          const notebookResult = await manager.readNotebook(args.notebook_id as string)\n          return { content: [{ type: \"text\", text: notebookResult }], isError: false }\n        }\n\n        case \"read_note\": {\n          const noteResult = await manager.readNote(args.note_id as string)\n          return { content: [{ type: \"text\", text: noteResult }], isError: false }\n        }\n\n        case \"read_multinote\": {\n          const multiResult = await manager.readMultiNote(args.note_ids as string[])\n          return { content: [{ type: \"text\", text: multiResult }], isError: false }\n        }\n\n        case \"create_note\": {\n          const createNoteResult = await manager.createNote(\n            args as {\n              title?: string | undefined\n              body?: string | undefined\n              body_html?: string | undefined\n              parent_id?: string | undefined\n              is_todo?: boolean | undefined\n              image_data_url?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: createNoteResult }], isError: false }\n        }\n\n        case \"create_folder\": {\n          const createFolderResult = await manager.createFolder(\n            args as {\n              title: string\n              parent_id?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: createFolderResult }], isError: false }\n        }\n\n        case \"edit_note\": {\n          const editNoteResult = await manager.editNote(\n            args as {\n              note_id: string\n              title?: string | undefined\n              body?: string | undefined\n              body_html?: string | undefined\n              parent_id?: string | undefined\n              is_todo?: boolean | undefined\n              todo_completed?: boolean | undefined\n              todo_due?: number | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: editNoteResult }], isError: false }\n        }\n\n        case \"edit_folder\": {\n          const editFolderResult = await manager.editFolder(\n            args as {\n              folder_id: string\n              title?: string | undefined\n              parent_id?: string | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: editFolderResult }], isError: false }\n        }\n\n        case \"delete_note\": {\n          const deleteNoteResult = await manager.deleteNote(\n            args as {\n              note_id: string\n              confirm?: boolean | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: deleteNoteResult }], isError: false }\n        }\n\n        case \"delete_folder\": {\n          const deleteFolderResult = await manager.deleteFolder(\n            args as {\n              folder_id: string\n              confirm?: boolean | undefined\n              force?: boolean | undefined\n            },\n          )\n          return { content: [{ type: \"text\", text: deleteFolderResult }], isError: false }\n        }\n\n        case \"sync\": {\n          const syncResult = await manager.sync()\n          return { content: [{ type: \"text\", text: syncResult }], isError: false }\n        }\n\n        default:\n          throw new Error(`Unknown tool: ${toolName}`)\n      }\n    } catch (error) {\n      const errorMessage = error instanceof Error ? error.message : String(error)\n      return {\n        content: [{ type: \"text\", text: `Error: ${errorMessage}` }],\n        isError: true,\n      }\n    }\n  })\n\n  // Create logs directory if it doesn't exist\n  const __filename = fileURLToPath(import.meta.url)\n  const __dirname = path.dirname(__filename)\n  const logsDir = path.join(__dirname, \"..\", \"logs\")\n\n  if (!fs.existsSync(logsDir)) {\n    fs.mkdirSync(logsDir, { recursive: true })\n  }\n\n  // Create a log file for this session\n  const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\")\n  const logFile = path.join(logsDir, `mcp-server-${timestamp}.log`)\n\n  // Create a custom transport wrapper to log commands and responses\n  class LoggingTransport extends StdioServerTransport {\n    private commandCounter: number\n\n    constructor() {\n      super()\n      this.commandCounter = 0\n    }\n\n    async sendMessage(message: unknown): Promise<void> {\n      const logEntry = {\n        timestamp: new Date().toISOString(),\n        direction: \"RESPONSE\",\n        message,\n      }\n\n      fs.appendFileSync(logFile, JSON.stringify(logEntry) + \"\\n\")\n\n      const parent = Object.getPrototypeOf(Object.getPrototypeOf(this))\n      return parent.sendMessage.call(this, message)\n    }\n\n    async handleMessage(message: unknown): Promise<void> {\n      this.commandCounter++\n      const logEntry = {\n        timestamp: new Date().toISOString(),\n        direction: \"COMMAND\",\n        commandNumber: this.commandCounter,\n        message,\n      }\n\n      fs.appendFileSync(logFile, JSON.stringify(logEntry) + \"\\n\")\n\n      const parent = Object.getPrototypeOf(Object.getPrototypeOf(this))\n      return parent.handleMessage.call(this, message)\n    }\n  }\n\n  const stdioTransport = new LoggingTransport()\n\n  try {\n    await server.connect(stdioTransport)\n    process.stderr.write(\"MCP server started and ready to receive commands\\n\")\n  } catch (error: unknown) {\n    process.stderr.write(`Failed to start MCP server: ${error instanceof Error ? error.message : String(error)}\\n`)\n    process.exit(1)\n  }\n}\n"],"mappings":"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"}
Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.9 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.8 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.7 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (5.0 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.9 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumNS-003Network SuspiciousMedium ConfidenceLine 0

JavaScript fetch() call

Detected by automated pattern matching (rule NS-003) with medium confidence. May be a false positive.

    32: 	const timer = setTimeout(() => controller.abort(), timeoutMs);
    33: 	try {
>>> 34: 		return await fetch(url, { signal: controller.signal });
    35: 	} finally {
    36: 		clearTimeout(timer);
Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.5 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.6 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive
mediumEN-001unknownMedium ConfidenceLine 0

High-entropy string (4.6 bits/char) — possible encoded payload

Detected by automated pattern matching (rule EN-001) with medium confidence. May be a false positive.

Report false positive

Scan History

DateRiskFindings
Feb 25, 2026critical81
Feb 24, 2026critical81