@leo000001/claude-code-mcp 2.8.1 → 2.8.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/index.js CHANGED
@@ -914,7 +914,7 @@ var SessionManager = class _SessionManager {
914
914
  };
915
915
 
916
916
  // src/tools/claude-code.ts
917
- import { existsSync as existsSync2, statSync } from "fs";
917
+ import { existsSync as existsSync3, statSync as statSync2 } from "fs";
918
918
 
919
919
  // src/types.ts
920
920
  var EFFORT_LEVELS = ["low", "medium", "high", "max"];
@@ -1949,6 +1949,131 @@ function toSessionCreateParams(input) {
1949
1949
  };
1950
1950
  }
1951
1951
 
1952
+ // src/utils/claude-executable.ts
1953
+ import { accessSync, constants, existsSync as existsSync2, statSync } from "fs";
1954
+ import path3 from "path";
1955
+ var DEFAULT_CLAUDE_COMMAND_ENV = "CLAUDE_CODE_MCP_DEFAULT_CLAUDE_COMMAND";
1956
+ var DEFAULT_CLAUDE_PATH_ENV = "CLAUDE_CODE_MCP_DEFAULT_CLAUDE_PATH";
1957
+ var AUTO_CLAUDE_COMMANDS = ["claude", "claude-internal"];
1958
+ function trimEnv(name) {
1959
+ const value = process.env[name];
1960
+ if (typeof value !== "string") return void 0;
1961
+ const trimmed = value.trim();
1962
+ return trimmed === "" ? void 0 : trimmed;
1963
+ }
1964
+ function normalizeMaybeQuotedPath2(raw) {
1965
+ return path3.normalize(raw.trim().replace(/^"|"$/g, ""));
1966
+ }
1967
+ function normalizeMaybeQuotedToken(raw) {
1968
+ return raw.trim().replace(/^"|"$/g, "");
1969
+ }
1970
+ function isExecutableFile(candidate) {
1971
+ try {
1972
+ const stat = statSync(candidate);
1973
+ if (!stat.isFile()) return false;
1974
+ if (process.platform === "win32") return true;
1975
+ accessSync(candidate, constants.X_OK);
1976
+ return true;
1977
+ } catch {
1978
+ return false;
1979
+ }
1980
+ }
1981
+ function isUsableExecutablePath(candidate) {
1982
+ return isExecutableFile(candidate);
1983
+ }
1984
+ function pathEntries2() {
1985
+ const raw = process.env.PATH;
1986
+ if (typeof raw !== "string" || raw.trim() === "") return [];
1987
+ return raw.split(path3.delimiter).map((entry) => entry.trim().replace(/^"|"$/g, "")).filter(Boolean).map((entry) => path3.normalize(entry));
1988
+ }
1989
+ function windowsExecutableNames(command) {
1990
+ const ext = path3.extname(command);
1991
+ if (ext) return [command];
1992
+ const pathExt = trimEnv("PATHEXT") ?? ".COM;.EXE;.BAT;.CMD";
1993
+ const exts = Array.from(
1994
+ new Set(
1995
+ pathExt.split(";").map((value) => value.trim()).filter(Boolean)
1996
+ )
1997
+ );
1998
+ return exts.map((suffix) => `${command}${suffix.toLowerCase()}`);
1999
+ }
2000
+ function resolveCommandFromPath(command) {
2001
+ if (command.includes("/") || command.includes("\\")) {
2002
+ throw new Error(
2003
+ `${DEFAULT_CLAUDE_COMMAND_ENV} must be a command name without path separators. Use ${DEFAULT_CLAUDE_PATH_ENV} for filesystem paths.`
2004
+ );
2005
+ }
2006
+ const names = process.platform === "win32" ? windowsExecutableNames(command) : [command];
2007
+ const uniqueNames = Array.from(new Set(names.filter(Boolean)));
2008
+ for (const dir of pathEntries2()) {
2009
+ for (const name of uniqueNames) {
2010
+ const candidate = path3.join(dir, name);
2011
+ if (existsSync2(candidate) && isExecutableFile(candidate)) {
2012
+ return path3.normalize(candidate);
2013
+ }
2014
+ }
2015
+ }
2016
+ return void 0;
2017
+ }
2018
+ function resolveConfiguredPath(rawPath) {
2019
+ const normalized = normalizeMaybeQuotedPath2(rawPath);
2020
+ const resolved = path3.isAbsolute(normalized) ? normalized : path3.resolve(normalized);
2021
+ if (!isUsableExecutablePath(resolved)) {
2022
+ throw new Error(`${DEFAULT_CLAUDE_PATH_ENV} does not point to an executable file: ${resolved}`);
2023
+ }
2024
+ return path3.normalize(resolved);
2025
+ }
2026
+ function resolveDefaultClaudeExecutable() {
2027
+ const configuredPath = trimEnv(DEFAULT_CLAUDE_PATH_ENV);
2028
+ const configuredCommandRaw = trimEnv(DEFAULT_CLAUDE_COMMAND_ENV);
2029
+ const configuredCommand = configuredCommandRaw ? normalizeMaybeQuotedToken(configuredCommandRaw) : void 0;
2030
+ if (configuredPath && configuredCommand) {
2031
+ throw new Error(
2032
+ `${DEFAULT_CLAUDE_PATH_ENV} and ${DEFAULT_CLAUDE_COMMAND_ENV} are mutually exclusive; set only one.`
2033
+ );
2034
+ }
2035
+ if (configuredPath) {
2036
+ return {
2037
+ source: "env_path",
2038
+ resolvedPath: resolveConfiguredPath(configuredPath)
2039
+ };
2040
+ }
2041
+ if (configuredCommand) {
2042
+ const resolved = resolveCommandFromPath(configuredCommand);
2043
+ if (!resolved) {
2044
+ throw new Error(
2045
+ `${DEFAULT_CLAUDE_COMMAND_ENV}='${configuredCommand}' was not found in PATH.`
2046
+ );
2047
+ }
2048
+ return {
2049
+ source: "env_command",
2050
+ command: configuredCommand,
2051
+ resolvedPath: resolved
2052
+ };
2053
+ }
2054
+ for (const candidate of AUTO_CLAUDE_COMMANDS) {
2055
+ const resolved = resolveCommandFromPath(candidate);
2056
+ if (!resolved) continue;
2057
+ return {
2058
+ source: candidate === "claude" ? "auto_claude" : "auto_claude_internal",
2059
+ command: candidate,
2060
+ resolvedPath: resolved
2061
+ };
2062
+ }
2063
+ return { source: "sdk_bundled" };
2064
+ }
2065
+ function getDefaultClaudeExecutablePath() {
2066
+ return resolveDefaultClaudeExecutable().resolvedPath;
2067
+ }
2068
+ function checkDefaultClaudeExecutableAvailability() {
2069
+ const resolution = resolveDefaultClaudeExecutable();
2070
+ if (!resolution.resolvedPath) return;
2071
+ const commandText = resolution.command ? `, command=${resolution.command}` : "";
2072
+ console.error(
2073
+ `[executable] Default Claude executable resolved: ${resolution.resolvedPath} (source=${resolution.source}${commandText})`
2074
+ );
2075
+ }
2076
+
1952
2077
  // src/tools/claude-code.ts
1953
2078
  async function executeClaudeCode(input, sessionManager, serverCwd, toolCache, requestSignal) {
1954
2079
  const cwdProvided = input.cwd !== void 0;
@@ -1961,7 +2086,7 @@ async function executeClaudeCode(input, sessionManager, serverCwd, toolCache, re
1961
2086
  };
1962
2087
  }
1963
2088
  const normalizedCwd = normalizeWindowsPathLike(cwd);
1964
- if (cwdProvided && !existsSync2(normalizedCwd)) {
2089
+ if (cwdProvided && !existsSync3(normalizedCwd)) {
1965
2090
  return {
1966
2091
  sessionId: "",
1967
2092
  status: "error",
@@ -1970,7 +2095,7 @@ async function executeClaudeCode(input, sessionManager, serverCwd, toolCache, re
1970
2095
  }
1971
2096
  if (cwdProvided) {
1972
2097
  try {
1973
- if (!statSync(normalizedCwd).isDirectory()) {
2098
+ if (!statSync2(normalizedCwd).isDirectory()) {
1974
2099
  return {
1975
2100
  sessionId: "",
1976
2101
  status: "error",
@@ -2014,7 +2139,7 @@ async function executeClaudeCode(input, sessionManager, serverCwd, toolCache, re
2014
2139
  cwd: normalizeWindowsPathLike(flat.cwd),
2015
2140
  additionalDirectories: flat.additionalDirectories !== void 0 ? normalizeWindowsPathArray(flat.additionalDirectories) : void 0,
2016
2141
  debugFile: flat.debugFile !== void 0 ? normalizeWindowsPathLike(flat.debugFile) : void 0,
2017
- pathToClaudeCodeExecutable: flat.pathToClaudeCodeExecutable !== void 0 ? normalizeWindowsPathLike(flat.pathToClaudeCodeExecutable) : void 0
2142
+ pathToClaudeCodeExecutable: flat.pathToClaudeCodeExecutable !== void 0 ? normalizeWindowsPathLike(flat.pathToClaudeCodeExecutable) : getDefaultClaudeExecutablePath()
2018
2143
  };
2019
2144
  try {
2020
2145
  const handle = consumeQuery({
@@ -2064,19 +2189,19 @@ async function executeClaudeCode(input, sessionManager, serverCwd, toolCache, re
2064
2189
  }
2065
2190
 
2066
2191
  // src/tools/claude-code-reply.ts
2067
- import { existsSync as existsSync3, statSync as statSync2 } from "fs";
2192
+ import { existsSync as existsSync4, statSync as statSync3 } from "fs";
2068
2193
  import os2 from "os";
2069
- import path3 from "path";
2194
+ import path4 from "path";
2070
2195
  function normalizeAndAssertCwd(cwd, contextLabel) {
2071
2196
  const normalizedCwd = normalizeWindowsPathLike(cwd);
2072
2197
  const resolvedCwd = resolvePortableTmpAlias(normalizedCwd);
2073
- if (!existsSync3(resolvedCwd)) {
2198
+ if (!existsSync4(resolvedCwd)) {
2074
2199
  throw new Error(
2075
2200
  `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: ${contextLabel} path does not exist: ${resolvedCwd}`
2076
2201
  );
2077
2202
  }
2078
2203
  try {
2079
- const stat = statSync2(resolvedCwd);
2204
+ const stat = statSync3(resolvedCwd);
2080
2205
  if (!stat.isDirectory()) {
2081
2206
  throw new Error(
2082
2207
  `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: ${contextLabel} must be a directory: ${resolvedCwd}`
@@ -2096,7 +2221,7 @@ function resolvePortableTmpAlias(cwd) {
2096
2221
  const normalized = cwd.replace(/\\/g, "/");
2097
2222
  if (normalized === "/tmp") return os2.tmpdir();
2098
2223
  if (normalized.startsWith("/tmp/")) {
2099
- return path3.join(os2.tmpdir(), normalized.slice("/tmp/".length));
2224
+ return path4.join(os2.tmpdir(), normalized.slice("/tmp/".length));
2100
2225
  }
2101
2226
  return cwd;
2102
2227
  }
@@ -2120,10 +2245,14 @@ function buildOptionsFromDiskResume(dr) {
2120
2245
  throw new Error(`Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: cwd must be provided for disk resume.`);
2121
2246
  }
2122
2247
  const normalizedCwd = normalizeAndAssertCwd(dr.cwd, "disk resume cwd");
2123
- return buildOptions({
2248
+ const options = buildOptions({
2124
2249
  ...dr,
2125
2250
  cwd: normalizedCwd
2126
2251
  });
2252
+ if (options.pathToClaudeCodeExecutable === void 0) {
2253
+ options.pathToClaudeCodeExecutable = getDefaultClaudeExecutablePath();
2254
+ }
2255
+ return options;
2127
2256
  }
2128
2257
  async function executeClaudeCodeReply(input, sessionManager, toolCache, requestSignal) {
2129
2258
  const permissionRequestTimeoutMs = input.permissionRequestTimeoutMs ?? 6e4;
@@ -2290,6 +2419,10 @@ async function executeClaudeCodeReply(input, sessionManager, toolCache, requestS
2290
2419
  const normalizedCwd = normalizeAndAssertCwd(session.cwd, "session cwd");
2291
2420
  const options = buildOptions(session);
2292
2421
  options.cwd = normalizedCwd;
2422
+ const resolvedDefaultExecutable = options.pathToClaudeCodeExecutable ?? getDefaultClaudeExecutablePath();
2423
+ if (resolvedDefaultExecutable !== void 0) {
2424
+ options.pathToClaudeCodeExecutable = resolvedDefaultExecutable;
2425
+ }
2293
2426
  if (input.forkSession) options.forkSession = true;
2294
2427
  if (input.forkSession && !sessionManager.hasCapacityFor(1)) {
2295
2428
  sessionManager.update(input.sessionId, { status: originalStatus, abortController: void 0 });
@@ -2301,8 +2434,14 @@ async function executeClaudeCodeReply(input, sessionManager, toolCache, requestS
2301
2434
  }
2302
2435
  const sourceOverrides = {
2303
2436
  effort: input.effort ?? session.effort,
2304
- thinking: input.thinking ?? session.thinking
2437
+ thinking: input.thinking ?? session.thinking,
2438
+ pathToClaudeCodeExecutable: session.pathToClaudeCodeExecutable ?? resolvedDefaultExecutable ?? void 0
2305
2439
  };
2440
+ if (session.pathToClaudeCodeExecutable === void 0 && resolvedDefaultExecutable !== void 0) {
2441
+ sessionManager.update(input.sessionId, {
2442
+ pathToClaudeCodeExecutable: resolvedDefaultExecutable
2443
+ });
2444
+ }
2306
2445
  if (input.effort !== void 0) options.effort = input.effort;
2307
2446
  if (input.thinking !== void 0) options.thinking = input.thinking;
2308
2447
  if (!input.forkSession && (input.effort !== void 0 || input.thinking !== void 0)) {
@@ -2553,8 +2692,8 @@ function groupByCategory(tools) {
2553
2692
  function buildInternalToolsDescription(tools) {
2554
2693
  const grouped = groupByCategory(tools);
2555
2694
  const categories = Object.keys(grouped).sort((a, b) => a.localeCompare(b));
2556
- let desc = "Start a Claude Code session and return sessionId.\nUse claude_code_check to poll events/results and handle permissions.\nAdjust polling cadence to progress: poll faster while new events/actions are arriving, and slower when the session is quietly thinking.\nLong-running work is normal: Claude Code can keep working for 10+ minutes, especially with high/max effort, so wait for polling to settle before assuming it is stuck.\nIf you want to continue after a run pauses or finishes, use claude_code_reply with the same sessionId instead of starting a brand-new claude_code session.\n\n";
2557
- desc += "Internal tools (authoritative list: includeTools=true in claude_code_check):\n";
2695
+ let desc = "Start a background Claude Code run and return sessionId immediately. No final result is returned here.\nMain loop: call claude_code_check(action='poll'), store nextCursor, and keep polling until status becomes idle, error, or cancelled.\nIf actions[] contains permission requests, answer them with claude_code_check(action='respond_permission'). respond_user_input is not supported.\nAdjust polling cadence to progress: poll faster while new events/actions are arriving, and slower when the session is quietly thinking.\nLong-running work is normal: Claude Code can keep working for 10+ minutes, especially with high/max effort, so wait for polling to settle before assuming it is stuck.\nIf you want to continue after a run pauses or finishes, use claude_code_reply with the same sessionId instead of starting a new claude_code session.\nFor runtime-authoritative tool names, call claude_code_check with pollOptions.includeTools=true.\n\n";
2696
+ desc += "Internal tools (runtime list when includeTools=true):\n";
2558
2697
  for (const category of categories) {
2559
2698
  desc += `
2560
2699
  [${category}]
@@ -2564,7 +2703,7 @@ function buildInternalToolsDescription(tools) {
2564
2703
  `;
2565
2704
  }
2566
2705
  }
2567
- desc += "\nPermission control: allowedTools auto-approves; disallowedTools always denies; others require approval.\n";
2706
+ desc += "\nPermission control: allowedTools pre-approves tools but is not a strict allowlist unless strictAllowedTools=true; disallowedTools always denies; other tools may require approval.\n";
2568
2707
  return desc;
2569
2708
  }
2570
2709
 
@@ -3161,6 +3300,7 @@ function executeClaudeCodeSession(input, sessionManager, requestSignal) {
3161
3300
  // src/resources/register-resources.ts
3162
3301
  import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
3163
3302
  import { createHash } from "crypto";
3303
+ import { basename } from "path";
3164
3304
  var RESOURCE_SCHEME = "claude-code-mcp";
3165
3305
  var RESOURCE_URIS = {
3166
3306
  serverInfo: `${RESOURCE_SCHEME}:///server-info`,
@@ -3312,7 +3452,7 @@ function asVersionedPayload(params) {
3312
3452
  }
3313
3453
  function registerResources(server, deps) {
3314
3454
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
3315
- const resourceSchemaVersion = "1.3";
3455
+ const resourceSchemaVersion = "1.4";
3316
3456
  const mcpProtocolVersion = "2025-03-26";
3317
3457
  const gotchasEntries = buildGotchasEntries();
3318
3458
  const catalogToolNames = new Set(defaultCatalogTools().map((tool) => tool.name));
@@ -3403,7 +3543,7 @@ function registerResources(server, deps) {
3403
3543
  gotchasUri.toString(),
3404
3544
  {
3405
3545
  title: "Gotchas",
3406
- description: "Practical limits and gotchas when using Claude Code via this MCP server.",
3546
+ description: "High-signal failure modes, symptoms, and remedies for this MCP workflow.",
3407
3547
  mimeType: "text/markdown"
3408
3548
  },
3409
3549
  () => asTextResource(
@@ -3411,7 +3551,18 @@ function registerResources(server, deps) {
3411
3551
  [
3412
3552
  "# claude-code-mcp: gotchas",
3413
3553
  "",
3414
- ...gotchasEntries.map((entry) => `- ${entry.title}. ${entry.remedy}`),
3554
+ "Check these before assuming the session is broken.",
3555
+ "",
3556
+ ...gotchasEntries.flatMap((entry) => [
3557
+ `## ${entry.title}`,
3558
+ `- Severity: ${entry.severity}`,
3559
+ `- Applies to: ${entry.appliesTo.join(", ")}`,
3560
+ `- Symptom: ${entry.symptom}`,
3561
+ `- Detection: ${entry.detection}`,
3562
+ `- Remedy: ${entry.remedy}`,
3563
+ ...entry.example ? [`- Example: ${entry.example}`] : [],
3564
+ ""
3565
+ ]),
3415
3566
  ""
3416
3567
  ].join("\n"),
3417
3568
  "text/markdown"
@@ -3424,7 +3575,7 @@ function registerResources(server, deps) {
3424
3575
  quickstartUri.toString(),
3425
3576
  {
3426
3577
  title: "Quickstart",
3427
- description: "Minimal async polling flow for claude_code / claude_code_check.",
3578
+ description: "Canonical async start, poll, permission, and continue flow for MCP callers.",
3428
3579
  mimeType: "text/markdown"
3429
3580
  },
3430
3581
  () => asTextResource(
@@ -3432,26 +3583,43 @@ function registerResources(server, deps) {
3432
3583
  [
3433
3584
  "# claude-code-mcp quickstart",
3434
3585
  "",
3435
- "1. Call `claude_code` with `{ prompt }` and keep `sessionId`.",
3436
- "2. Poll with `claude_code_check(action='poll')` using `nextCursor`.",
3437
- "3. If actions are returned, respond with `claude_code_check(action='respond_permission')`.",
3438
- "4. Continue polling until status becomes `idle` / `error` / `cancelled`.",
3586
+ "## Required state",
3587
+ "",
3588
+ "Persist these client-side:",
3589
+ "- `sessionId`: returned by `claude_code` / `claude_code_reply`.",
3590
+ "- `nextCursor`: returned by each `claude_code_check(action='poll')` call.",
3591
+ "",
3592
+ "## Main loop",
3593
+ "",
3594
+ "1. Call `claude_code` with `{ prompt }`.",
3595
+ "2. Store `sessionId` from the start response.",
3596
+ "3. Poll with `claude_code_check(action='poll')`, passing the previous `nextCursor` back as `cursor`.",
3597
+ "4. If `actions[]` contains a permission request, answer it with `claude_code_check(action='respond_permission')`.",
3598
+ "5. Continue polling until `status` becomes `idle`, `error`, or `cancelled`.",
3599
+ "",
3600
+ "## Continue an existing run",
3439
3601
  "",
3440
- "Usage reminders:",
3441
- "- Claude Code may keep working for 10+ minutes on larger tasks, especially with `effort='high'` or `effort='max'`; keep polling and be patient before treating it as stuck.",
3442
- "- Adjust poll intervals to the current progress: poll faster while new events or permission actions are arriving, and slower while the session is quietly thinking with no new output.",
3443
3602
  "- If Claude Code stops and you want to keep going, call `claude_code_reply` with the existing `sessionId` instead of starting a fresh `claude_code` session.",
3603
+ "- `claude_code_reply` requires a persistent session, or `diskResumeConfig` when disk resume fallback is enabled and the in-memory session is missing.",
3444
3604
  "",
3445
- "Common mistakes:",
3446
- "- Do not call `claude_code_reply` on a non-persistent session; if you plan to continue later, start with `advanced.persistSession=true`.",
3605
+ "## Reminders",
3606
+ "",
3607
+ "- This backend is asynchronous: `claude_code` and `claude_code_reply` start work, and the final result arrives later via polling.",
3608
+ "- Claude Code may keep working for 10+ minutes on larger tasks, especially with `effort='high'` or `effort='max'`; keep polling and be patient before treating it as stuck.",
3609
+ "- Adjust poll intervals to the current progress: poll faster while new events or permission actions are arriving, and slower while the session is quietly thinking with no new output.",
3447
3610
  "- `model` is optional. If omitted, Claude Code chooses the effective model from its own defaults/settings.",
3611
+ "- `allowedTools` is pre-approval by default; set `strictAllowedTools=true` when you need a strict allowlist.",
3612
+ "- `allow_for_session` usually works best when the same tool will be used repeatedly in one session.",
3613
+ "",
3614
+ "## Common mistakes",
3615
+ "",
3616
+ "- Do not call `claude_code_reply` on a non-persistent session if you expect to continue later; keep `advanced.persistSession=true`.",
3448
3617
  "- Store `nextCursor` and feed it back into the next `claude_code_check(action='poll')`; otherwise you will keep replaying old events.",
3449
3618
  "- On Windows, set `CLAUDE_CODE_GIT_BASH_PATH` or Claude Code may fail before the session starts.",
3450
3619
  "- If `decision='allow_for_session'` still shows a permission request, inspect `actions[].suggestions` / `blockedPath`; directory access may need a more specific permission update.",
3451
3620
  "",
3452
3621
  "Notes:",
3453
- "- `respond_user_input` is not supported on this backend.",
3454
- "- `allowedTools` is pre-approval by default; set `strictAllowedTools=true` for strict allowlist behavior.",
3622
+ "- `respond_user_input` is not supported. Use only `respond_permission` for approvals.",
3455
3623
  "- OpenCode/Codex-style clients usually work best when they store `sessionId` + `nextCursor` and answer approvals with `decision=allow_for_session`.",
3456
3624
  "- Prefer `responseMode='delta_compact'` for high-frequency polling."
3457
3625
  ].join("\n"),
@@ -3511,6 +3679,7 @@ function registerResources(server, deps) {
3511
3679
  const diskResumeEnabled = process.env.CLAUDE_CODE_MCP_ALLOW_DISK_RESUME === "1";
3512
3680
  const resumeSecretConfigured = typeof process.env.CLAUDE_CODE_MCP_RESUME_SECRET === "string" && process.env.CLAUDE_CODE_MCP_RESUME_SECRET.trim() !== "";
3513
3681
  const runtimeToolStats = deps.sessionManager.getRuntimeToolStats();
3682
+ const defaultClaudeExecutable = resolveDefaultClaudeExecutable();
3514
3683
  const toolCatalogCount = deps.toolCache.getTools().length;
3515
3684
  const detectedMismatches = [];
3516
3685
  if (runtimeToolStats.sessionsWithInitTools > 0 && runtimeToolStats.runtimeDiscoveredUniqueCount < toolCatalogCount) {
@@ -3539,6 +3708,12 @@ function registerResources(server, deps) {
3539
3708
  enabled: diskResumeEnabled,
3540
3709
  resumeSecretConfigured
3541
3710
  },
3711
+ defaultClaudeExecutable: {
3712
+ source: defaultClaudeExecutable.source,
3713
+ command: defaultClaudeExecutable.command,
3714
+ resolvedFileName: defaultClaudeExecutable.resolvedPath ? basename(defaultClaudeExecutable.resolvedPath) : void 0,
3715
+ usingBundled: defaultClaudeExecutable.resolvedPath === void 0
3716
+ },
3542
3717
  features: {
3543
3718
  resources: true,
3544
3719
  resourceTemplates: true,
@@ -3565,8 +3740,10 @@ function registerResources(server, deps) {
3565
3740
  },
3566
3741
  guidance: [
3567
3742
  "Some clients cache tool descriptions at connect time. Prefer claude_code_check(pollOptions.includeTools=true) for runtime-authoritative tool lists.",
3743
+ "Treat tool descriptions and MCP resources as agent-visible guidance; do not assume README-level documentation is visible to the model.",
3568
3744
  "Use allowedTools/disallowedTools only with exact runtime tool names.",
3569
3745
  "Set strictAllowedTools=true when you need allowedTools to behave as a strict allowlist.",
3746
+ "Default Claude executable selection prefers request path, then CLAUDE_CODE_MCP_DEFAULT_CLAUDE_PATH, then CLAUDE_CODE_MCP_DEFAULT_CLAUDE_COMMAND, then auto-detected 'claude'/'claude-internal', then SDK-bundled.",
3570
3747
  "This server assumes MCP client and server run on the same machine/platform.",
3571
3748
  "For high-frequency status checks, prefer responseMode='delta_compact'.",
3572
3749
  "respond_user_input is not supported on this backend; use poll/respond_permission flow."
@@ -3735,7 +3912,8 @@ function registerResources(server, deps) {
3735
3912
  detectedMismatches: [],
3736
3913
  recommendations: [
3737
3914
  "Prefer responseMode='delta_compact' for fast status loops.",
3738
- "Enable pollOptions.includeTools=true when exact runtime tool names are required."
3915
+ "Enable pollOptions.includeTools=true when exact runtime tool names are required.",
3916
+ "Do not assume human-facing README guidance is visible to the agent; keep critical calling rules in tool descriptions or MCP resources."
3739
3917
  ]
3740
3918
  };
3741
3919
  }
@@ -3746,7 +3924,8 @@ function registerResources(server, deps) {
3746
3924
  recommendations: [
3747
3925
  "Use resources and resource templates for low-latency diagnostics.",
3748
3926
  "Use allowedTools/disallowedTools with exact runtime names.",
3749
- "Enable strictAllowedTools when running in locked-down governance mode."
3927
+ "Enable strictAllowedTools when running in locked-down governance mode.",
3928
+ "Keep protocol-critical calling rules in tool descriptions or resources, not only in README-like docs."
3750
3929
  ]
3751
3930
  };
3752
3931
  }
@@ -3767,7 +3946,8 @@ function registerResources(server, deps) {
3767
3946
  detectedMismatches: [],
3768
3947
  recommendations: [
3769
3948
  "Persist nextCursor and de-duplicate by event.id.",
3770
- "Use responseMode='delta_compact' for high-frequency polling, full mode only for diagnostics."
3949
+ "Use responseMode='delta_compact' for high-frequency polling, full mode only for diagnostics.",
3950
+ "Do not assume repository docs are model-visible; tool descriptions and resources are safer places for runtime guidance."
3771
3951
  ]
3772
3952
  };
3773
3953
  })();
@@ -3795,7 +3975,7 @@ function registerResources(server, deps) {
3795
3975
  }
3796
3976
 
3797
3977
  // src/server.ts
3798
- var SERVER_VERSION = true ? "2.8.1" : "0.0.0-dev";
3978
+ var SERVER_VERSION = true ? "2.8.3" : "0.0.0-dev";
3799
3979
  function createServerContext(serverCwd) {
3800
3980
  const sessionManager = new SessionManager();
3801
3981
  const server = new McpServer(
@@ -3803,7 +3983,7 @@ function createServerContext(serverCwd) {
3803
3983
  name: "claude-code-mcp",
3804
3984
  version: SERVER_VERSION,
3805
3985
  title: "Claude Code MCP",
3806
- description: "MCP server that runs Claude Code via the Claude Agent SDK with async polling and interactive permissions.",
3986
+ description: "MCP server that runs Claude Code via the Claude Agent SDK. Starts and replies return quickly; callers poll with claude_code_check and answer permission requests explicitly.",
3807
3987
  websiteUrl: "https://github.com/xihuai18/claude-code-mcp",
3808
3988
  icons: []
3809
3989
  },
@@ -3833,11 +4013,13 @@ function createServerContext(serverCwd) {
3833
4013
  const agentDefinitionSchema = z.object({
3834
4014
  description: z.string(),
3835
4015
  prompt: z.string(),
3836
- tools: z.array(z.string()).optional().describe("Default: inherit"),
4016
+ tools: z.array(z.string()).optional().describe("Allowed tool names for this subagent. Default: inherit"),
3837
4017
  disallowedTools: z.array(z.string()).optional().describe("Default: none"),
3838
4018
  model: z.string().optional().describe("Default: inherit"),
3839
4019
  maxTurns: z.number().int().positive().optional().describe("Default: none"),
3840
- mcpServers: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional().describe("Default: inherit"),
4020
+ mcpServers: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional().describe(
4021
+ "Additional MCP server names or inline process-transport specs for this subagent. Default: inherit"
4022
+ ),
3841
4023
  skills: z.array(z.string()).optional().describe("Default: none"),
3842
4024
  criticalSystemReminder_EXPERIMENTAL: z.string().optional().describe("Default: none")
3843
4025
  });
@@ -3860,7 +4042,7 @@ function createServerContext(serverCwd) {
3860
4042
  z.object({ type: z.literal("adaptive") }),
3861
4043
  z.object({
3862
4044
  type: z.literal("enabled"),
3863
- budgetTokens: z.number().int().positive()
4045
+ budgetTokens: z.number().int().positive().optional()
3864
4046
  }),
3865
4047
  z.object({ type: z.literal("disabled") })
3866
4048
  ]);
@@ -3871,51 +4053,71 @@ function createServerContext(serverCwd) {
3871
4053
  schema: z.record(z.string(), z.unknown())
3872
4054
  });
3873
4055
  const sharedOptionFieldsSchemaShape = {
3874
- tools: toolsConfigSchema.optional().describe("Tool set. Default: SDK"),
4056
+ tools: toolsConfigSchema.optional().describe(
4057
+ "Visible built-in tool set. Use this to restrict what Claude can see/call: string[] of exact tool names, [] to disable built-ins, or {type:'preset',preset:'claude_code'}. Default: SDK"
4058
+ ),
3875
4059
  persistSession: z.boolean().optional().describe("Default: true"),
3876
- agents: z.record(z.string(), agentDefinitionSchema).optional().describe("Default: none"),
4060
+ agents: z.record(z.string(), agentDefinitionSchema).optional().describe("Custom subagents keyed by agent name. Default: none"),
3877
4061
  agent: z.string().optional().describe("Default: none"),
3878
4062
  maxBudgetUsd: z.number().positive().optional().describe("Default: none"),
3879
4063
  betas: z.array(z.string()).optional().describe("Default: none"),
3880
4064
  additionalDirectories: z.array(z.string()).optional().describe("Default: none"),
3881
- outputFormat: outputFormatSchema.optional().describe("Default: none"),
3882
- pathToClaudeCodeExecutable: z.string().optional().describe("Default: SDK-bundled"),
3883
- mcpServers: z.record(z.string(), z.record(z.string(), z.unknown())).optional().describe("Default: none"),
3884
- sandbox: z.record(z.string(), z.unknown()).optional().describe("Default: none"),
4065
+ outputFormat: outputFormatSchema.optional().describe("Structured output config: {type:'json_schema', schema:{...}}. Default: none"),
4066
+ pathToClaudeCodeExecutable: z.string().optional().describe(
4067
+ "Explicit Claude executable path. Default: auto-detect 'claude', then 'claude-internal', else SDK-bundled. Server env vars can override the default."
4068
+ ),
4069
+ mcpServers: z.record(z.string(), z.record(z.string(), z.unknown())).optional().describe("MCP server configs keyed by server name. Default: none"),
4070
+ sandbox: z.record(z.string(), z.unknown()).optional().describe(
4071
+ "Sandbox behavior config object. This controls sandbox behavior, not the actual allow/deny permission rules. Default: none"
4072
+ ),
3885
4073
  fallbackModel: z.string().optional().describe("Default: none"),
3886
4074
  enableFileCheckpointing: z.boolean().optional().describe("Default: false"),
3887
- toolConfig: z.record(z.string(), z.unknown()).optional().describe("Default: none"),
4075
+ toolConfig: z.record(z.string(), z.unknown()).optional().describe(
4076
+ "Per-tool built-in config object, e.g. {askUserQuestion:{previewFormat:'markdown'|'html'}}. Default: none"
4077
+ ),
3888
4078
  includePartialMessages: z.boolean().optional().describe("Default: false"),
3889
4079
  promptSuggestions: z.boolean().optional().describe("Default: false"),
3890
4080
  agentProgressSummaries: z.boolean().optional().describe("Default: false"),
3891
4081
  strictMcpConfig: z.boolean().optional().describe("Default: false"),
3892
- settings: z.union([z.string(), z.record(z.string(), z.unknown())]).optional().describe("Default: none"),
4082
+ settings: z.union([z.string(), z.record(z.string(), z.unknown())]).optional().describe(
4083
+ "Path to a settings JSON file or an inline settings object. Loaded as highest-priority flag settings. Default: none"
4084
+ ),
3893
4085
  settingSources: z.array(z.enum(["user", "project", "local"])).optional().describe("Default: ['user','project','local']. []=isolation"),
3894
4086
  debug: z.boolean().optional().describe("Default: false"),
3895
- debugFile: z.string().optional().describe("Default: none"),
3896
- env: z.record(z.string(), z.string().optional()).optional().describe("Default: none")
4087
+ debugFile: z.string().optional().describe("Write debug logs to this file and implicitly enable debug. Default: none"),
4088
+ env: z.record(z.string(), z.string().optional()).optional().describe(
4089
+ "Environment variables merged over process.env before launching Claude Code. Default: none"
4090
+ )
3897
4091
  };
3898
4092
  const advancedOptionFieldsSchemaShape = {
3899
4093
  ...sharedOptionFieldsSchemaShape
3900
4094
  };
3901
4095
  const diskResumeOptionFieldsSchemaShape = {
3902
4096
  ...sharedOptionFieldsSchemaShape,
3903
- effort: effortOptionSchema.describe("Default: SDK"),
3904
- thinking: thinkingOptionSchema.describe("Default: SDK")
4097
+ effort: effortOptionSchema.describe(
4098
+ "Effort string: 'low' | 'medium' | 'high' | 'max'. Default: SDK"
4099
+ ),
4100
+ thinking: thinkingOptionSchema.describe(
4101
+ "Thinking config object, not a string. Use {type:'adaptive'} | {type:'enabled', budgetTokens?:N} | {type:'disabled'}. Default: SDK"
4102
+ )
3905
4103
  };
3906
4104
  const advancedOptionsSchema = z.object({
3907
4105
  ...advancedOptionFieldsSchemaShape,
3908
- sessionInitTimeoutMs: z.number().int().positive().optional().describe("Default: 10000")
4106
+ sessionInitTimeoutMs: z.number().int().positive().optional().describe("Server init wait timeout in ms; not forwarded to SDK Options. Default: 10000")
3909
4107
  }).optional().describe("Default: none");
3910
4108
  const diskResumeConfigSchema = z.object({
3911
4109
  resumeToken: z.string(),
3912
4110
  cwd: z.string(),
3913
- allowedTools: z.array(z.string()).optional().describe("Default: []"),
3914
- disallowedTools: z.array(z.string()).optional().describe("Default: []"),
3915
- strictAllowedTools: z.boolean().optional().describe("Default: false"),
4111
+ allowedTools: z.array(z.string()).optional().describe("Pre-approved tool names for resumed execution. Default: []"),
4112
+ disallowedTools: z.array(z.string()).optional().describe("Always-denied tool names. Default: []"),
4113
+ strictAllowedTools: z.boolean().optional().describe(
4114
+ "Server-side strict allowlist toggle; not forwarded to SDK Options. Default: false"
4115
+ ),
3916
4116
  maxTurns: z.number().int().positive().optional().describe("Default: SDK"),
3917
4117
  model: z.string().optional().describe("Default: SDK"),
3918
- systemPrompt: systemPromptSchema.optional().describe("Default: SDK"),
4118
+ systemPrompt: systemPromptSchema.optional().describe(
4119
+ "System prompt config: string, {type:'preset',preset:'claude_code'}, or {type:'preset',preset:'claude_code',append:'...'}. Default: SDK"
4120
+ ),
3919
4121
  resumeSessionAt: z.string().optional().describe("Default: none"),
3920
4122
  ...diskResumeOptionFieldsSchemaShape
3921
4123
  }).optional().describe("Default: none");
@@ -3977,18 +4179,28 @@ function createServerContext(serverCwd) {
3977
4179
  description: buildInternalToolsDescription(toolCache.getTools()),
3978
4180
  inputSchema: {
3979
4181
  prompt: z.string().describe(
3980
- "Prompt. Long runs are normal; keep polling, adapt poll cadence to current progress, and reuse sessionId with claude_code_reply if you want to continue."
4182
+ "Prompt for a new background run. The final result arrives later via claude_code_check, not this call. Store nextCursor from polls and reuse sessionId with claude_code_reply if you want to continue later."
3981
4183
  ),
3982
4184
  cwd: z.string().optional().describe("Working dir. Default: server cwd"),
3983
- allowedTools: z.array(z.string()).optional().describe("Default: []"),
3984
- disallowedTools: z.array(z.string()).optional().describe("Default: []"),
3985
- strictAllowedTools: z.boolean().optional().describe("Default: false"),
4185
+ allowedTools: z.array(z.string()).optional().describe(
4186
+ "Pre-approved tool names. Default: []. This is not a strict allowlist unless strictAllowedTools=true."
4187
+ ),
4188
+ disallowedTools: z.array(z.string()).optional().describe("Always-denied tool names. Default: []"),
4189
+ strictAllowedTools: z.boolean().optional().describe("Default: false. When true, tools outside allowedTools are denied."),
3986
4190
  maxTurns: z.number().int().positive().optional().describe("Default: SDK"),
3987
4191
  model: z.string().optional().describe("Default: SDK"),
3988
- effort: effortOptionSchema.describe("Default: SDK"),
3989
- thinking: thinkingOptionSchema.describe("Default: SDK"),
3990
- systemPrompt: systemPromptSchema.optional().describe("Default: SDK"),
3991
- permissionRequestTimeoutMs: z.number().int().positive().optional().describe("Default: 60000, clamped to 300000"),
4192
+ effort: effortOptionSchema.describe(
4193
+ "Effort string: 'low' | 'medium' | 'high' | 'max'. Default: SDK"
4194
+ ),
4195
+ thinking: thinkingOptionSchema.describe(
4196
+ "Thinking config object, not a string. Use {type:'adaptive'} | {type:'enabled', budgetTokens?:N} | {type:'disabled'}. Default: SDK"
4197
+ ),
4198
+ systemPrompt: systemPromptSchema.optional().describe(
4199
+ "System prompt config: string, {type:'preset',preset:'claude_code'}, or {type:'preset',preset:'claude_code',append:'...'}. Default: SDK"
4200
+ ),
4201
+ permissionRequestTimeoutMs: z.number().int().positive().optional().describe(
4202
+ "Server permission wait timeout in ms; not forwarded to SDK Options. Default: 60000, clamped to 300000"
4203
+ ),
3992
4204
  advanced: advancedOptionsSchema
3993
4205
  },
3994
4206
  outputSchema: startResultSchema,
@@ -4042,18 +4254,26 @@ function createServerContext(serverCwd) {
4042
4254
  server.registerTool(
4043
4255
  "claude_code_reply",
4044
4256
  {
4045
- description: "Send a follow-up to an existing session. Reuse the same sessionId instead of starting a new claude_code session when you want to continue. Returns immediately; use claude_code_check to poll, and adjust poll cadence to current progress.",
4257
+ description: "Send a follow-up to an existing session. Reuse the same sessionId instead of starting a new claude_code session when you want to continue. Returns immediately; poll with claude_code_check. Use diskResumeConfig only when the in-memory session is missing and disk resume is enabled.",
4046
4258
  inputSchema: {
4047
4259
  sessionId: z.string().describe(
4048
- "Session ID from the existing conversation you want to continue; prefer this over starting a fresh claude_code session."
4260
+ "Session ID from claude_code or an earlier claude_code_reply. Prefer this over starting a fresh claude_code session. Reply works only for persistent sessions, or with diskResumeConfig when disk resume fallback is enabled."
4049
4261
  ),
4050
4262
  prompt: z.string().describe("Follow-up prompt for the existing session."),
4051
4263
  forkSession: z.boolean().optional().describe("Default: false"),
4052
- effort: effortOptionSchema.describe("Default: SDK"),
4053
- thinking: thinkingOptionSchema.describe("Default: SDK"),
4054
- sessionInitTimeoutMs: z.number().int().positive().optional().describe("Default: 10000"),
4055
- permissionRequestTimeoutMs: z.number().int().positive().optional().describe("Default: 60000, clamped to 300000"),
4056
- diskResumeConfig: diskResumeConfigSchema
4264
+ effort: effortOptionSchema.describe(
4265
+ "Effort string: 'low' | 'medium' | 'high' | 'max'. Default: SDK"
4266
+ ),
4267
+ thinking: thinkingOptionSchema.describe(
4268
+ "Thinking config object, not a string. Use {type:'adaptive'} | {type:'enabled', budgetTokens?:N} | {type:'disabled'}. Default: SDK"
4269
+ ),
4270
+ sessionInitTimeoutMs: z.number().int().positive().optional().describe("Default: 10000. Applies when forkSession=true."),
4271
+ permissionRequestTimeoutMs: z.number().int().positive().optional().describe(
4272
+ "Server permission wait timeout in ms; not forwarded to SDK Options. Default: 60000, clamped to 300000"
4273
+ ),
4274
+ diskResumeConfig: diskResumeConfigSchema.describe(
4275
+ "Default: none. Use only when the in-memory session is missing and the server allows disk resume."
4276
+ )
4057
4277
  },
4058
4278
  outputSchema: startResultSchema,
4059
4279
  annotations: {
@@ -4102,9 +4322,11 @@ function createServerContext(serverCwd) {
4102
4322
  {
4103
4323
  description: "List, inspect, cancel, or interrupt sessions.",
4104
4324
  inputSchema: {
4105
- action: z.enum(SESSION_ACTIONS),
4106
- sessionId: z.string().optional().describe("Required for get/cancel/interrupt"),
4107
- includeSensitive: z.boolean().optional().describe("Default: false")
4325
+ action: z.enum(SESSION_ACTIONS).describe("Session operation: list, get, cancel, or interrupt."),
4326
+ sessionId: z.string().optional().describe("Required for get/cancel/interrupt."),
4327
+ includeSensitive: z.boolean().optional().describe(
4328
+ "Default: false. Exposes more session fields, but some secrets remain redacted."
4329
+ )
4108
4330
  },
4109
4331
  outputSchema: sessionResultSchema,
4110
4332
  annotations: {
@@ -4150,21 +4372,31 @@ function createServerContext(serverCwd) {
4150
4372
  server.registerTool(
4151
4373
  "claude_code_check",
4152
4374
  {
4153
- description: "Poll session events or respond to permission requests. Long Claude Code runs are normal, especially with high/max effort; poll faster when progress is active and slower when the session is quietly thinking.",
4375
+ description: "Poll session state or answer a pending permission request. Main loop: call action='poll', persist nextCursor, and use action='respond_permission' for approvals. respond_user_input is not supported.",
4154
4376
  inputSchema: {
4155
- action: z.enum(CHECK_ACTIONS),
4156
- sessionId: z.string().describe("Session ID"),
4157
- cursor: z.number().int().nonnegative().optional().describe("Default: 0"),
4377
+ action: z.enum(CHECK_ACTIONS).describe(
4378
+ "'poll' fetches new events/actions/result; 'respond_permission' answers one pending permission request."
4379
+ ),
4380
+ sessionId: z.string().describe("Session ID returned by claude_code or claude_code_reply."),
4381
+ cursor: z.number().int().nonnegative().optional().describe(
4382
+ "Default: 0. Pass the previous nextCursor to avoid replaying old buffered events."
4383
+ ),
4158
4384
  responseMode: z.enum(CHECK_RESPONSE_MODES).optional().describe(
4159
- "Default: 'minimal'. Use 'delta_compact' for lightweight polling, especially for faster adaptive polling loops."
4385
+ "Default: 'minimal'. Use 'delta_compact' for lightweight high-frequency polling; use 'full' mainly for diagnostics."
4160
4386
  ),
4161
4387
  maxEvents: z.number().int().positive().optional().describe("Default: 200 (minimal), unlimited (full/delta_compact)"),
4162
- requestId: z.string().optional().describe("Default: none"),
4163
- decision: z.enum(["allow", "deny", "allow_for_session"]).optional().describe("Default: none"),
4164
- denyMessage: z.string().optional().describe("Default: 'Permission denied by caller'"),
4165
- interrupt: z.boolean().optional().describe("Default: false"),
4388
+ requestId: z.string().optional().describe("Default: none. Required for action='respond_permission'."),
4389
+ decision: z.enum(["allow", "deny", "allow_for_session"]).optional().describe(
4390
+ "Default: none. Required for action='respond_permission'. 'allow_for_session' reduces repeated prompts in the same session."
4391
+ ),
4392
+ denyMessage: z.string().optional().describe("Default: 'Permission denied by caller'. Used only with decision='deny'."),
4393
+ interrupt: z.boolean().optional().describe(
4394
+ "Default: false. When true, a deny decision also interrupts the whole session."
4395
+ ),
4166
4396
  pollOptions: z.object({
4167
- includeTools: z.boolean().optional().describe("Default: false"),
4397
+ includeTools: z.boolean().optional().describe(
4398
+ "Default: false. When true, include runtime-discovered availableTools; use this when exact tool names matter."
4399
+ ),
4168
4400
  includeEvents: z.boolean().optional().describe("Default: true"),
4169
4401
  includeActions: z.boolean().optional().describe("Default: true"),
4170
4402
  includeResult: z.boolean().optional().describe("Default: true"),
@@ -4174,11 +4406,13 @@ function createServerContext(serverCwd) {
4174
4406
  includeTerminalEvents: z.boolean().optional().describe("Default: full=true, minimal/delta_compact=false"),
4175
4407
  includeProgressEvents: z.boolean().optional().describe("Default: full=true, minimal/delta_compact=false"),
4176
4408
  maxBytes: z.number().int().positive().optional().describe("Default: unlimited")
4177
- }).optional().describe("Default: none"),
4409
+ }).optional().describe("Default: none. Advanced polling payload controls for action='poll'."),
4178
4410
  permissionOptions: z.object({
4179
4411
  updatedInput: z.record(z.string(), z.unknown()).optional().describe("Default: none"),
4180
4412
  updatedPermissions: z.array(z.record(z.string(), z.unknown())).optional().describe("Default: none")
4181
- }).optional().describe("Default: none")
4413
+ }).optional().describe(
4414
+ "Default: none. Advanced permission response overrides for action='respond_permission'."
4415
+ )
4182
4416
  },
4183
4417
  outputSchema: checkResultSchema,
4184
4418
  annotations: {
@@ -4414,10 +4648,11 @@ async function main() {
4414
4648
  process.stdin.on("error", handleStdinError);
4415
4649
  process.stdin.on("end", onStdinEnd);
4416
4650
  process.stdin.on("close", onStdinClose);
4651
+ checkWindowsBashAvailability();
4652
+ checkDefaultClaudeExecutableAvailability();
4417
4653
  await server.connect(transport);
4418
4654
  server.sendToolListChanged();
4419
4655
  server.sendResourceListChanged();
4420
- checkWindowsBashAvailability();
4421
4656
  try {
4422
4657
  if (transport && server) {
4423
4658
  await server.sendLoggingMessage({