joplin-mcp-server 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -3,12 +3,12 @@
3
3
  if (!process.env.TRANSPORT_TYPE) process.env.TRANSPORT_TYPE = "stdio";
4
4
  const args = process.argv.slice(2);
5
5
  if (args.includes("--version") || args.includes("-v")) {
6
- console.log("2.0.2");
6
+ console.log("2.0.3");
7
7
  process.exit(0);
8
8
  }
9
9
  if (args.includes("--help") || args.includes("-h")) {
10
10
  console.log(`
11
- Joplin MCP Server v2.0.2
11
+ Joplin MCP Server v2.0.3
12
12
 
13
13
  Usage: joplin-mcp-server [options]
14
14
 
package/dist/index.js CHANGED
@@ -14,6 +14,8 @@ import { z } from "zod";
14
14
 
15
15
  //#region src/lib/joplin-sidecar.ts
16
16
  dirname(fileURLToPath(import.meta.url));
17
+ const isWindows = process.platform === "win32";
18
+ const whichCmd = isWindows ? "where" : "which";
17
19
  const sidecarError = (code, message, cause) => ({
18
20
  code,
19
21
  message,
@@ -26,13 +28,14 @@ const findJoplinCli = () => {
26
28
  if (fs.existsSync(envCli)) return Right(envCli);
27
29
  return Left(sidecarError("CLI_NOT_FOUND", `JOPLIN_CLI path not found: ${envCli}`));
28
30
  }
29
- const localBin = join(process.cwd(), "node_modules", ".bin", "joplin");
31
+ const localBin = join(process.cwd(), "node_modules", ".bin", isWindows ? "joplin.cmd" : "joplin");
30
32
  if (fs.existsSync(localBin)) return Right(localBin);
31
33
  try {
32
- return Right(execSync("which joplin", { encoding: "utf-8" }).trim());
34
+ const joplinPath = execSync(`${whichCmd} joplin`, { encoding: "utf-8" }).trim().split("\n")[0];
35
+ return Right(joplinPath);
33
36
  } catch {}
34
37
  try {
35
- const npxPath = execSync("which npx", { encoding: "utf-8" }).trim();
38
+ const npxPath = execSync(`${whichCmd} npx`, { encoding: "utf-8" }).trim().split("\n")[0];
36
39
  process.stderr.write("[joplin-sidecar] No local joplin found, using npx (may download on first run)\n");
37
40
  return Right(npxPath);
38
41
  } catch {}
@@ -52,7 +55,7 @@ const runJoplinConfig = (cmd, profileDir, key, value) => {
52
55
  const configureJoplin = (cli, config) => {
53
56
  const cmd = cli.endsWith("npx") ? `${cli} joplin` : cli;
54
57
  try {
55
- execSync(`mkdir -p ${config.profileDir}`, { encoding: "utf-8" });
58
+ fs.mkdirSync(config.profileDir, { recursive: true });
56
59
  } catch (e) {
57
60
  return Left(sidecarError("CONFIG_FAILED", "Failed to create profile directory", e));
58
61
  }
@@ -111,7 +114,8 @@ const spawnServer = (cli, config) => {
111
114
  "pipe",
112
115
  "pipe"
113
116
  ],
114
- detached: false
117
+ detached: false,
118
+ shell: isWindows
115
119
  });
116
120
  (_proc$stderr = proc.stderr) === null || _proc$stderr === void 0 || _proc$stderr.on("data", (data) => {
117
121
  process.stderr.write(`[joplin-sidecar] ${data.toString()}`);
@@ -144,7 +148,7 @@ var JoplinSidecar = class {
144
148
  childProcess = null;
145
149
  constructor(config) {
146
150
  this.config = {
147
- profileDir: config.profileDir ?? `${process.env.HOME}/.config/joplin-mcp`,
151
+ profileDir: config.profileDir ?? join(os.homedir(), ".config", "joplin-mcp"),
148
152
  apiPort: config.apiPort ?? 41184,
149
153
  apiToken: config.apiToken,
150
154
  syncTarget: config.syncTarget,
@@ -201,7 +205,13 @@ var JoplinSidecar = class {
201
205
  }
202
206
  async sync() {
203
207
  try {
204
- return Right(execSync(`${execSync("which joplin", { encoding: "utf-8" }).trim() || "npx joplin"} sync --profile ${this.config.profileDir}`, {
208
+ let cmd;
209
+ try {
210
+ cmd = execSync(`${whichCmd} joplin`, { encoding: "utf-8" }).trim().split("\n")[0];
211
+ } catch {
212
+ cmd = "npx joplin";
213
+ }
214
+ return Right(execSync(`${cmd} sync --profile ${this.config.profileDir}`, {
205
215
  encoding: "utf-8",
206
216
  timeout: 12e4
207
217
  }));
@@ -1562,7 +1572,7 @@ async function startStdioServer(host, port, token, sidecar) {
1562
1572
  });
1563
1573
  const server = new Server({
1564
1574
  name: "joplin-mcp-server",
1565
- version: "2.0.2"
1575
+ version: "2.0.3"
1566
1576
  }, { capabilities: {
1567
1577
  resources: {},
1568
1578
  tools: {},
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"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, execSync, spawn } from \"child_process\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\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: \"CLI_NOT_FOUND\" | \"CONFIG_FAILED\" | \"SPAWN_FAILED\" | \"HEALTH_CHECK_FAILED\" | \"STOP_FAILED\" | \"SYNC_FAILED\"\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 findJoplinCli = (): 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\", \"joplin\")\n if (fs.existsSync(localBin)) return Right(localBin)\n\n // 3. Global install\n try {\n const joplinPath = execSync(\"which joplin\", { encoding: \"utf-8\" }).trim()\n return Right(joplinPath)\n } catch {\n // not found\n }\n\n // 4. npx fallback (auto-downloads on first run)\n try {\n const npxPath = execSync(\"which npx\", { encoding: \"utf-8\" }).trim()\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 runJoplinConfig = (cmd: string, profileDir: string, key: string, value: string): Either<SidecarError, void> => {\n try {\n execSync(`${cmd} config --profile ${profileDir} ${key} ${value}`, {\n encoding: \"utf-8\",\n timeout: 30_000,\n })\n return Right(undefined as void)\n } catch (e) {\n return Left(sidecarError(\"CONFIG_FAILED\", `Failed to set ${key}`, e))\n }\n}\n\nconst configureJoplin = (cli: string, config: SidecarConfig): Either<SidecarError, void> => {\n const cmd = cli.endsWith(\"npx\") ? `${cli} joplin` : cli\n\n // Ensure profile directory exists\n try {\n execSync(`mkdir -p ${config.profileDir}`, { encoding: \"utf-8\" })\n } catch (e) {\n return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to create profile directory\", e))\n }\n\n // Set API token\n const tokenResult = runJoplinConfig(cmd, config.profileDir, \"api.token\", config.apiToken)\n if (Either.isLeft(tokenResult)) return tokenResult\n\n // Set API port\n const portResult = runJoplinConfig(cmd, config.profileDir, \"api.port\", String(config.apiPort))\n if (Either.isLeft(portResult)) return portResult\n\n // Configure sync target\n const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n const syncResult = runJoplinConfig(cmd, config.profileDir, \"sync.target\", String(syncTargetId(syncTarget)))\n if (Either.isLeft(syncResult)) return syncResult\n\n // Configure sync-target-specific settings\n const configResults: Either<SidecarError, void>[] = []\n\n if (syncTarget.type === \"filesystem\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.2.path\", syncTarget.path))\n } else if (syncTarget.type === \"webdav\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.6.path\", syncTarget.url))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.6.username\", syncTarget.username))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.6.password\", syncTarget.password))\n } else if (syncTarget.type === \"nextcloud\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.5.path\", syncTarget.url))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.5.username\", syncTarget.username))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.5.password\", syncTarget.password))\n } else if (syncTarget.type === \"s3\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.path\", syncTarget.bucket))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.region\", syncTarget.region))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.username\", syncTarget.accessKey))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.password\", syncTarget.secretKey))\n } else if (syncTarget.type === \"joplin-server\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.9.path\", syncTarget.url))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.9.username\", syncTarget.email))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.9.password\", syncTarget.password))\n } else if (syncTarget.type === \"joplin-cloud\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.10.username\", syncTarget.email))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.10.password\", syncTarget.password))\n }\n\n const failed = configResults.find((r) => Either.isLeft(r))\n if (failed) return failed\n\n // Set sync interval\n const interval = Option(config.syncInterval).orElse(300)\n return runJoplinConfig(cmd, config.profileDir, \"sync.interval\", String(interval))\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 })\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 maxRetries: number = 30,\n intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n for (let i = 0; i < maxRetries; i++) {\n try {\n const response = await fetch(`http://127.0.0.1:${port}/ping`)\n if (response.ok) {\n process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n return Right(true as const)\n }\n } catch {\n // Not ready yet\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n }\n\n return Left(\n sidecarError(\"HEALTH_CHECK_FAILED\", `Joplin server did not become ready within ${maxRetries * intervalMs}ms`),\n )\n}\n\nexport class JoplinSidecar {\n private config: SidecarConfig\n private childProcess: ChildProcess | null = null\n\n constructor(config: Partial<SidecarConfig> & { apiToken: string }) {\n this.config = {\n profileDir: config.profileDir ?? `${process.env.HOME}/.config/joplin-mcp`,\n apiPort: config.apiPort ?? 41184,\n apiToken: config.apiToken,\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n }\n }\n\n async start(): Promise<Either<SidecarError, ChildProcess>> {\n // Step 1: Find CLI\n const cliResult = 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: Configure\n const configResult = 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 process.stderr.write(\"[joplin-sidecar] Configuration applied\\n\")\n\n // Step 3: Spawn server\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\n // Step 4: Wait for ready\n const readyResult = await waitForReady(this.config.apiPort)\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(proc)\n }\n\n async stop(): Promise<Either<Error, true>> {\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 fetch(`http://127.0.0.1:${this.config.apiPort}/ping`)\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 async sync(): Promise<Either<Error, string>> {\n try {\n const cli = execSync(\"which joplin\", { encoding: \"utf-8\" }).trim()\n const cmd = cli || \"npx joplin\"\n const output = execSync(`${cmd} sync --profile ${this.config.profileDir}`, {\n encoding: \"utf-8\",\n timeout: 120_000,\n })\n return Right(output)\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 { 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 expandPath = (p: string): string => {\n const expanded = expandVars(p)\n return expanded.startsWith(\"~/\") || expanded === \"~\" ? expanded.replace(\"~\", os.homedir()) : 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`)\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 })\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 })\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 })\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 })\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.isRight(startResult)) {\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\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 if (!this.config.sidecar) {\n return \"Sync not available in external mode. Sync is managed by your Joplin instance directly.\"\n }\n const result = await this.config.sidecar.sync()\n return result.fold(\n (error) => `Sync failed: ${error.message}`,\n (output) => `Sync completed successfully.\\n\\n${output}`,\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 { 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\nconst joplinToken = process.env.JOPLIN_TOKEN || `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\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 || 41184\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: 41184,\n apiToken: joplinToken,\n syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n })\n\n const startResult = await sidecar.start()\n startResult.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 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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"}
1
+ {"version":3,"file":"index.js","names":[],"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, execSync, spawn } from \"child_process\"\nimport fs from \"fs\"\nimport { Either, Left, Match, Option, Right } from \"functype\"\nimport os from \"os\"\nimport { dirname, join } from \"path\"\nimport { fileURLToPath } from \"url\"\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nconst isWindows = process.platform === \"win32\"\nconst whichCmd = isWindows ? \"where\" : \"which\"\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: \"CLI_NOT_FOUND\" | \"CONFIG_FAILED\" | \"SPAWN_FAILED\" | \"HEALTH_CHECK_FAILED\" | \"STOP_FAILED\" | \"SYNC_FAILED\"\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 findJoplinCli = (): 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 joplinPath = execSync(`${whichCmd} joplin`, { encoding: \"utf-8\" }).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 npxPath = execSync(`${whichCmd} npx`, { encoding: \"utf-8\" }).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 runJoplinConfig = (cmd: string, profileDir: string, key: string, value: string): Either<SidecarError, void> => {\n try {\n execSync(`${cmd} config --profile ${profileDir} ${key} ${value}`, {\n encoding: \"utf-8\",\n timeout: 30_000,\n })\n return Right(undefined as void)\n } catch (e) {\n return Left(sidecarError(\"CONFIG_FAILED\", `Failed to set ${key}`, e))\n }\n}\n\nconst configureJoplin = (cli: string, config: SidecarConfig): Either<SidecarError, void> => {\n const cmd = cli.endsWith(\"npx\") ? `${cli} joplin` : cli\n\n // Ensure profile directory exists\n try {\n fs.mkdirSync(config.profileDir, { recursive: true })\n } catch (e) {\n return Left(sidecarError(\"CONFIG_FAILED\", \"Failed to create profile directory\", e))\n }\n\n // Set API token\n const tokenResult = runJoplinConfig(cmd, config.profileDir, \"api.token\", config.apiToken)\n if (Either.isLeft(tokenResult)) return tokenResult\n\n // Set API port\n const portResult = runJoplinConfig(cmd, config.profileDir, \"api.port\", String(config.apiPort))\n if (Either.isLeft(portResult)) return portResult\n\n // Configure sync target\n const syncTarget = Option(config.syncTarget).orElse({ type: \"none\" } as SyncTarget)\n const syncResult = runJoplinConfig(cmd, config.profileDir, \"sync.target\", String(syncTargetId(syncTarget)))\n if (Either.isLeft(syncResult)) return syncResult\n\n // Configure sync-target-specific settings\n const configResults: Either<SidecarError, void>[] = []\n\n if (syncTarget.type === \"filesystem\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.2.path\", syncTarget.path))\n } else if (syncTarget.type === \"webdav\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.6.path\", syncTarget.url))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.6.username\", syncTarget.username))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.6.password\", syncTarget.password))\n } else if (syncTarget.type === \"nextcloud\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.5.path\", syncTarget.url))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.5.username\", syncTarget.username))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.5.password\", syncTarget.password))\n } else if (syncTarget.type === \"s3\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.path\", syncTarget.bucket))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.region\", syncTarget.region))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.username\", syncTarget.accessKey))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.8.password\", syncTarget.secretKey))\n } else if (syncTarget.type === \"joplin-server\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.9.path\", syncTarget.url))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.9.username\", syncTarget.email))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.9.password\", syncTarget.password))\n } else if (syncTarget.type === \"joplin-cloud\") {\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.10.username\", syncTarget.email))\n configResults.push(runJoplinConfig(cmd, config.profileDir, \"sync.10.password\", syncTarget.password))\n }\n\n const failed = configResults.find((r) => Either.isLeft(r))\n if (failed) return failed\n\n // Set sync interval\n const interval = Option(config.syncInterval).orElse(300)\n return runJoplinConfig(cmd, config.profileDir, \"sync.interval\", String(interval))\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 maxRetries: number = 30,\n intervalMs: number = 1000,\n): Promise<Either<SidecarError, true>> => {\n for (let i = 0; i < maxRetries; i++) {\n try {\n const response = await fetch(`http://127.0.0.1:${port}/ping`)\n if (response.ok) {\n process.stderr.write(`[joplin-sidecar] Server ready on port ${port}\\n`)\n return Right(true as const)\n }\n } catch {\n // Not ready yet\n }\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n }\n\n return Left(\n sidecarError(\"HEALTH_CHECK_FAILED\", `Joplin server did not become ready within ${maxRetries * intervalMs}ms`),\n )\n}\n\nexport class JoplinSidecar {\n private config: SidecarConfig\n private childProcess: ChildProcess | 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 ?? 41184,\n apiToken: config.apiToken,\n syncTarget: config.syncTarget,\n syncInterval: config.syncInterval,\n }\n }\n\n async start(): Promise<Either<SidecarError, ChildProcess>> {\n // Step 1: Find CLI\n const cliResult = 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: Configure\n const configResult = 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 process.stderr.write(\"[joplin-sidecar] Configuration applied\\n\")\n\n // Step 3: Spawn server\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\n // Step 4: Wait for ready\n const readyResult = await waitForReady(this.config.apiPort)\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(proc)\n }\n\n async stop(): Promise<Either<Error, true>> {\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 fetch(`http://127.0.0.1:${this.config.apiPort}/ping`)\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 async sync(): Promise<Either<Error, string>> {\n try {\n let cmd: string\n try {\n cmd = execSync(`${whichCmd} joplin`, { encoding: \"utf-8\" }).trim().split(\"\\n\")[0]\n } catch {\n cmd = \"npx joplin\"\n }\n const output = execSync(`${cmd} sync --profile ${this.config.profileDir}`, {\n encoding: \"utf-8\",\n timeout: 120_000,\n })\n return Right(output)\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 { 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 expandPath = (p: string): string => {\n const expanded = expandVars(p)\n return expanded.startsWith(\"~/\") || expanded === \"~\" ? expanded.replace(\"~\", os.homedir()) : 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`)\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 })\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 })\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 })\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 })\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.isRight(startResult)) {\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\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 if (!this.config.sidecar) {\n return \"Sync not available in external mode. Sync is managed by your Joplin instance directly.\"\n }\n const result = await this.config.sidecar.sync()\n return result.fold(\n (error) => `Sync failed: ${error.message}`,\n (output) => `Sync completed successfully.\\n\\n${output}`,\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 { 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\nconst joplinToken = process.env.JOPLIN_TOKEN || `mcp-${crypto.randomUUID().replace(/-/g, \"\").slice(0, 32)}`\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 || 41184\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: 41184,\n apiToken: joplinToken,\n syncTarget: syncTarget.orUndefined() as SyncTarget | undefined,\n })\n\n const startResult = await sidecar.start()\n startResult.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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joplin-mcp-server",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "MCP server for Joplin",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",