@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.
- package/README.md +490 -366
- package/build/agent-capabilities/probe-context.js +8 -1
- package/build/agent-capabilities/probes.js +7 -1
- package/build/agents.generated.js +1 -1
- package/build/base-url.js +79 -0
- package/build/bridge-api-urls.js +9 -0
- package/build/chain-orchestrator.js +93 -15
- package/build/claude-review-workflow.js +264 -0
- package/build/claude-user-config-doctor.js +317 -0
- package/build/cli-release.js +53 -0
- package/build/commands.generated.js +6 -5
- package/build/conductor/bridge-api-client.js +393 -4
- package/build/conductor/deny-enforcement-preflight.js +1 -0
- package/build/conductor/done-gate.js +44 -5
- package/build/conductor/epic-reconcile.js +6 -0
- package/build/conductor/install-doctor.js +462 -0
- package/build/conductor-bin.js +3 -3
- package/build/conductor-bundle-artifacts.js +37 -15
- package/build/credential-store.js +205 -4
- package/build/direct-ticket-tools.js +70 -0
- package/build/doctor.js +473 -81
- package/build/executor/cli.js +83 -6
- package/build/executor/credentials.js +39 -11
- package/build/executor/deps.js +32 -1
- package/build/executor/env.js +71 -28
- package/build/executor/heartbeat.js +138 -17
- package/build/executor/http-client.js +49 -8
- package/build/executor/index.js +4 -0
- package/build/executor/job-errors.js +4 -0
- package/build/executor/job-runner.js +530 -20
- package/build/executor/observation.js +130 -0
- package/build/executor/permissions.js +114 -8
- package/build/executor/preflight.js +127 -8
- package/build/executor/prompt-spec.js +51 -0
- package/build/executor/runner.js +23 -2
- package/build/executor/service-unit.js +876 -0
- package/build/executor/test-clock.js +75 -3
- package/build/executor/types.js +4 -18
- package/build/executor/worker-command.js +73 -12
- package/build/executor/worker-config-isolation.js +287 -0
- package/build/executor/worker-finalization.js +68 -14
- package/build/executor/worktree.js +46 -4
- package/build/index.js +1039 -237
- package/build/init.js +503 -111
- package/build/install-bridge-conductor.js +491 -0
- package/build/install-bridge.js +1188 -247
- package/build/install-reexec.js +233 -0
- package/build/launcher-config-inspection.js +351 -0
- package/build/mcp-host-config.js +11 -1
- package/build/mcp-install-state.js +32 -0
- package/build/mcp-invoke.js +49 -6
- package/build/mcp-provisioning.js +52 -13
- package/build/mcp-registration-doctor.js +14 -5
- package/build/notifications.js +553 -0
- package/build/pipeline-orchestrator.js +146 -4
- package/build/pipeline-utils.js +3 -0
- package/build/pipelines.generated.js +32 -13
- package/build/plan-execution-ledger.js +550 -0
- package/build/plan-phase-routing.js +272 -0
- package/build/plane/alembic-head.js +110 -0
- package/build/plane/build-freshness.js +167 -0
- package/build/plane/cli.js +480 -0
- package/build/plane/defaults.js +266 -0
- package/build/plane/manifest.js +377 -0
- package/build/plane/member-logs.js +147 -0
- package/build/plane/member-roster.js +147 -0
- package/build/plane/preflight.js +289 -0
- package/build/plane/shutdown.js +195 -0
- package/build/plane/status.js +125 -0
- package/build/plane/supervisor.js +569 -0
- package/build/plane/test-fakes.js +156 -0
- package/build/plane/types.js +75 -0
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +259 -0
- package/build/setup-epic.js +772 -28
- package/build/sfcc/log-gate.js +38 -11
- package/build/sfcc/log-query.js +55 -15
- package/build/sfcc/ocapi-shape.js +51 -14
- package/build/sfcc/output.js +41 -11
- package/build/sfcc/permissions.js +24 -2
- package/build/sfcc/read-projection.js +181 -0
- package/build/sfcc/read-result.js +158 -0
- package/build/sfcc/reads-custom-object-def.js +29 -18
- package/build/sfcc/reads-site-preference.js +75 -29
- package/build/sfcc/reads-system-object.js +40 -34
- package/build/sfcc/sfcc-result.js +106 -0
- package/build/sfcc/tool-wrapper.js +56 -13
- package/build/sfcc/write-grants.js +45 -22
- package/build/sfcc/write-guard.js +21 -13
- package/build/sfcc/write-result.js +61 -14
- package/build/sfcc/write-tool-common.js +126 -32
- package/build/sfcc/writes-system-object.js +11 -50
- package/build/start-tickets-prereqs.js +129 -0
- package/build/start-tickets.js +17 -13
- package/build/ticket-backend-metadata.js +59 -0
- package/build/ticket-key-utils.js +92 -0
- package/build/tool-error-envelope.js +71 -0
- package/build/tool-surface-gating.js +72 -0
- package/build/update-status.js +102 -0
- package/build/upgrade-advice.js +47 -0
- package/build/upgrade-cli.js +412 -110
- package/build/version.generated.js +1 -1
- package/build/worktree-core.js +73 -0
- package/docs/CONDUCTOR.md +132 -6
- package/docs/install/mcp-tool-integrations.md +29 -21
- package/package.json +9 -6
- package/pipelines/implement-ticket.json +19 -4
- package/build/conductor/supervisor-judgment-python.js +0 -141
- package/build/conductor/supervisor-judgment.js +0 -215
package/build/mcp-invoke.js
CHANGED
|
@@ -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
|
|
56
|
-
* flags, positional arguments, missing values, and
|
|
57
|
-
* non-empty `--target` identifier and a
|
|
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
|
-
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
12
|
-
*
|
|
13
|
-
* `
|
|
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
|
-
|
|
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) };
|