81
Total
18
Critical
34
High
29
Medium
Findings
unknownEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveEnvironment 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 positiveDecoded base64 content: ��?�&���%��j�!
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveNode.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 positiveDecoded base64 content: J�b�'���ӭ�즊�
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��(������i�^>��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��(������i�^>��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��?�&���%��+y�^
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��?�&���%��+y�^
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��?�&���%��^��^
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��?�&���%��^��^
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: J�b�'���ӭ�즊�
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveNode.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 positiveDecoded base64 content: ��(������i�^>��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��(������i�^>��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ����h�E�)�{
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ����h�E�)�{
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �ƴko_����^5o�\���w~��
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �-0#�z�ެ�m���
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �-0#�z�ެ�m���
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: �-0#�z�ެ�m���
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: J�b�'���ӭ�즊�
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: J�b�'���ӭ�즊�
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded base64 content: ��^��'��m��-��%��d
Detected by automated pattern matching (rule DO-BAS) with medium confidence. May be a false positive.
Report false positiveDecoded 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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positivePossible 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 positiveJavaScript 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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveJavaScript 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 positiveHigh-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 positiveHigh-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 positiveHigh-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 positiveScan History
| Date | Risk | Findings | Files | Duration |
|---|---|---|---|---|
| Feb 25, 2026 | critical | 81 | 10 | 0.00s |
| Feb 24, 2026 | critical | 81 | 10 | 0.00s |