@bridge_gpt/mcp-server 0.2.18 → 0.2.20

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.
Files changed (100) hide show
  1. package/CONDUCTOR.md +75 -0
  2. package/README.md +8 -5
  3. package/build/agent-capabilities/probe-context.js +13 -3
  4. package/build/agent-capabilities/probes.js +262 -11
  5. package/build/agent-capabilities/reporter.js +1 -0
  6. package/build/agents.generated.js +1 -1
  7. package/build/backend-warnings.js +44 -0
  8. package/build/claude-settings.js +129 -0
  9. package/build/commands.generated.js +5 -3
  10. package/build/conductor/bridge-api-client.js +7 -7
  11. package/build/conductor/cli.js +65 -12
  12. package/build/conductor/deny-enforcement-preflight.js +96 -0
  13. package/build/conductor/doctor.js +183 -2
  14. package/build/conductor/epic-reconcile.js +9 -1
  15. package/build/conductor/epic-runtime.js +403 -43
  16. package/build/conductor/epic-state.js +7 -0
  17. package/build/conductor/errors.js +115 -3
  18. package/build/conductor/event-accessors.js +28 -10
  19. package/build/conductor/local-merge.js +458 -95
  20. package/build/conductor/merge-ledger.js +6 -4
  21. package/build/conductor/pr-ci-producer.js +17 -2
  22. package/build/conductor/producer-ledger.js +1 -1
  23. package/build/conductor/store.js +161 -18
  24. package/build/conductor/supervisor-merge.js +32 -5
  25. package/build/conductor/taxonomy.js +8 -0
  26. package/build/conductor/tools.js +28 -6
  27. package/build/conductor/worker-ledger-cli.js +244 -0
  28. package/build/conductor-bin.js +1884 -6917
  29. package/build/doctor.js +8 -0
  30. package/build/estimate-epic.js +84 -0
  31. package/build/executor/cli.js +229 -0
  32. package/build/executor/credentials.js +65 -0
  33. package/build/executor/deps.js +117 -0
  34. package/build/executor/env.js +79 -0
  35. package/build/executor/heartbeat.js +59 -0
  36. package/build/executor/http-client.js +131 -0
  37. package/build/executor/index.js +10 -0
  38. package/build/executor/job-errors.js +55 -0
  39. package/build/executor/job-log-registry.js +110 -0
  40. package/build/executor/job-runner.js +822 -0
  41. package/build/executor/job-types.js +60 -0
  42. package/build/executor/merge-job.js +229 -0
  43. package/build/executor/observation.js +123 -0
  44. package/build/executor/permissions.js +79 -0
  45. package/build/executor/preflight.js +144 -0
  46. package/build/executor/process.js +81 -0
  47. package/build/executor/prompt-spec.js +235 -0
  48. package/build/executor/results.js +134 -0
  49. package/build/executor/resume-pre-spawn.js +179 -0
  50. package/build/executor/runner.js +98 -0
  51. package/build/executor/terminal-mutation.js +34 -0
  52. package/build/executor/test-clock.js +109 -0
  53. package/build/executor/types.js +18 -0
  54. package/build/executor/verdict-artifact.js +53 -0
  55. package/build/executor/viewer-tabs.js +78 -0
  56. package/build/executor/watch-cli.js +113 -0
  57. package/build/executor/worker-command.js +106 -0
  58. package/build/executor/worker-finalization.js +177 -0
  59. package/build/executor/worker-log.js +92 -0
  60. package/build/executor/worktree-gc.js +134 -0
  61. package/build/executor/worktree-inspection.js +86 -0
  62. package/build/executor/worktree.js +103 -0
  63. package/build/index.js +14534 -10416
  64. package/build/mcp-invoke.js +19 -3
  65. package/build/mcp-provisioning.js +31 -25
  66. package/build/mcp-registration-doctor.js +27 -7
  67. package/build/mcp-server-invocation.js +152 -0
  68. package/build/pipelines.generated.js +16 -20
  69. package/build/readme.generated.js +1 -1
  70. package/build/sfcc/client.js +192 -50
  71. package/build/sfcc/ocapi-write-faults.js +94 -0
  72. package/build/sfcc/permissions.js +7 -22
  73. package/build/sfcc/reads-site-preference.js +52 -19
  74. package/build/sfcc/register.js +9 -0
  75. package/build/sfcc/write-grants.js +80 -0
  76. package/build/sfcc/write-guard.js +39 -0
  77. package/build/sfcc/write-result.js +47 -0
  78. package/build/sfcc/write-tool-common.js +85 -0
  79. package/build/sfcc/writes-custom-object-def.js +141 -0
  80. package/build/sfcc/writes-object-attribute-payloads.js +97 -0
  81. package/build/sfcc/writes-site-preference-payloads.js +59 -0
  82. package/build/sfcc/writes-site-preference.js +96 -0
  83. package/build/sfcc/writes-system-object-payloads.js +213 -0
  84. package/build/sfcc/writes-system-object.js +348 -0
  85. package/build/sfcc/writes.js +66 -0
  86. package/build/start-tickets-conductor.js +25 -93
  87. package/build/start-tickets-prereqs.js +152 -1
  88. package/build/start-tickets.js +96 -158
  89. package/build/version.generated.js +1 -1
  90. package/build/visual-diff-worker.js +313 -0
  91. package/build/visual-diff.js +632 -0
  92. package/build/worktree-core.js +202 -0
  93. package/package.json +8 -4
  94. package/pipelines/idea-to-ticket.json +7 -0
  95. package/pipelines/review-ticket.json +5 -18
  96. package/public/css/main.min.css +1612 -107
  97. package/public/css/main.min.css.map +1 -1
  98. package/public/js/main.min.js +10267 -1
  99. package/public/js/main.min.js.map +1 -1
  100. package/smoke-test/SMOKE-TEST.md +2 -1
@@ -32,10 +32,15 @@ import { getThirdPartyTargetDefinition, resolveThirdPartyTargetEnv, validateThir
32
32
  export function getMcpInvokeUsage() {
33
33
  return [
34
34
  "Usage:",
35
- " npx -y @bridge_gpt/mcp-server mcp-invoke --target <target> --project-root <ABS_WORKTREE_PATH>",
35
+ " node <abs>/mcp_server/build/index.js mcp-invoke --target <target> --project-root <ABS_WORKTREE_PATH>",
36
+ "",
37
+ " (npm-channel fallback may invoke the same shim through a package spec, e.g.",
38
+ " npx -y @bridge_gpt/mcp-server@latest mcp-invoke --target <target> --project-root <ABS_WORKTREE_PATH>)",
36
39
  "",
37
40
  "Internal worktree shim: resolves the target's launch command and credentials",
38
41
  "from the given project root, then spawns the real MCP server over stdio.",
42
+ "Argument parsing is identical regardless of how this process was launched",
43
+ "(absolute-path node invocation vs npm-channel npx).",
39
44
  "",
40
45
  "Flags:",
41
46
  " --target <target> MCP target to launch. 'bapi' launches the Bridge",
@@ -264,11 +269,22 @@ export async function resolveBapiInvocation(projectRoot, deps) {
264
269
  if (!credResult.ok) {
265
270
  return { ok: false, error: `failed to resolve credentials: ${credResult.error}` };
266
271
  }
267
- const env = buildChildEnv(deps.env, {
272
+ const overlay = {
268
273
  BAPI_API_KEY: credResult.credentials.apiKey,
269
274
  BAPI_PROJECT_ROOT: projectRoot,
270
275
  BAPI_REPO_NAME: repoName,
271
- });
276
+ };
277
+ // BAPI-527: `CONDUCTOR_NODE_PATH` is NON-SECRET operational metadata (the
278
+ // captured conductor Node path used by the worker-ledger CLI subprocess).
279
+ // `buildChildEnv` already inherits the full parent env, so this re-affirms the
280
+ // pass-through explicitly and ONLY when present — it is a non-secret allowlist
281
+ // entry, never a credential. It is emphatically NOT the MCP server's re-exec
282
+ // command: the spawned command stays `deps.execPath` + `[scriptPath]` below.
283
+ const conductorNodePath = deps.env.CONDUCTOR_NODE_PATH;
284
+ if (typeof conductorNodePath === "string" && conductorNodePath.trim().length > 0) {
285
+ overlay.CONDUCTOR_NODE_PATH = conductorNodePath;
286
+ }
287
+ const env = buildChildEnv(deps.env, overlay);
272
288
  return {
273
289
  ok: true,
274
290
  invocation: { command: deps.execPath, args: [deps.scriptPath], env },
@@ -18,9 +18,9 @@
18
18
  * to avoid a runtime import cycle.
19
19
  */
20
20
  import path from "path";
21
- import { VERSION } from "./version.generated.js";
22
21
  import { readBridgeConfig } from "./bridge-config.js";
23
22
  import { getThirdPartyTargetDefinition, validateThirdPartyTargetManifestEntry, } from "./third-party-mcp-targets.js";
23
+ import { buildMcpShimCommand, } from "./mcp-server-invocation.js";
24
24
  // ---------------------------------------------------------------------------
25
25
  // Pure helpers
26
26
  // ---------------------------------------------------------------------------
@@ -55,27 +55,27 @@ export function serverNameForMcpTarget(target) {
55
55
  return target === "bapi" ? "bridge-api" : target;
56
56
  }
57
57
  /**
58
- * Build a secret-free shim entry for any target. `args` are version-pinned and
59
- * carry the target name plus the absolute project root; there is intentionally
60
- * no `env` block.
58
+ * Build a secret-free shim entry for any target from a structured invocation.
59
+ * The command/args carry the target name plus the absolute project root; there is
60
+ * intentionally no `env` block. The absolute-build-path form serializes as
61
+ * `{ command: "node", args: ["<abs>/build/index.js", "mcp-invoke", …] }` (the
62
+ * node executable is a single substitutable field), and the npm-channel fallback
63
+ * as `{ command: "npx", args: ["-y", "<spec>", "mcp-invoke", …] }`.
64
+ *
65
+ * BAPI-527: `.mcp.json` remains intentionally ENV-FREE. `CONDUCTOR_NODE_PATH`
66
+ * (the captured conductor Node path for the worker-ledger CLI subprocess) is NOT
67
+ * written here — it is inherited at runtime from the worker SHELL env that
68
+ * `start-tickets` injects at the spawn boundary, never stored as a registration
69
+ * value. Keeping the shim env-free preserves the credential-safety invariant
70
+ * (secrets are resolved by the `mcp-invoke` shim at launch, not persisted in the
71
+ * worktree) and equally avoids persisting non-secret operational metadata here.
61
72
  */
62
- export function buildShimMcpServerEntry(target, absoluteWorktreePath) {
63
- return {
64
- command: "npx",
65
- args: [
66
- "-y",
67
- `@bridge_gpt/mcp-server@${VERSION}`,
68
- "mcp-invoke",
69
- "--target",
70
- target,
71
- "--project-root",
72
- absoluteWorktreePath,
73
- ],
74
- };
73
+ export function buildShimMcpServerEntry(target, absoluteWorktreePath, invocation) {
74
+ return buildMcpShimCommand(invocation, target, absoluteWorktreePath);
75
75
  }
76
76
  /** Back-compat wrapper around `buildShimMcpServerEntry("bapi", ...)`. */
77
- export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath) {
78
- return buildShimMcpServerEntry("bapi", absoluteWorktreePath);
77
+ export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath, invocation) {
78
+ return buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation);
79
79
  }
80
80
  /**
81
81
  * Convert all supported manifest `mcp` entries into a map of server name -> shim
@@ -84,12 +84,12 @@ export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath) {
84
84
  * or incomplete non-`bapi` targets are skipped with a secret-free warning (they
85
85
  * never appear in the returned entries).
86
86
  */
87
- export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath) {
87
+ export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath, invocation) {
88
88
  const entries = {};
89
89
  const warnings = [];
90
90
  for (const mcp of manifest.mcp) {
91
91
  if (mcp.target === "bapi") {
92
- entries[serverNameForMcpTarget("bapi")] = buildShimMcpServerEntry("bapi", absoluteWorktreePath);
92
+ entries[serverNameForMcpTarget("bapi")] = buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation);
93
93
  continue;
94
94
  }
95
95
  const definition = getThirdPartyTargetDefinition(mcp.target);
@@ -102,9 +102,9 @@ export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath)
102
102
  warnings.push(`MCP target '${mcp.target}' registration skipped: ${validation.error}.`);
103
103
  continue;
104
104
  }
105
- entries[serverNameForMcpTarget(mcp.target)] = buildShimMcpServerEntry(mcp.target, absoluteWorktreePath);
105
+ entries[serverNameForMcpTarget(mcp.target)] = buildShimMcpServerEntry(mcp.target, absoluteWorktreePath, invocation);
106
106
  }
107
- return { entries, warnings };
107
+ return { entries, warnings, registrationForm: invocation.form };
108
108
  }
109
109
  /** Both registration files written for every provisioned worktree. */
110
110
  export function getWorktreeMcpRegistrationTargets(worktreePath, platform) {
@@ -305,7 +305,7 @@ export async function provisionMcpRegistrationForWorktree(row, deps) {
305
305
  if (!normalized.ok) {
306
306
  return { ...row, status: "spawn-failed", error: `MCP provisioning failed: ${normalized.error}` };
307
307
  }
308
- const built = buildMcpServerEntriesForManifest(read.manifest, normalized.path);
308
+ const built = buildMcpServerEntriesForManifest(read.manifest, normalized.path, deps.mcpServerInvocation);
309
309
  if (Object.keys(built.entries).length === 0) {
310
310
  // Nothing supported to write (e.g. a manifest with no bapi target and no
311
311
  // supported Tier-2 targets). Surface any warnings but leave status unchanged.
@@ -318,10 +318,16 @@ export async function provisionMcpRegistrationForWorktree(row, deps) {
318
318
  return { ...row, status: "spawn-failed", error: `MCP provisioning failed: ${result.error}` };
319
319
  }
320
320
  }
321
+ // Record the resolved registration form for audit (path-free — the form label
322
+ // only, never the absolute server entry path, which could leak a username).
323
+ let result = { ...row, mcpRegistrationForm: built.registrationForm };
324
+ result = withWarnings(result, built.warnings);
325
+ if (built.registrationForm !== "absolute-build-path") {
326
+ result = withWarning(result, "MCP registration used npm-channel fallback because an on-disk build entry was not resolvable.");
327
+ }
321
328
  // Pre-approve the just-registered servers so Claude Code does not prompt to
322
329
  // trust them in this fresh worktree path. Best-effort: a failure here only
323
330
  // loses the convenience of skipping the prompt, so warn and still spawn.
324
- let result = withWarnings(row, built.warnings);
325
331
  const serverNames = Object.keys(built.entries);
326
332
  const trust = await writeClaudeServerTrustSettings(normalized.path, serverNames, deps);
327
333
  if (!trust.ok) {
@@ -35,22 +35,42 @@ function flagValue(args, flag) {
35
35
  return undefined;
36
36
  }
37
37
  /**
38
- * True when `entry` is a secret-free Bridge API shim registration that launches
39
- * `npx -y @bridge_gpt/mcp-server@<version> mcp-invoke --target bapi
40
- * --project-root <worktreeRoot>`. The `--target` must be `bapi` and the
41
- * `--project-root` must match the given worktree exactly.
38
+ * True when `entry` is a secret-free Bridge API shim registration pointing at
39
+ * THIS worktree. Two forms are accepted (BAPI-526):
40
+ *
41
+ * - Absolute-build-path (PRIMARY): an absolute `node` (or absolute Node-path)
42
+ * invocation of the conductor's own compiled server, e.g.
43
+ * `{ command: "node", args: ["<abs>/mcp_server/build/index.js", "mcp-invoke",
44
+ * "--target", "bapi", "--project-root", "<worktreeRoot>"] }`. `command` may be
45
+ * any non-empty string ("node" or a future absolute Node path); `args[0]` is a
46
+ * non-empty server entry path.
47
+ * - npm-channel (LEGACY/FALLBACK): `{ command: "npx", args: ["-y",
48
+ * "@bridge_gpt/mcp-server@<spec>", "mcp-invoke", "--target", "bapi",
49
+ * "--project-root", "<worktreeRoot>"] }`.
50
+ *
51
+ * In both forms `--target` must be `bapi` and `--project-root` must match the
52
+ * given worktree exactly. The server entry path is NOT required to sit under any
53
+ * particular username/path prefix.
42
54
  */
43
55
  export function isBridgeApiShimEntry(entry, worktreeRoot) {
44
56
  if (!entry || typeof entry !== "object" || Array.isArray(entry))
45
57
  return false;
46
58
  const candidate = entry;
47
- if (candidate.command !== "npx")
59
+ if (typeof candidate.command !== "string" || candidate.command.length === 0)
48
60
  return false;
49
61
  if (!Array.isArray(candidate.args))
50
62
  return false;
51
63
  const args = candidate.args.filter((a) => typeof a === "string");
52
- if (!args.some((a) => a.startsWith("@bridge_gpt/mcp-server")))
53
- return false;
64
+ if (candidate.command === "npx") {
65
+ // npm-channel fallback: must reference the package spec.
66
+ if (!args.some((a) => a.startsWith("@bridge_gpt/mcp-server")))
67
+ return false;
68
+ }
69
+ else {
70
+ // Absolute-build-path: first arg must be a non-empty server entry path.
71
+ if (typeof args[0] !== "string" || args[0].length === 0)
72
+ return false;
73
+ }
54
74
  if (!args.includes("mcp-invoke"))
55
75
  return false;
56
76
  if (flagValue(args, "--target") !== "bapi")
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Shared, dependency-injected model for how a dispatched worker launches the
3
+ * Bridge MCP `mcp-invoke` shim (BAPI-526, Change A).
4
+ *
5
+ * The conductor used to register each dispatched worktree's `.mcp.json` against
6
+ * an EXACT npm version (`npx -y @bridge_gpt/mcp-server@<VERSION> mcp-invoke …`).
7
+ * When that version was built but not yet published, `npx` returned ETARGET/404
8
+ * and the worker had NO Bridge MCP tools. This module lets provisioning instead
9
+ * invoke the conductor's own on-disk build by ABSOLUTE PATH
10
+ * (`node <abs>/mcp_server/build/index.js mcp-invoke …`) — removing the
11
+ * npm-registry publish dependency and guaranteeing the worker runs the exact MCP
12
+ * code the conductor is running. A resolvable npm-channel spec remains the
13
+ * fallback when an on-disk build entry cannot be resolved (published-package
14
+ * install).
15
+ *
16
+ * SCOPE BOUNDARY (BAPI-526 is detection/registration only): this selects WHICH
17
+ * server file is invoked (`node <absPath>/index.js` vs `npx @…`). It does NOT
18
+ * change WHICH Node binary runs that file — the `nodeExecutable` field is kept as
19
+ * a single substitutable value (never string-concatenated into the command) so
20
+ * the later Ticket 3 (P2) work can point it at `CONDUCTOR_NODE_PATH` without
21
+ * re-plumbing argv construction.
22
+ */
23
+ import path from "node:path";
24
+ export const MCP_SERVER_PACKAGE_NAME = "@bridge_gpt/mcp-server";
25
+ /**
26
+ * Resolvable npm-channel fallback spec. Deliberately a moving channel (`@latest`)
27
+ * rather than an exact generated version pin, so the fallback path can never
28
+ * reintroduce the ETARGET/404 failure the absolute-path form exists to remove.
29
+ */
30
+ export const DEFAULT_MCP_SERVER_NPM_CHANNEL_SPEC = "@bridge_gpt/mcp-server@latest";
31
+ /**
32
+ * Build the concrete `{ command, args }` launch pair for a target + worktree from
33
+ * a structured invocation. Pure — no filesystem or process access. The
34
+ * `nodeExecutable` is returned as the standalone `command`, never concatenated
35
+ * with the server entry path, so `CONDUCTOR_NODE_PATH` can substitute it later.
36
+ */
37
+ export function buildMcpShimCommand(invocation, target, absoluteWorktreePath) {
38
+ if (invocation.form === "absolute-build-path") {
39
+ return {
40
+ command: invocation.nodeExecutable,
41
+ args: [
42
+ invocation.serverEntryPath,
43
+ "mcp-invoke",
44
+ "--target",
45
+ target,
46
+ "--project-root",
47
+ absoluteWorktreePath,
48
+ ],
49
+ };
50
+ }
51
+ return {
52
+ command: "npx",
53
+ args: [
54
+ "-y",
55
+ invocation.packageSpec,
56
+ "mcp-invoke",
57
+ "--target",
58
+ target,
59
+ "--project-root",
60
+ absoluteWorktreePath,
61
+ ],
62
+ };
63
+ }
64
+ /**
65
+ * Secret-free, path-free audit label for summaries and diagnostics. Returns ONLY
66
+ * the registration form; never the server entry path, username, argv, or package
67
+ * cache path (any of which can leak a username through a filesystem path).
68
+ */
69
+ export function formatMcpShimInvocationForAudit(invocation) {
70
+ return invocation.form;
71
+ }
72
+ /**
73
+ * Resolve the package root from a module URL by walking the module file path and
74
+ * locating the nearest `src` or `build` path segment; the package root is the
75
+ * portion BEFORE that segment. Returns forward-slash form so it is deterministic
76
+ * across platforms (Node accepts forward slashes on Windows). Does NOT consult
77
+ * `process.cwd()`. Returns `null` when neither `src` nor `build` appears.
78
+ */
79
+ export function resolvePackageRootFromModuleUrl(moduleUrl) {
80
+ // Parse the URL's pathname directly rather than via fileURLToPath: on Windows,
81
+ // fileURLToPath throws ERR_INVALID_FILE_URL_PATH for a drive-less `file:///…`
82
+ // URL, whereas the pathname (`/repo/mcp_server/…`) is available on every
83
+ // platform. This keeps resolution deterministic across POSIX and Windows.
84
+ let pathname;
85
+ try {
86
+ pathname = decodeURIComponent(new URL(moduleUrl).pathname);
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ // A real Windows `import.meta.url` is `file:///C:/…`, whose pathname is
92
+ // `/C:/…`; strip the leading slash so the drive letter leads (Node accepts
93
+ // forward slashes on Windows). Drive-less POSIX pathnames are left unchanged.
94
+ if (/^\/[A-Za-z]:/.test(pathname)) {
95
+ pathname = pathname.slice(1);
96
+ }
97
+ // Split on both separators so a Windows-materialized path (backslashes) and a
98
+ // posix path both segment identically.
99
+ const segments = pathname.split(/[\\/]/);
100
+ let markerIndex = -1;
101
+ for (let i = segments.length - 1; i >= 0; i--) {
102
+ if (segments[i] === "src" || segments[i] === "build") {
103
+ markerIndex = i;
104
+ break;
105
+ }
106
+ }
107
+ if (markerIndex <= 0)
108
+ return null;
109
+ return segments.slice(0, markerIndex).join("/");
110
+ }
111
+ /** Manual basename that tolerates either path separator. */
112
+ function basenameAnySep(filePath) {
113
+ const segments = filePath.split(/[\\/]/);
114
+ return segments[segments.length - 1] ?? "";
115
+ }
116
+ /**
117
+ * Resolve how the worker MCP shim should be launched, preferring the conductor's
118
+ * own on-disk build by absolute path and falling back to a resolvable npm channel.
119
+ *
120
+ * Resolution order (never uses `process.cwd()`):
121
+ * 1. `<packageRoot>/build/index.js` derived from `deps.moduleUrl`.
122
+ * 2. `deps.argv1` when it is absolute, exists, and its basename is `index.js`.
123
+ * 3. npm-channel fallback (`deps.npmPackageSpec ?? DEFAULT_MCP_SERVER_NPM_CHANNEL_SPEC`).
124
+ *
125
+ * NOTE (scope boundary): this ticket changes WHICH server file is invoked, not
126
+ * WHICH Node runtime runs it. `nodeExecutable` defaults to `"node"` (NOT
127
+ * `process.execPath`) to preserve today's worker runtime selection; Ticket 3 (P2)
128
+ * can later inject `CONDUCTOR_NODE_PATH` into this single field.
129
+ */
130
+ export function resolveMcpShimInvocationForRuntime(deps) {
131
+ const nodeExecutable = deps.nodeExecutable ?? "node";
132
+ const packageRoot = resolvePackageRootFromModuleUrl(deps.moduleUrl);
133
+ if (packageRoot) {
134
+ const candidate = `${packageRoot}/build/index.js`;
135
+ if (deps.fileExists(candidate)) {
136
+ return { form: "absolute-build-path", nodeExecutable, serverEntryPath: candidate };
137
+ }
138
+ }
139
+ const argv1 = deps.argv1;
140
+ if (typeof argv1 === "string" &&
141
+ argv1.length > 0 &&
142
+ path.isAbsolute(argv1) &&
143
+ basenameAnySep(argv1) === "index.js" &&
144
+ deps.fileExists(argv1)) {
145
+ return { form: "absolute-build-path", nodeExecutable, serverEntryPath: argv1 };
146
+ }
147
+ return {
148
+ form: "npm-channel",
149
+ command: "npx",
150
+ packageSpec: deps.npmPackageSpec ?? DEFAULT_MCP_SERVER_NPM_CHANNEL_SPEC,
151
+ };
152
+ }