@leo000001/claude-code-mcp 2.8.0 → 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.\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,14 +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",
3601
+ "",
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.",
3604
+ "",
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.",
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`.",
3617
+ "- Store `nextCursor` and feed it back into the next `claude_code_check(action='poll')`; otherwise you will keep replaying old events.",
3618
+ "- On Windows, set `CLAUDE_CODE_GIT_BASH_PATH` or Claude Code may fail before the session starts.",
3619
+ "- If `decision='allow_for_session'` still shows a permission request, inspect `actions[].suggestions` / `blockedPath`; directory access may need a more specific permission update.",
3439
3620
  "",
3440
3621
  "Notes:",
3441
- "- `respond_user_input` is not supported on this backend.",
3442
- "- `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.",
3443
3623
  "- OpenCode/Codex-style clients usually work best when they store `sessionId` + `nextCursor` and answer approvals with `decision=allow_for_session`.",
3444
3624
  "- Prefer `responseMode='delta_compact'` for high-frequency polling."
3445
3625
  ].join("\n"),
@@ -3499,6 +3679,7 @@ function registerResources(server, deps) {
3499
3679
  const diskResumeEnabled = process.env.CLAUDE_CODE_MCP_ALLOW_DISK_RESUME === "1";
3500
3680
  const resumeSecretConfigured = typeof process.env.CLAUDE_CODE_MCP_RESUME_SECRET === "string" && process.env.CLAUDE_CODE_MCP_RESUME_SECRET.trim() !== "";
3501
3681
  const runtimeToolStats = deps.sessionManager.getRuntimeToolStats();
3682
+ const defaultClaudeExecutable = resolveDefaultClaudeExecutable();
3502
3683
  const toolCatalogCount = deps.toolCache.getTools().length;
3503
3684
  const detectedMismatches = [];
3504
3685
  if (runtimeToolStats.sessionsWithInitTools > 0 && runtimeToolStats.runtimeDiscoveredUniqueCount < toolCatalogCount) {
@@ -3527,6 +3708,12 @@ function registerResources(server, deps) {
3527
3708
  enabled: diskResumeEnabled,
3528
3709
  resumeSecretConfigured
3529
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
+ },
3530
3717
  features: {
3531
3718
  resources: true,
3532
3719
  resourceTemplates: true,
@@ -3553,8 +3740,10 @@ function registerResources(server, deps) {
3553
3740
  },
3554
3741
  guidance: [
3555
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.",
3556
3744
  "Use allowedTools/disallowedTools only with exact runtime tool names.",
3557
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.",
3558
3747
  "This server assumes MCP client and server run on the same machine/platform.",
3559
3748
  "For high-frequency status checks, prefer responseMode='delta_compact'.",
3560
3749
  "respond_user_input is not supported on this backend; use poll/respond_permission flow."
@@ -3723,7 +3912,8 @@ function registerResources(server, deps) {
3723
3912
  detectedMismatches: [],
3724
3913
  recommendations: [
3725
3914
  "Prefer responseMode='delta_compact' for fast status loops.",
3726
- "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."
3727
3917
  ]
3728
3918
  };
3729
3919
  }
@@ -3734,7 +3924,8 @@ function registerResources(server, deps) {
3734
3924
  recommendations: [
3735
3925
  "Use resources and resource templates for low-latency diagnostics.",
3736
3926
  "Use allowedTools/disallowedTools with exact runtime names.",
3737
- "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."
3738
3929
  ]
3739
3930
  };
3740
3931
  }
@@ -3755,7 +3946,8 @@ function registerResources(server, deps) {
3755
3946
  detectedMismatches: [],
3756
3947
  recommendations: [
3757
3948
  "Persist nextCursor and de-duplicate by event.id.",
3758
- "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."
3759
3951
  ]
3760
3952
  };
3761
3953
  })();
@@ -3783,7 +3975,7 @@ function registerResources(server, deps) {
3783
3975
  }
3784
3976
 
3785
3977
  // src/server.ts
3786
- var SERVER_VERSION = true ? "2.8.0" : "0.0.0-dev";
3978
+ var SERVER_VERSION = true ? "2.8.3" : "0.0.0-dev";
3787
3979
  function createServerContext(serverCwd) {
3788
3980
  const sessionManager = new SessionManager();
3789
3981
  const server = new McpServer(
@@ -3791,7 +3983,7 @@ function createServerContext(serverCwd) {
3791
3983
  name: "claude-code-mcp",
3792
3984
  version: SERVER_VERSION,
3793
3985
  title: "Claude Code MCP",
3794
- 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.",
3795
3987
  websiteUrl: "https://github.com/xihuai18/claude-code-mcp",
3796
3988
  icons: []
3797
3989
  },
@@ -3821,11 +4013,13 @@ function createServerContext(serverCwd) {
3821
4013
  const agentDefinitionSchema = z.object({
3822
4014
  description: z.string(),
3823
4015
  prompt: z.string(),
3824
- 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"),
3825
4017
  disallowedTools: z.array(z.string()).optional().describe("Default: none"),
3826
4018
  model: z.string().optional().describe("Default: inherit"),
3827
4019
  maxTurns: z.number().int().positive().optional().describe("Default: none"),
3828
- 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
+ ),
3829
4023
  skills: z.array(z.string()).optional().describe("Default: none"),
3830
4024
  criticalSystemReminder_EXPERIMENTAL: z.string().optional().describe("Default: none")
3831
4025
  });
@@ -3848,7 +4042,7 @@ function createServerContext(serverCwd) {
3848
4042
  z.object({ type: z.literal("adaptive") }),
3849
4043
  z.object({
3850
4044
  type: z.literal("enabled"),
3851
- budgetTokens: z.number().int().positive()
4045
+ budgetTokens: z.number().int().positive().optional()
3852
4046
  }),
3853
4047
  z.object({ type: z.literal("disabled") })
3854
4048
  ]);
@@ -3859,51 +4053,71 @@ function createServerContext(serverCwd) {
3859
4053
  schema: z.record(z.string(), z.unknown())
3860
4054
  });
3861
4055
  const sharedOptionFieldsSchemaShape = {
3862
- 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
+ ),
3863
4059
  persistSession: z.boolean().optional().describe("Default: true"),
3864
- 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"),
3865
4061
  agent: z.string().optional().describe("Default: none"),
3866
4062
  maxBudgetUsd: z.number().positive().optional().describe("Default: none"),
3867
4063
  betas: z.array(z.string()).optional().describe("Default: none"),
3868
4064
  additionalDirectories: z.array(z.string()).optional().describe("Default: none"),
3869
- outputFormat: outputFormatSchema.optional().describe("Default: none"),
3870
- pathToClaudeCodeExecutable: z.string().optional().describe("Default: SDK-bundled"),
3871
- mcpServers: z.record(z.string(), z.record(z.string(), z.unknown())).optional().describe("Default: none"),
3872
- 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
+ ),
3873
4073
  fallbackModel: z.string().optional().describe("Default: none"),
3874
4074
  enableFileCheckpointing: z.boolean().optional().describe("Default: false"),
3875
- 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
+ ),
3876
4078
  includePartialMessages: z.boolean().optional().describe("Default: false"),
3877
4079
  promptSuggestions: z.boolean().optional().describe("Default: false"),
3878
4080
  agentProgressSummaries: z.boolean().optional().describe("Default: false"),
3879
4081
  strictMcpConfig: z.boolean().optional().describe("Default: false"),
3880
- 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
+ ),
3881
4085
  settingSources: z.array(z.enum(["user", "project", "local"])).optional().describe("Default: ['user','project','local']. []=isolation"),
3882
4086
  debug: z.boolean().optional().describe("Default: false"),
3883
- debugFile: z.string().optional().describe("Default: none"),
3884
- 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
+ )
3885
4091
  };
3886
4092
  const advancedOptionFieldsSchemaShape = {
3887
4093
  ...sharedOptionFieldsSchemaShape
3888
4094
  };
3889
4095
  const diskResumeOptionFieldsSchemaShape = {
3890
4096
  ...sharedOptionFieldsSchemaShape,
3891
- effort: effortOptionSchema.describe("Default: SDK"),
3892
- 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
+ )
3893
4103
  };
3894
4104
  const advancedOptionsSchema = z.object({
3895
4105
  ...advancedOptionFieldsSchemaShape,
3896
- 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")
3897
4107
  }).optional().describe("Default: none");
3898
4108
  const diskResumeConfigSchema = z.object({
3899
4109
  resumeToken: z.string(),
3900
4110
  cwd: z.string(),
3901
- allowedTools: z.array(z.string()).optional().describe("Default: []"),
3902
- disallowedTools: z.array(z.string()).optional().describe("Default: []"),
3903
- 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
+ ),
3904
4116
  maxTurns: z.number().int().positive().optional().describe("Default: SDK"),
3905
4117
  model: z.string().optional().describe("Default: SDK"),
3906
- 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
+ ),
3907
4121
  resumeSessionAt: z.string().optional().describe("Default: none"),
3908
4122
  ...diskResumeOptionFieldsSchemaShape
3909
4123
  }).optional().describe("Default: none");
@@ -3964,17 +4178,29 @@ function createServerContext(serverCwd) {
3964
4178
  {
3965
4179
  description: buildInternalToolsDescription(toolCache.getTools()),
3966
4180
  inputSchema: {
3967
- prompt: z.string().describe("Prompt"),
4181
+ prompt: z.string().describe(
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."
4183
+ ),
3968
4184
  cwd: z.string().optional().describe("Working dir. Default: server cwd"),
3969
- allowedTools: z.array(z.string()).optional().describe("Default: []"),
3970
- disallowedTools: z.array(z.string()).optional().describe("Default: []"),
3971
- 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."),
3972
4190
  maxTurns: z.number().int().positive().optional().describe("Default: SDK"),
3973
4191
  model: z.string().optional().describe("Default: SDK"),
3974
- effort: effortOptionSchema.describe("Default: SDK"),
3975
- thinking: thinkingOptionSchema.describe("Default: SDK"),
3976
- systemPrompt: systemPromptSchema.optional().describe("Default: SDK"),
3977
- 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
+ ),
3978
4204
  advanced: advancedOptionsSchema
3979
4205
  },
3980
4206
  outputSchema: startResultSchema,
@@ -4028,16 +4254,26 @@ function createServerContext(serverCwd) {
4028
4254
  server.registerTool(
4029
4255
  "claude_code_reply",
4030
4256
  {
4031
- description: "Send a follow-up to an existing session. Returns immediately; use claude_code_check to poll.",
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.",
4032
4258
  inputSchema: {
4033
- sessionId: z.string().describe("Session ID"),
4034
- prompt: z.string().describe("Prompt"),
4259
+ sessionId: z.string().describe(
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."
4261
+ ),
4262
+ prompt: z.string().describe("Follow-up prompt for the existing session."),
4035
4263
  forkSession: z.boolean().optional().describe("Default: false"),
4036
- effort: effortOptionSchema.describe("Default: SDK"),
4037
- thinking: thinkingOptionSchema.describe("Default: SDK"),
4038
- sessionInitTimeoutMs: z.number().int().positive().optional().describe("Default: 10000"),
4039
- permissionRequestTimeoutMs: z.number().int().positive().optional().describe("Default: 60000, clamped to 300000"),
4040
- 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
+ )
4041
4277
  },
4042
4278
  outputSchema: startResultSchema,
4043
4279
  annotations: {
@@ -4086,9 +4322,11 @@ function createServerContext(serverCwd) {
4086
4322
  {
4087
4323
  description: "List, inspect, cancel, or interrupt sessions.",
4088
4324
  inputSchema: {
4089
- action: z.enum(SESSION_ACTIONS),
4090
- sessionId: z.string().optional().describe("Required for get/cancel/interrupt"),
4091
- 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
+ )
4092
4330
  },
4093
4331
  outputSchema: sessionResultSchema,
4094
4332
  annotations: {
@@ -4134,19 +4372,31 @@ function createServerContext(serverCwd) {
4134
4372
  server.registerTool(
4135
4373
  "claude_code_check",
4136
4374
  {
4137
- description: "Poll session events or respond to permission requests.",
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.",
4138
4376
  inputSchema: {
4139
- action: z.enum(CHECK_ACTIONS),
4140
- sessionId: z.string().describe("Session ID"),
4141
- cursor: z.number().int().nonnegative().optional().describe("Default: 0"),
4142
- responseMode: z.enum(CHECK_RESPONSE_MODES).optional().describe("Default: 'minimal'. Use 'delta_compact' for lightweight polling."),
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
+ ),
4384
+ responseMode: z.enum(CHECK_RESPONSE_MODES).optional().describe(
4385
+ "Default: 'minimal'. Use 'delta_compact' for lightweight high-frequency polling; use 'full' mainly for diagnostics."
4386
+ ),
4143
4387
  maxEvents: z.number().int().positive().optional().describe("Default: 200 (minimal), unlimited (full/delta_compact)"),
4144
- requestId: z.string().optional().describe("Default: none"),
4145
- decision: z.enum(["allow", "deny", "allow_for_session"]).optional().describe("Default: none"),
4146
- denyMessage: z.string().optional().describe("Default: 'Permission denied by caller'"),
4147
- 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
+ ),
4148
4396
  pollOptions: z.object({
4149
- 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
+ ),
4150
4400
  includeEvents: z.boolean().optional().describe("Default: true"),
4151
4401
  includeActions: z.boolean().optional().describe("Default: true"),
4152
4402
  includeResult: z.boolean().optional().describe("Default: true"),
@@ -4156,11 +4406,13 @@ function createServerContext(serverCwd) {
4156
4406
  includeTerminalEvents: z.boolean().optional().describe("Default: full=true, minimal/delta_compact=false"),
4157
4407
  includeProgressEvents: z.boolean().optional().describe("Default: full=true, minimal/delta_compact=false"),
4158
4408
  maxBytes: z.number().int().positive().optional().describe("Default: unlimited")
4159
- }).optional().describe("Default: none"),
4409
+ }).optional().describe("Default: none. Advanced polling payload controls for action='poll'."),
4160
4410
  permissionOptions: z.object({
4161
4411
  updatedInput: z.record(z.string(), z.unknown()).optional().describe("Default: none"),
4162
4412
  updatedPermissions: z.array(z.record(z.string(), z.unknown())).optional().describe("Default: none")
4163
- }).optional().describe("Default: none")
4413
+ }).optional().describe(
4414
+ "Default: none. Advanced permission response overrides for action='respond_permission'."
4415
+ )
4164
4416
  },
4165
4417
  outputSchema: checkResultSchema,
4166
4418
  annotations: {
@@ -4396,10 +4648,11 @@ async function main() {
4396
4648
  process.stdin.on("error", handleStdinError);
4397
4649
  process.stdin.on("end", onStdinEnd);
4398
4650
  process.stdin.on("close", onStdinClose);
4651
+ checkWindowsBashAvailability();
4652
+ checkDefaultClaudeExecutableAvailability();
4399
4653
  await server.connect(transport);
4400
4654
  server.sendToolListChanged();
4401
4655
  server.sendResourceListChanged();
4402
- checkWindowsBashAvailability();
4403
4656
  try {
4404
4657
  if (transport && server) {
4405
4658
  await server.sendLoggingMessage({