@bridge_gpt/mcp-server 0.2.56 → 0.2.57

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.
@@ -58,9 +58,10 @@ import { readFile, writeFile, mkdir, mkdtemp, stat, lstat, symlink, unlink, copy
58
58
  import os from "node:os";
59
59
  import { createHash } from "node:crypto";
60
60
  import path from "path";
61
+ import { validateHttpBaseUrl } from "./base-url.js";
61
62
  import { VERSION } from "./version.generated.js";
62
63
  import { resolveBapiCredentials, getPrimaryCredentialStorePath, } from "./credential-store.js";
63
- import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
64
+ import { LEGACY_SERVER_NAMES, MCP_PACKAGE_NAME, MCP_SERVER_NAME } from "./mcp-identity.js";
64
65
  // BAPI-941: the shared drift diagnostic formatter. The leaf drift module imports
65
66
  // nothing from here, so this direction stays acyclic.
66
67
  import { formatClaudeReviewWorkflowDriftDiagnostic } from "./claude-review-workflow-drift.js";
@@ -560,7 +561,7 @@ resumeMode = false, workflow = "implement", reviewRounds,
560
561
  // conductor runs always, and (BAPI-801/BAPI-847) for every plain run whose base
561
562
  // resolved, `main` included — and threaded (BAPI-593) into the spawned workflow
562
563
  // command.
563
- baseBranch) {
564
+ baseBranch, bridgeApiBaseUrlOverride) {
564
565
  if (!isSupportedStartTicketsPlatform(deps.platform)) {
565
566
  return { ok: false, error: unsupportedPlatformMessage(deps.platform) };
566
567
  }
@@ -571,6 +572,7 @@ baseBranch) {
571
572
  // conductor-only — a plain worker's prompt stays bare and learns the base from
572
573
  // the env plus the create-PR instruction files.
573
574
  const prBaseBranch = resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch);
575
+ const bridgeApiBaseUrl = bridgeApiBaseUrlOverride ?? resolveSpawnBridgeApiBaseUrl(deps.env);
574
576
  return {
575
577
  ok: true,
576
578
  config: {
@@ -580,7 +582,7 @@ baseBranch) {
580
582
  // falls back to the basename-derived repo name (the 403 root cause), and
581
583
  // (BAPI-586/BAPI-801) the run base so the spawned worker opens its PR
582
584
  // against it. The base assignment stays outermost.
583
- buildAgentShellCommand: (key, worktreePath, modelAlias, launchArgs) => prependBaseBranchEnvAssignment(prependRepoNameEnvAssignment(buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, launchArgs), repoName, platform), prBaseBranch, platform),
585
+ buildAgentShellCommand: (key, worktreePath, modelAlias, launchArgs) => prependBaseBranchEnvAssignment(prependRepoNameEnvAssignment(buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, launchArgs, bridgeApiBaseUrl), repoName, platform), prBaseBranch, platform),
584
586
  spawnTerminalTab: deps.spawnTerminalTab,
585
587
  },
586
588
  };
@@ -610,6 +612,51 @@ export function prependRepoNameEnvAssignment(command, repoName, platform = "darw
610
612
  }
611
613
  return `export BAPI_REPO_NAME='${shSquoteInner(repoName)}' && ${command}`;
612
614
  }
615
+ /** Carry the parent's non-secret Bridge endpoint into a newly opened terminal. */
616
+ export function prependBridgeApiBaseUrlEnvAssignment(command, baseUrl, platform = "darwin") {
617
+ if (!baseUrl)
618
+ return command;
619
+ if (platform === "win32") {
620
+ return `$env:BAPI_BASE_URL = ${powershellSquote(baseUrl)}; ${command}`;
621
+ }
622
+ return `export BAPI_BASE_URL='${shSquoteInner(baseUrl)}' && ${command}`;
623
+ }
624
+ /** Validate before placing an endpoint in a launch script or MCP registration. */
625
+ export function resolveSpawnBridgeApiBaseUrl(env) {
626
+ const raw = env.BAPI_BASE_URL;
627
+ if (!raw || !raw.trim())
628
+ return undefined;
629
+ const result = validateHttpBaseUrl(raw);
630
+ return result.ok ? result.baseUrl : undefined;
631
+ }
632
+ /**
633
+ * A shell-spawned CLI cannot see the parent MCP process's environment. Read only
634
+ * the non-secret endpoint from the selected host's project registration when the
635
+ * shell did not inherit BAPI_BASE_URL (common for Cursor launched from the GUI).
636
+ */
637
+ export async function resolveStartTicketsBridgeApiBaseUrl(deps, agent) {
638
+ const inherited = resolveSpawnBridgeApiBaseUrl(deps.env);
639
+ if (inherited)
640
+ return inherited;
641
+ const configPath = agent.name === "cursor-agent"
642
+ ? path.join(deps.cwd, ".cursor", "mcp.json")
643
+ : path.join(deps.cwd, ".mcp.json");
644
+ try {
645
+ const raw = await (deps.readFile ?? ((filePath) => readFile(filePath, "utf-8")))(configPath);
646
+ const parsed = JSON.parse(raw);
647
+ const bridge = parsed.mcpServers?.[MCP_SERVER_NAME] ?? parsed.mcpServers?.[LEGACY_SERVER_NAMES[0]];
648
+ if (!bridge)
649
+ return undefined;
650
+ const args = Array.isArray(bridge.args) ? bridge.args : [];
651
+ const flagIndex = args.indexOf("--base-url");
652
+ const candidate = bridge.env?.BAPI_BASE_URL ?? (flagIndex >= 0 ? args[flagIndex + 1] : undefined);
653
+ const validated = validateHttpBaseUrl(candidate);
654
+ return validated.ok ? validated.baseUrl : undefined;
655
+ }
656
+ catch {
657
+ return undefined;
658
+ }
659
+ }
613
660
  /**
614
661
  * BAPI-801/BAPI-847: decide the `BAPI_BASE_BRANCH` value a spawned worker's shell
615
662
  * should carry — the single rule shared by the real spawn builder
@@ -1347,14 +1394,14 @@ export function buildAgentInvocation(agent, prompt, quote, modelAlias, launchArg
1347
1394
  }
1348
1395
  }
1349
1396
  /** POSIX agent shell command: `cd '<path>' && <agent> [--model '<alias>'] '<prompt>'`. */
1350
- export function buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias, conductorEnabled = false, resumeMode = false, workflow = "implement", reviewRounds, baseBranch, launchArgs = []) {
1397
+ export function buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias, conductorEnabled = false, resumeMode = false, workflow = "implement", reviewRounds, baseBranch, launchArgs = [], bridgeApiBaseUrl) {
1351
1398
  const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, agentName: agent.name }), (p) => `'${shSquoteInner(p)}'`, modelAlias, agent.name === "cursor-agent" && autoApprove ? worktreePath : launchArgs);
1352
- return `cd '${shSquoteInner(worktreePath)}' && ${invocation}`;
1399
+ return `cd '${shSquoteInner(worktreePath)}' && ${prependBridgeApiBaseUrlEnvAssignment(invocation, bridgeApiBaseUrl, "darwin")}`;
1353
1400
  }
1354
1401
  /** PowerShell agent shell command: `Set-Location -LiteralPath '<path>'; <agent> [--model '<alias>'] '<prompt>'`. */
1355
- export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias, conductorEnabled = false, resumeMode = false, workflow = "implement", reviewRounds, baseBranch, launchArgs = []) {
1402
+ export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove = false, modelAlias, conductorEnabled = false, resumeMode = false, workflow = "implement", reviewRounds, baseBranch, launchArgs = [], bridgeApiBaseUrl) {
1356
1403
  const invocation = buildAgentInvocation(agent, buildAgentPrompt(key, { autoApprove, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, agentName: agent.name }), powershellSquote, modelAlias, agent.name === "cursor-agent" && autoApprove ? worktreePath : launchArgs);
1357
- return `Set-Location -LiteralPath ${powershellSquote(worktreePath)}; ${invocation}`;
1404
+ return `Set-Location -LiteralPath ${powershellSquote(worktreePath)}; ${prependBridgeApiBaseUrlEnvAssignment(invocation, bridgeApiBaseUrl, "win32")}`;
1358
1405
  }
1359
1406
  /**
1360
1407
  * Build the shell command run inside each spawned tab/session, dispatched by
@@ -1364,10 +1411,10 @@ export function buildPowerShellAgentShellCommand(agent, key, worktreePath, autoA
1364
1411
  * injected as `--model` at the spawn boundary. `conductorEnabled` appends the
1365
1412
  * BAPI-586 PR-base contract instruction to the prompt (opt-in via `--conductor`).
1366
1413
  */
1367
- export function buildAgentShellCommand(agent, key, worktreePath, platform = "darwin", autoApprove = false, modelAlias, conductorEnabled = false, resumeMode = false, workflow = "implement", reviewRounds, baseBranch, launchArgs = []) {
1414
+ export function buildAgentShellCommand(agent, key, worktreePath, platform = "darwin", autoApprove = false, modelAlias, conductorEnabled = false, resumeMode = false, workflow = "implement", reviewRounds, baseBranch, launchArgs = [], bridgeApiBaseUrl) {
1368
1415
  if (platform === "win32")
1369
- return buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, launchArgs);
1370
- return buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, launchArgs);
1416
+ return buildPowerShellAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, launchArgs, bridgeApiBaseUrl);
1417
+ return buildPosixAgentShellCommand(agent, key, worktreePath, autoApprove, modelAlias, conductorEnabled, resumeMode, workflow, reviewRounds, baseBranch, launchArgs, bridgeApiBaseUrl);
1371
1418
  }
1372
1419
  /**
1373
1420
  * Build the shell command run inside a spawned tab/session for an ARBITRARY
@@ -2008,7 +2055,7 @@ export function getDryRunPlatformDetails(agent, platform = process.platform, env
2008
2055
  // buildAgentShellCommand/buildAgentPrompt path as a real spawn. A Codex
2009
2056
  // dry-run has no created worktree to bind, so its preview omits the
2010
2057
  // process-local -C/-c MCP scope that the real spawn resolves later.
2011
- buildAgentShellCommand: (key, worktreePath, modelAlias, launchArgs) => prependBaseBranchEnvAssignment(prependRepoNameEnvAssignment(buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled, false, workflow, reviewRounds, baseBranch, launchArgs), repoName, platform), resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch), platform),
2058
+ buildAgentShellCommand: (key, worktreePath, modelAlias, launchArgs) => prependBaseBranchEnvAssignment(prependRepoNameEnvAssignment(buildAgentShellCommand(agent, key, worktreePath, platform, autoApprove, modelAlias, conductorEnabled, false, workflow, reviewRounds, baseBranch, launchArgs, resolveSpawnBridgeApiBaseUrl(env)), repoName, platform), resolvePrBaseBranchEnvValue(conductorEnabled, baseBranch), platform),
2012
2059
  };
2013
2060
  }
2014
2061
  /**
@@ -2017,7 +2064,7 @@ export function getDryRunPlatformDetails(agent, platform = process.platform, env
2017
2064
  * the `bapi` target. Lists both registration files and the version-pinned shim
2018
2065
  * command. Pure formatting — implies no credentials and writes no files.
2019
2066
  */
2020
- export function buildDryRunMcpProvisioningLines(worktreePath, platform = process.platform, mcpServerInvocation) {
2067
+ export function buildDryRunMcpProvisioningLines(worktreePath, platform = process.platform, mcpServerInvocation, bridgeApiBaseUrl) {
2021
2068
  const api = platform === "win32" ? path.win32 : path.posix;
2022
2069
  const mcpJson = api.join(worktreePath, ".mcp.json");
2023
2070
  const cursorJson = api.join(worktreePath, ".cursor", "mcp.json");
@@ -2038,6 +2085,7 @@ export function buildDryRunMcpProvisioningLines(worktreePath, platform = process
2038
2085
  `DRY-RUN: ${mcpJson}`,
2039
2086
  `DRY-RUN: ${cursorJson}`,
2040
2087
  `DRY-RUN: ${shim}`,
2088
+ ...(bridgeApiBaseUrl ? [`DRY-RUN: bapi shim adds --base-url ${bridgeApiBaseUrl}`] : []),
2041
2089
  ];
2042
2090
  }
2043
2091
  /**
@@ -2062,7 +2110,7 @@ export function buildDryRunDetailLines(agent, key, branch, platform = process.pl
2062
2110
  `DRY-RUN: ${worktrunkBinary} ${wtArgs.join(" ")}`,
2063
2111
  `DRY-RUN: ${agentInvocation}`,
2064
2112
  ...(prBaseBranch ? [`DRY-RUN: PR base: ${prBaseBranch}`] : []),
2065
- ...buildDryRunMcpProvisioningLines("<worktree-path>", platform, mcpServerInvocation),
2113
+ ...buildDryRunMcpProvisioningLines("<worktree-path>", platform, mcpServerInvocation, resolveSpawnBridgeApiBaseUrl(env)),
2066
2114
  ];
2067
2115
  }
2068
2116
  /**
@@ -2136,13 +2184,15 @@ export function formatSummaryReport(rows) {
2136
2184
  * Provisioning needs real `fs/promises` ops (not part of the command-runner DI
2137
2185
  * boundary), but inherits the platform and cwd from the orchestration deps.
2138
2186
  */
2139
- export function buildMcpProvisioningDeps(deps) {
2187
+ export function buildMcpProvisioningDeps(deps, bridgeApiBaseUrlOverride) {
2140
2188
  return {
2189
+ env: deps.env,
2141
2190
  readFile: deps.readFile ?? ((filePath) => readFile(filePath, "utf-8")),
2142
2191
  writeFile: deps.writeFile ?? ((filePath, data) => writeFile(filePath, data, "utf-8")),
2143
2192
  mkdir: deps.mkdir ?? ((dirPath, options) => mkdir(dirPath, options)),
2144
2193
  platform: deps.platform,
2145
2194
  cwd: deps.cwd,
2195
+ bridgeApiBaseUrl: bridgeApiBaseUrlOverride ?? resolveSpawnBridgeApiBaseUrl(deps.env),
2146
2196
  // Resolve how the worker MCP shim launches at dispatch time from the running
2147
2197
  // conductor's own on-disk build (absolute-build-path primary, npm-channel
2148
2198
  // fallback) — never from process.cwd(). `nodeExecutable` is kept as "node"
@@ -3199,6 +3249,7 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
3199
3249
  // Fail-open: an unresolved name is warned about loudly but never aborts the run.
3200
3250
  const resolveRepoNameFn = overrides.resolveRepoName ?? resolveStartTicketsRepoName;
3201
3251
  const resolvedRepoName = await resolveRepoNameFn(deps);
3252
+ const bridgeApiBaseUrl = await resolveStartTicketsBridgeApiBaseUrl(deps, agent);
3202
3253
  if (!resolvedRepoName) {
3203
3254
  overrides.repoNameWarningLog?.("Warning: could not resolve the repo name (set BAPI_REPO_NAME or add a valid " +
3204
3255
  ".bridge/config). Spawned worktrees may fall back to the directory name and " +
@@ -3211,7 +3262,7 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
3211
3262
  // epic.base_branch override applied above) so the spawned worker gets
3212
3263
  // BAPI_BASE_BRANCH — every conductor worker, and a plain worker whose base
3213
3264
  // is non-default.
3214
- options.baseBranch);
3265
+ options.baseBranch, bridgeApiBaseUrl);
3215
3266
  if (!platformConfig.ok)
3216
3267
  return { ok: false, error: platformConfig.error };
3217
3268
  // BAPI-527: resolve the base start point new worktrees are cut from BEFORE any
@@ -3261,7 +3312,7 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
3261
3312
  const provisionCommandsFn = overrides.provisionCommands ??
3262
3313
  ((rows, d) => provisionCommandsForCreatedWorktrees(rows, buildCommandProvisioningDeps(d), agent.name === "codex"));
3263
3314
  const provisionFn = overrides.provisionMcpRegistrations ??
3264
- ((rows, d) => provisionMcpRegistrationsForCreatedWorktrees(rows, buildMcpProvisioningDeps(d)));
3315
+ ((rows, d) => provisionMcpRegistrationsForCreatedWorktrees(rows, buildMcpProvisioningDeps(d, bridgeApiBaseUrl)));
3265
3316
  const materializeFn = overrides.materializeFileCredentials ?? materializeFileCredentialsForCreatedWorktrees;
3266
3317
  const detectTerminalFn = overrides.detectTerminal ?? detectTerminal;
3267
3318
  const spawnTabsFn = overrides.spawnTabsForCreatedWorktrees ?? spawnTabsForCreatedWorktrees;
@@ -3427,7 +3478,7 @@ export async function runStartTicketsCli(argv, overrides = {}) {
3427
3478
  return 1;
3428
3479
  }
3429
3480
  const options = parsed.options;
3430
- const deps = overrides.deps ?? createDefaultStartTicketsDeps();
3481
+ const rawDeps = overrides.deps ?? createDefaultStartTicketsDeps();
3431
3482
  const orchestrate = overrides.orchestrate ?? orchestrateStartTickets;
3432
3483
  // The parser validates `--agent`, so this resolves to a real spec; the guard
3433
3484
  // is defensive only.
@@ -3436,6 +3487,14 @@ export async function runStartTicketsCli(argv, overrides = {}) {
3436
3487
  errorLog(`Error: Unknown agent: '${options.agentName}'. Launchable agents: ${formatStartTicketsAgentNames()}.`);
3437
3488
  return 1;
3438
3489
  }
3490
+ // The editor's MCP registration can have a local endpoint that its Bash tool
3491
+ // does not inherit. Use that endpoint for the CLI's own routing as well as for
3492
+ // its generated worktree registration and terminal command. Never mutate the
3493
+ // caller's environment or read/copy its API key from the registration.
3494
+ const bridgeApiBaseUrl = await resolveStartTicketsBridgeApiBaseUrl(rawDeps, agent);
3495
+ const deps = bridgeApiBaseUrl
3496
+ ? { ...rawDeps, env: { ...rawDeps.env, BAPI_BASE_URL: bridgeApiBaseUrl } }
3497
+ : rawDeps;
3439
3498
  // BAPI-642: a malformed `--tier` value is fail-open — emit ONE concise,
3440
3499
  // secret-free warning and continue (routing applies the premium fallback). It
3441
3500
  // never aborts the launch or changes the numeric exit code.
@@ -1,4 +1,4 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.56";
3
- export const BUILD_COMMIT = "ba95aad09dd4";
4
- export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.56", "serve"];
2
+ export const VERSION = "0.2.57";
3
+ export const BUILD_COMMIT = "dcfa5632b4eb";
4
+ export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.57", "serve"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.56",
3
+ "version": "0.2.57",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",