@bridge_gpt/mcp-server 0.2.34 → 0.2.37

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 (109) hide show
  1. package/README.md +490 -366
  2. package/build/agent-capabilities/probe-context.js +8 -1
  3. package/build/agent-capabilities/probes.js +7 -1
  4. package/build/agents.generated.js +1 -1
  5. package/build/base-url.js +79 -0
  6. package/build/bridge-api-urls.js +9 -0
  7. package/build/chain-orchestrator.js +93 -15
  8. package/build/claude-review-workflow.js +264 -0
  9. package/build/claude-user-config-doctor.js +317 -0
  10. package/build/cli-release.js +53 -0
  11. package/build/commands.generated.js +6 -5
  12. package/build/conductor/bridge-api-client.js +393 -4
  13. package/build/conductor/deny-enforcement-preflight.js +1 -0
  14. package/build/conductor/done-gate.js +44 -5
  15. package/build/conductor/epic-reconcile.js +6 -0
  16. package/build/conductor/install-doctor.js +462 -0
  17. package/build/conductor-bin.js +3 -3
  18. package/build/conductor-bundle-artifacts.js +37 -15
  19. package/build/credential-store.js +205 -4
  20. package/build/direct-ticket-tools.js +70 -0
  21. package/build/doctor.js +473 -81
  22. package/build/executor/cli.js +83 -6
  23. package/build/executor/credentials.js +39 -11
  24. package/build/executor/deps.js +32 -1
  25. package/build/executor/env.js +71 -28
  26. package/build/executor/heartbeat.js +138 -17
  27. package/build/executor/http-client.js +49 -8
  28. package/build/executor/index.js +4 -0
  29. package/build/executor/job-errors.js +4 -0
  30. package/build/executor/job-runner.js +530 -20
  31. package/build/executor/observation.js +130 -0
  32. package/build/executor/permissions.js +114 -8
  33. package/build/executor/preflight.js +127 -8
  34. package/build/executor/prompt-spec.js +51 -0
  35. package/build/executor/runner.js +23 -2
  36. package/build/executor/service-unit.js +876 -0
  37. package/build/executor/test-clock.js +75 -3
  38. package/build/executor/types.js +4 -18
  39. package/build/executor/worker-command.js +73 -12
  40. package/build/executor/worker-config-isolation.js +287 -0
  41. package/build/executor/worker-finalization.js +68 -14
  42. package/build/executor/worktree.js +46 -4
  43. package/build/index.js +1039 -237
  44. package/build/init.js +503 -111
  45. package/build/install-bridge-conductor.js +491 -0
  46. package/build/install-bridge.js +1188 -247
  47. package/build/install-reexec.js +233 -0
  48. package/build/launcher-config-inspection.js +351 -0
  49. package/build/mcp-host-config.js +11 -1
  50. package/build/mcp-install-state.js +32 -0
  51. package/build/mcp-invoke.js +49 -6
  52. package/build/mcp-provisioning.js +52 -13
  53. package/build/mcp-registration-doctor.js +14 -5
  54. package/build/notifications.js +553 -0
  55. package/build/pipeline-orchestrator.js +146 -4
  56. package/build/pipeline-utils.js +3 -0
  57. package/build/pipelines.generated.js +32 -13
  58. package/build/plan-execution-ledger.js +550 -0
  59. package/build/plan-phase-routing.js +272 -0
  60. package/build/plane/alembic-head.js +110 -0
  61. package/build/plane/build-freshness.js +167 -0
  62. package/build/plane/cli.js +480 -0
  63. package/build/plane/defaults.js +266 -0
  64. package/build/plane/manifest.js +377 -0
  65. package/build/plane/member-logs.js +147 -0
  66. package/build/plane/member-roster.js +147 -0
  67. package/build/plane/preflight.js +289 -0
  68. package/build/plane/shutdown.js +195 -0
  69. package/build/plane/status.js +125 -0
  70. package/build/plane/supervisor.js +569 -0
  71. package/build/plane/test-fakes.js +156 -0
  72. package/build/plane/types.js +75 -0
  73. package/build/readme.generated.js +1 -1
  74. package/build/run-unit-tests-launcher.js +259 -0
  75. package/build/setup-epic.js +772 -28
  76. package/build/sfcc/log-gate.js +38 -11
  77. package/build/sfcc/log-query.js +55 -15
  78. package/build/sfcc/ocapi-shape.js +51 -14
  79. package/build/sfcc/output.js +41 -11
  80. package/build/sfcc/permissions.js +24 -2
  81. package/build/sfcc/read-projection.js +181 -0
  82. package/build/sfcc/read-result.js +158 -0
  83. package/build/sfcc/reads-custom-object-def.js +29 -18
  84. package/build/sfcc/reads-site-preference.js +75 -29
  85. package/build/sfcc/reads-system-object.js +40 -34
  86. package/build/sfcc/sfcc-result.js +106 -0
  87. package/build/sfcc/tool-wrapper.js +56 -13
  88. package/build/sfcc/write-grants.js +45 -22
  89. package/build/sfcc/write-guard.js +21 -13
  90. package/build/sfcc/write-result.js +61 -14
  91. package/build/sfcc/write-tool-common.js +126 -32
  92. package/build/sfcc/writes-system-object.js +11 -50
  93. package/build/start-tickets-prereqs.js +129 -0
  94. package/build/start-tickets.js +17 -13
  95. package/build/ticket-backend-metadata.js +59 -0
  96. package/build/ticket-key-utils.js +92 -0
  97. package/build/tool-error-envelope.js +71 -0
  98. package/build/tool-surface-gating.js +72 -0
  99. package/build/update-status.js +102 -0
  100. package/build/upgrade-advice.js +47 -0
  101. package/build/upgrade-cli.js +412 -110
  102. package/build/version.generated.js +1 -1
  103. package/build/worktree-core.js +73 -0
  104. package/docs/CONDUCTOR.md +132 -6
  105. package/docs/install/mcp-tool-integrations.md +29 -21
  106. package/package.json +9 -6
  107. package/pipelines/implement-ticket.json +19 -4
  108. package/build/conductor/supervisor-judgment-python.js +0 -141
  109. package/build/conductor/supervisor-judgment.js +0 -215
@@ -25,6 +25,7 @@ import path from "path";
25
25
  import os from "os";
26
26
  import { resolveRepoNameForProjectRoot, readBridgeConfig, validateMcpTarget, } from "./bridge-config.js";
27
27
  import { resolveBapiCredentials } from "./credential-store.js";
28
+ import { describeBaseUrlRejection, validateHttpBaseUrl } from "./base-url.js";
28
29
  import { getThirdPartyTargetDefinition, resolveThirdPartyTargetEnv, validateThirdPartyTargetManifestEntry, } from "./third-party-mcp-targets.js";
29
30
  // ---------------------------------------------------------------------------
30
31
  // Usage / argument parsing
@@ -47,18 +48,30 @@ export function getMcpInvokeUsage() {
47
48
  " API server; a configured third-party target from",
48
49
  " .bridge/config (e.g. sfcc) launches that server.",
49
50
  " --project-root <ABS_PATH> Absolute path to the worktree (required)",
51
+ " --base-url <HTTP(S)_URL> Optional. Bridge API endpoint for the spawned",
52
+ " server; applies ONLY to --target bapi. When",
53
+ " omitted, the child inherits BAPI_BASE_URL",
54
+ " unchanged. Must be an absolute http/https URL",
55
+ " with no embedded username or password.",
50
56
  " -h, --help Show this help",
51
57
  ].join("\n");
52
58
  }
53
- const KNOWN_FLAGS = new Set(["--target", "--project-root"]);
59
+ const KNOWN_FLAGS = new Set(["--target", "--project-root", "--base-url"]);
54
60
  /**
55
- * Parse `--target` and `--project-root` (split or `=` form). Rejects unknown
56
- * flags, positional arguments, missing values, and duplicates. Requires a safe
57
- * non-empty `--target` identifier and a host-platform-absolute `--project-root`.
61
+ * Parse `--target`, `--project-root`, and the optional `--base-url` (split or `=`
62
+ * form). Rejects unknown flags, positional arguments, missing values, and
63
+ * duplicates. Requires a safe non-empty `--target` identifier and a
64
+ * host-platform-absolute `--project-root`.
65
+ *
66
+ * `--base-url` is validated strictly and is valid ONLY with `--target bapi`;
67
+ * pairing it with any other target is an error rather than a silent no-op, so a
68
+ * misgenerated registration fails loudly instead of quietly routing a Tier-2
69
+ * server nowhere. No rejection message ever echoes the supplied URL.
58
70
  */
59
71
  export function parseMcpInvokeArgs(argv) {
60
72
  let target;
61
73
  let projectRoot;
74
+ let baseUrl;
62
75
  for (let i = 0; i < argv.length; i++) {
63
76
  const token = argv[i];
64
77
  if (token === "-h" || token === "--help") {
@@ -91,6 +104,16 @@ export function parseMcpInvokeArgs(argv) {
91
104
  }
92
105
  target = value;
93
106
  }
107
+ else if (flag === "--base-url") {
108
+ if (baseUrl !== undefined) {
109
+ return { status: "error", message: "Duplicate --base-url flag" };
110
+ }
111
+ const validation = validateHttpBaseUrl(value);
112
+ if (!validation.ok) {
113
+ return { status: "error", message: describeBaseUrlRejection("--base-url", validation.reason) };
114
+ }
115
+ baseUrl = validation.baseUrl;
116
+ }
94
117
  else {
95
118
  if (projectRoot !== undefined) {
96
119
  return { status: "error", message: "Duplicate --project-root flag" };
@@ -111,7 +134,15 @@ export function parseMcpInvokeArgs(argv) {
111
134
  if (!path.isAbsolute(projectRoot)) {
112
135
  return { status: "error", message: "--project-root must be an absolute path" };
113
136
  }
114
- return { status: "ok", target: targetValidation.value, projectRoot };
137
+ if (baseUrl !== undefined && targetValidation.value !== "bapi") {
138
+ return {
139
+ status: "error",
140
+ message: "--base-url is only valid with --target bapi",
141
+ };
142
+ }
143
+ return baseUrl === undefined
144
+ ? { status: "ok", target: targetValidation.value, projectRoot }
145
+ : { status: "ok", target: targetValidation.value, projectRoot, baseUrl };
115
146
  }
116
147
  // ---------------------------------------------------------------------------
117
148
  // Validation, env construction, signals
@@ -284,6 +315,15 @@ export async function resolveBapiInvocation(projectRoot, deps) {
284
315
  if (typeof conductorNodePath === "string" && conductorNodePath.trim().length > 0) {
285
316
  overlay.CONDUCTOR_NODE_PATH = conductorNodePath;
286
317
  }
318
+ // BAPI-727: overlay the endpoint ONLY when `--base-url` was supplied, mirroring
319
+ // the conditional `CONDUCTOR_NODE_PATH` overlay directly above. The absent case
320
+ // must add, delete, normalize, and override nothing — `buildChildEnv` already
321
+ // inherits the parent's `BAPI_BASE_URL`, and the server keeps its own production
322
+ // default for callers that were never given a URL. Like `CONDUCTOR_NODE_PATH`,
323
+ // this is non-secret routing metadata, not a credential.
324
+ if (typeof deps.baseUrl === "string" && deps.baseUrl.length > 0) {
325
+ overlay.BAPI_BASE_URL = deps.baseUrl;
326
+ }
287
327
  const env = buildChildEnv(deps.env, overlay);
288
328
  return {
289
329
  ok: true,
@@ -356,7 +396,7 @@ export async function runMcpInvokeCli(argv, overrides = {}) {
356
396
  stderr(getMcpInvokeUsage());
357
397
  return 1;
358
398
  }
359
- const { target, projectRoot } = parsed;
399
+ const { target, projectRoot, baseUrl } = parsed;
360
400
  const statFn = overrides.stat ?? ((p) => stat(p));
361
401
  const dirCheck = await validateProjectRootDirectory(projectRoot, statFn);
362
402
  if (!dirCheck.ok) {
@@ -379,12 +419,15 @@ export async function runMcpInvokeCli(argv, overrides = {}) {
379
419
  }));
380
420
  const resolveCredentials = overrides.resolveCredentials ??
381
421
  ((rn) => resolveBapiCredentials(rn, credentialDeps));
422
+ // The parsed URL reaches ONLY this branch; the parser already rejected
423
+ // `--base-url` for every other target, so the Tier-2 path below cannot see it.
382
424
  const resolved = await resolveBapiInvocation(projectRoot, {
383
425
  env,
384
426
  execPath: process.execPath,
385
427
  scriptPath: process.argv[1],
386
428
  resolveRepoName,
387
429
  resolveCredentials,
430
+ baseUrl,
388
431
  });
389
432
  if (!resolved.ok) {
390
433
  stderr(`Error: ${resolved.error}`);
@@ -1,12 +1,15 @@
1
1
  /**
2
2
  * Worktree MCP registration provisioning.
3
3
  *
4
- * After `start-tickets` creates a worktree, this module writes secret-free MCP
5
- * registrations into both Claude (`.mcp.json`) and Cursor (`.cursor/mcp.json`)
6
- * so the configured MCP servers are reachable from either editor. Every
7
- * generated entry contains NO `env` block it points at the `mcp-invoke` shim
8
- * with an absolute `--project-root` and the target name; credentials are
9
- * resolved at runtime by the shim, never written into the worktree.
4
+ * Shared by `start-tickets` worktrees AND, since BAPI-724, every Conductor
5
+ * executor worker worktree (`mcp_server/src/executor/job-runner.ts`, alongside
6
+ * the executor's deny-layer provisioning) not start-tickets-only. Both
7
+ * callers write secret-free MCP registrations into both Claude (`.mcp.json`)
8
+ * and Cursor (`.cursor/mcp.json`) so the configured MCP servers are reachable
9
+ * from either editor. Every generated entry contains NO `env` block — it
10
+ * points at the `mcp-invoke` shim with an absolute `--project-root` and the
11
+ * target name; credentials are resolved at runtime by the shim, never written
12
+ * into the worktree.
10
13
  *
11
14
  * Registrations are driven by `.bridge/config`: the `bapi` target is always
12
15
  * provisioned when present, and every supported Tier-2 target (e.g. `sfcc`) is
@@ -69,13 +72,35 @@ export function serverNameForMcpTarget(target) {
69
72
  * value. Keeping the shim env-free preserves the credential-safety invariant
70
73
  * (secrets are resolved by the `mcp-invoke` shim at launch, not persisted in the
71
74
  * worktree) and equally avoids persisting non-secret operational metadata here.
75
+ *
76
+ * BAPI-724: the executor caller's `invocation` is resolved BEFORE spawn from its
77
+ * OWN running process (`resolveMcpShimInvocationForRuntime`, absolute-build-path
78
+ * form preferred) and never depends on a worker-injected `CONDUCTOR_NODE_PATH`
79
+ * or any other worker-shell env — the executor worker environment carries no
80
+ * such key at all (see `executor/env.ts`).
81
+ *
82
+ * BAPI-727 extends the BAPI-527 note above rather than contradicting it. The
83
+ * optional `bridgeApiBaseUrl` is appended as the ARGUMENT `--base-url <url>`, and
84
+ * only for the `bapi` target. A Bridge API endpoint is non-secret ROUTING
85
+ * metadata — the same value that appears in an operator's `--base-url` flag and in
86
+ * generated executor service units — so persisting it does not weaken the
87
+ * no-`env`, no-credential registration invariant. The entry keeps exactly
88
+ * `command` and `args`; credentials continue to be resolved by the `mcp-invoke`
89
+ * shim at launch and are never written into the worktree.
72
90
  */
73
- export function buildShimMcpServerEntry(target, absoluteWorktreePath, invocation) {
74
- return buildMcpShimCommand(invocation, target, absoluteWorktreePath);
91
+ export function buildShimMcpServerEntry(target, absoluteWorktreePath, invocation, bridgeApiBaseUrl) {
92
+ const entry = buildMcpShimCommand(invocation, target, absoluteWorktreePath);
93
+ // Tier-2 targets are structurally unchanged: `--base-url` is a `bapi`-only flag
94
+ // and `mcp-invoke` rejects it for any other target, so appending it elsewhere
95
+ // would generate a registration that refuses to launch.
96
+ if (target !== "bapi" || bridgeApiBaseUrl === undefined)
97
+ return entry;
98
+ // Clone/extend the generated args; never add a property beyond command/args.
99
+ return { command: entry.command, args: [...entry.args, "--base-url", bridgeApiBaseUrl] };
75
100
  }
76
101
  /** Back-compat wrapper around `buildShimMcpServerEntry("bapi", ...)`. */
77
- export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath, invocation) {
78
- return buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation);
102
+ export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath, invocation, bridgeApiBaseUrl) {
103
+ return buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation, bridgeApiBaseUrl);
79
104
  }
80
105
  /**
81
106
  * Convert all supported manifest `mcp` entries into a map of server name -> shim
@@ -84,12 +109,14 @@ export function buildBridgeApiShimMcpServerEntry(absoluteWorktreePath, invocatio
84
109
  * or incomplete non-`bapi` targets are skipped with a secret-free warning (they
85
110
  * never appear in the returned entries).
86
111
  */
87
- export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath, invocation) {
112
+ export function buildMcpServerEntriesForManifest(manifest, absoluteWorktreePath, invocation, bridgeApiBaseUrl) {
88
113
  const entries = {};
89
114
  const warnings = [];
90
115
  for (const mcp of manifest.mcp) {
91
116
  if (mcp.target === "bapi") {
92
- entries[serverNameForMcpTarget("bapi")] = buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation);
117
+ // The optional base URL reaches ONLY this branch — Tier-2 entries below are
118
+ // built without it and stay byte-identical to their pre-BAPI-727 shape.
119
+ entries[serverNameForMcpTarget("bapi")] = buildShimMcpServerEntry("bapi", absoluteWorktreePath, invocation, bridgeApiBaseUrl);
93
120
  continue;
94
121
  }
95
122
  const definition = getThirdPartyTargetDefinition(mcp.target);
@@ -153,6 +180,11 @@ export function mergeEnabledMcpjsonServers(existing, serverNames) {
153
180
  * Unrelated top-level fields and unrelated MCP servers are preserved; only the
154
181
  * generated server names are replaced. Any legacy secret-embedded entry for a
155
182
  * generated server name is force-upgraded to the secret-free shim shape.
183
+ *
184
+ * Deliberately UNCHANGED by BAPI-727: because a generated name is replaced rather
185
+ * than merged field-by-field, re-provisioning an existing worktree automatically
186
+ * upgrades an older `bridge-api` entry that lacks `--base-url` to the URL-bearing
187
+ * one. No migration step is needed.
156
188
  */
157
189
  export function mergeMcpRegistrations(existing, topLevelKey, entries) {
158
190
  const result = existing && typeof existing === "object" && !Array.isArray(existing)
@@ -284,6 +316,13 @@ function withWarnings(row, warnings) {
284
316
  * add a secret-free warning but never abort provisioning.
285
317
  * - A required write failure (or a malformed existing registration file) marks
286
318
  * only this row `spawn-failed` with a descriptive error; other rows continue.
319
+ * This `spawn-failed` status is the `start-tickets` row contract: that
320
+ * caller propagates it verbatim into its summary report. The executor caller
321
+ * (BAPI-724) deliberately does NOT propagate it the same way — it constructs
322
+ * a synthetic `created` row per job, and its adapter
323
+ * (`provisionMcpForPreparedSpawn` in `job-runner.ts`) converts a
324
+ * `spawn-failed` result into a logged fail-open warning instead, since a
325
+ * provisioning failure must never block an executor worker from spawning.
287
326
  * - After the registration files are written, the worktree's
288
327
  * `.claude/settings.local.json` is updated to pre-approve those servers via
289
328
  * `enabledMcpjsonServers` (suppressing Claude Code's per-project trust
@@ -305,7 +344,7 @@ export async function provisionMcpRegistrationForWorktree(row, deps) {
305
344
  if (!normalized.ok) {
306
345
  return { ...row, status: "spawn-failed", error: `MCP provisioning failed: ${normalized.error}` };
307
346
  }
308
- const built = buildMcpServerEntriesForManifest(read.manifest, normalized.path, deps.mcpServerInvocation);
347
+ const built = buildMcpServerEntriesForManifest(read.manifest, normalized.path, deps.mcpServerInvocation, deps.bridgeApiBaseUrl);
309
348
  if (Object.keys(built.entries).length === 0) {
310
349
  // Nothing supported to write (e.g. a manifest with no bapi target and no
311
350
  // supported Tier-2 targets). Surface any warnings but leave status unchanged.
@@ -8,17 +8,26 @@
8
8
  */
9
9
  import path from "path";
10
10
  /**
11
- * Read and parse a JSON file. A read failure (e.g. ENOENT) is `missing`; invalid
12
- * JSON is `malformed` (carrying only the path, never the raw content); otherwise
13
- * `present` with the parsed value.
11
+ * Read and parse a JSON file.
12
+ *
13
+ * A read failure whose error carries an `ENOENT` code is `missing` — the file
14
+ * genuinely is not there. Any OTHER read failure (e.g. `EACCES`) is `unreadable`:
15
+ * BAPI-727's shadow inspector must not treat "I could not look" as "there is
16
+ * nothing there", because an unreadable `~/.claude.json` could still be shadowing
17
+ * a worker's MCP registration. Invalid JSON stays `malformed`; both non-present
18
+ * failure states carry ONLY the path — never the raw content and never the read
19
+ * exception's message, which can contain arbitrary text.
14
20
  */
15
21
  export async function readJsonIfPresent(filePath, deps) {
16
22
  let raw;
17
23
  try {
18
24
  raw = await deps.readFile(filePath);
19
25
  }
20
- catch {
21
- return { state: "missing" };
26
+ catch (err) {
27
+ const code = err && typeof err === "object" ? err.code : undefined;
28
+ if (code === "ENOENT")
29
+ return { state: "missing" };
30
+ return { state: "unreadable", path: filePath };
22
31
  }
23
32
  try {
24
33
  return { state: "present", value: JSON.parse(raw) };