@bridge_gpt/mcp-server 0.2.50 → 0.2.51
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 +7 -7
- package/build/conduct-epic/bridge-client.js +115 -1
- package/build/conduct-epic/cli.js +351 -33
- package/build/conduct-epic/cut-protocol.js +51 -0
- package/build/doctor.js +190 -0
- package/build/epic-integration-pr.js +280 -0
- package/build/executor/job-runner.js +7 -1
- package/build/executor/merge-job.js +46 -1
- package/build/index.js +71 -24
- package/build/pipelines.generated.js +7 -1
- package/build/plan-epic-conductor-eligibility.js +183 -0
- package/build/readme.generated.js +1 -1
- package/build/setup-epic.js +32 -0
- package/build/sfcc/reads-custom-object-def.js +10 -13
- package/build/sfcc/reads-site-preference.js +5 -5
- package/build/sfcc/reads-system-object.js +4 -4
- package/build/sfcc/writes-custom-object-def.js +7 -7
- package/build/sfcc/writes-site-preference.js +4 -3
- package/build/sfcc/writes-system-object.js +7 -6
- package/build/version.generated.js +3 -3
- package/package.json +3 -2
- package/pipelines/plan-epic.json +5 -0
package/README.md
CHANGED
|
@@ -352,7 +352,7 @@ later boot.
|
|
|
352
352
|
"mcpServers": {
|
|
353
353
|
"bridge": {
|
|
354
354
|
"command": "npx",
|
|
355
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
355
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"],
|
|
356
356
|
"env": {
|
|
357
357
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
358
358
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -374,7 +374,7 @@ later boot.
|
|
|
374
374
|
"bridge": {
|
|
375
375
|
"type": "stdio",
|
|
376
376
|
"command": "npx",
|
|
377
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
377
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"],
|
|
378
378
|
"env": {
|
|
379
379
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
380
380
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -396,7 +396,7 @@ later boot.
|
|
|
396
396
|
"bridge": {
|
|
397
397
|
"type": "stdio",
|
|
398
398
|
"command": "npx",
|
|
399
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
399
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"],
|
|
400
400
|
"env": {
|
|
401
401
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
402
402
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -421,7 +421,7 @@ you select `copilot-cli`; the shape below is what it produces.
|
|
|
421
421
|
"bridge": {
|
|
422
422
|
"type": "local",
|
|
423
423
|
"command": "npx",
|
|
424
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
424
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"],
|
|
425
425
|
"tools": ["*"],
|
|
426
426
|
"env": {
|
|
427
427
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
@@ -445,7 +445,7 @@ Windsurf only supports global MCP configuration.
|
|
|
445
445
|
"mcpServers": {
|
|
446
446
|
"bridge": {
|
|
447
447
|
"command": "npx",
|
|
448
|
-
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
448
|
+
"args": ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"],
|
|
449
449
|
"env": {
|
|
450
450
|
"BAPI_BASE_URL": "https://bridgegpt-api.com",
|
|
451
451
|
"BAPI_REPO_NAME": "your-repo",
|
|
@@ -464,7 +464,7 @@ Windsurf only supports global MCP configuration.
|
|
|
464
464
|
```toml
|
|
465
465
|
[mcp_servers.bridge]
|
|
466
466
|
command = "npx"
|
|
467
|
-
args = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.
|
|
467
|
+
args = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"]
|
|
468
468
|
|
|
469
469
|
[mcp_servers.bridge.env]
|
|
470
470
|
BAPI_BASE_URL = "https://bridgegpt-api.com"
|
|
@@ -1441,7 +1441,7 @@ The full surface, for when you need the complete enumeration. Day-to-day, use [U
|
|
|
1441
1441
|
|
|
1442
1442
|
### MCP tools
|
|
1443
1443
|
|
|
1444
|
-
The authoritative tool catalog covers **
|
|
1444
|
+
The authoritative tool catalog covers **70 tools** (enumerated below). What's actually registered in a session depends on `BRIDGE_MCP_PROFILE`: the default `core` profile loads a trimmed subset, and the conductor/pipeline-authoring/SFCC tools are added only under their respective profiles (see [Environment Variables](#environment-variables)). Async AI tools follow a request/get pattern: call the `request_*` tool to kick off generation, then the matching `get_*` tool to retrieve the result (or pass `wait_for_result: true` to poll automatically).
|
|
1445
1445
|
|
|
1446
1446
|
- **Connectivity & identity** — `ping` (its JSON also carries `docs_dir`, `role`, and `customer_type`)
|
|
1447
1447
|
- **Team & access** — `invite_member` (admin-only; mints a scoped access key for a teammate on an already-configured project — the plaintext key is shown exactly once)
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* No MCP tool is registered from this module — the `tools/list` token budget is
|
|
29
29
|
* effectively frozen.
|
|
30
30
|
*/
|
|
31
|
-
import { buildConductorJiraUrl, fetchConductorJsonPostWithTimeout, fetchConductorJsonPutWithTimeout, fetchConductorJsonWithTimeout, fetchConductorReadiness, fetchEffectiveSupervisorConfig, fetchEffectiveSupervisorSetup, fetchEpicRunState, fetchParseStatus, fetchPrReviewStatus, pollCiChecksForCommit, ConductorBridgeApiError, CONDUCTOR_FETCH_TIMEOUT_MS, } from "../conductor/bridge-api-client.js";
|
|
31
|
+
import { buildConductorJiraUrl, buildConductorVcsUrl, fetchConductorJsonPostWithTimeout, fetchConductorJsonPutWithTimeout, fetchConductorJsonWithTimeout, fetchConductorReadiness, fetchEffectiveSupervisorConfig, fetchEffectiveSupervisorSetup, fetchEpicRunState, fetchParseStatus, fetchPrReviewStatus, pollCiChecksForCommit, ConductorBridgeApiError, CONDUCTOR_FETCH_TIMEOUT_MS, } from "../conductor/bridge-api-client.js";
|
|
32
32
|
/**
|
|
33
33
|
* The one message returned for any failure whose cause cannot be described
|
|
34
34
|
* without risking the credential. Stable so callers can match on it.
|
|
@@ -657,3 +657,117 @@ export async function reclaimIndexScope(access, request, fetchImpl = globalThis.
|
|
|
657
657
|
},
|
|
658
658
|
};
|
|
659
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Epic-run statuses that mean a v2 conductor may still be driving the run.
|
|
662
|
+
*
|
|
663
|
+
* These are the NON-TERMINAL members of the server's `EpicRunStatus` vocabulary
|
|
664
|
+
* (`api/models/epic_run.py`: `planning | pending_approval | active | blocked |
|
|
665
|
+
* abandoned | done`); only `abandoned` and `done` are terminal. `active` is the
|
|
666
|
+
* state a run spends nearly all of its life in, so omitting it would make this
|
|
667
|
+
* whole advisory silent for exactly the overlap it exists to surface.
|
|
668
|
+
*
|
|
669
|
+
* The vocabulary matters and is easy to get wrong: `running`/`queued` belong to
|
|
670
|
+
* `ExecutorJobStatusValue` and `paused` to `RunControlState`, and none of the
|
|
671
|
+
* three is ever an `epic_runs.status`. Status alone is not liveness either — the
|
|
672
|
+
* caller additionally requires an unexpired lease, so a `blocked` run whose lease
|
|
673
|
+
* has lapsed is correctly read as wreckage rather than as a live conductor.
|
|
674
|
+
*/
|
|
675
|
+
const ACTIVE_EPIC_RUN_STATUSES = [
|
|
676
|
+
"planning",
|
|
677
|
+
"pending_approval",
|
|
678
|
+
"active",
|
|
679
|
+
"blocked",
|
|
680
|
+
];
|
|
681
|
+
/**
|
|
682
|
+
* `GET /automation/health?repo_name=` — read-only parse-dispatcher liveness.
|
|
683
|
+
*
|
|
684
|
+
* Root-mounted, NOT under `/jira`. The response is narrowed to two safe fields;
|
|
685
|
+
* signals, stale-ticket samples, queue contents, and every timestamp are
|
|
686
|
+
* deliberately dropped — an advisory line needs a verdict, not a report.
|
|
687
|
+
*
|
|
688
|
+
* `absent` is claimed ONLY on positive evidence: a `stale` or `never_seen`
|
|
689
|
+
* heartbeat together with a responding process that is not itself sweeping.
|
|
690
|
+
* Every other shape — `unknown`, a missing field, a malformed body, any read
|
|
691
|
+
* failure — is `unavailable`.
|
|
692
|
+
*/
|
|
693
|
+
export async function getParseDispatcherHealth(access, fetchImpl = globalThis.fetch) {
|
|
694
|
+
const result = await wrap(access, () => {
|
|
695
|
+
const url = new URL(buildConductorVcsUrl(access.baseUrl, "/automation/health"));
|
|
696
|
+
url.searchParams.set("repo_name", access.repoName);
|
|
697
|
+
return fetchConductorJsonWithTimeout(url.toString(), getHeaders(access), CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
698
|
+
});
|
|
699
|
+
if (!result.ok)
|
|
700
|
+
return result;
|
|
701
|
+
if (!isRecord(result.value)) {
|
|
702
|
+
return {
|
|
703
|
+
ok: true,
|
|
704
|
+
value: { observation: "unavailable", heartbeatState: null, respondingSchedulerRunning: false },
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
const reconciler = isRecord(result.value["reconciler"]) ? result.value["reconciler"] : null;
|
|
708
|
+
const scheduler = isRecord(result.value["scheduler"]) ? result.value["scheduler"] : null;
|
|
709
|
+
const rawState = reconciler === null ? undefined : reconciler["state"];
|
|
710
|
+
const heartbeatState = typeof rawState === "string" ? rawState : null;
|
|
711
|
+
const respondingSchedulerRunning = scheduler !== null &&
|
|
712
|
+
scheduler["running"] === true &&
|
|
713
|
+
typeof scheduler["job_count"] === "number" &&
|
|
714
|
+
scheduler["job_count"] > 0;
|
|
715
|
+
let observation;
|
|
716
|
+
if (heartbeatState === "fresh" || respondingSchedulerRunning) {
|
|
717
|
+
observation = "observed";
|
|
718
|
+
}
|
|
719
|
+
else if (heartbeatState === "stale" || heartbeatState === "never_seen") {
|
|
720
|
+
observation = "absent";
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
observation = "unavailable";
|
|
724
|
+
}
|
|
725
|
+
return { ok: true, value: { observation, heartbeatState, respondingSchedulerRunning } };
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* `GET /jira/epic-runs/runs?repo_name=` — live v2 conductor runs on THIS repo.
|
|
729
|
+
*
|
|
730
|
+
* Scoped by the authenticated repository identity carried on `access`; no caller
|
|
731
|
+
* ever names a repository here. Liveness is judged on the LEASE, not on the
|
|
732
|
+
* status alone: an `active` row whose lease expired is a crashed run, and calling
|
|
733
|
+
* that a concurrent conductor would make the advisory cry wolf on exactly the
|
|
734
|
+
* wreckage `recover` exists to clean up.
|
|
735
|
+
*
|
|
736
|
+
* Only `epic_key` and a controlled state survive into the result. Lease owners,
|
|
737
|
+
* run ids, policies, and timestamps are dropped.
|
|
738
|
+
*/
|
|
739
|
+
export async function getLiveRepositoryConductors(access, now, fetchImpl = globalThis.fetch) {
|
|
740
|
+
const result = await wrap(access, () => {
|
|
741
|
+
const url = buildConductorJiraUrl(access.baseUrl, "/epic-runs/runs", {
|
|
742
|
+
repo_name: access.repoName,
|
|
743
|
+
});
|
|
744
|
+
return fetchConductorJsonWithTimeout(url, getHeaders(access), CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
745
|
+
});
|
|
746
|
+
if (!result.ok)
|
|
747
|
+
return result;
|
|
748
|
+
if (!isRecord(result.value) || !Array.isArray(result.value["runs"])) {
|
|
749
|
+
return { ok: true, value: { observation: "unavailable", conductors: [] } };
|
|
750
|
+
}
|
|
751
|
+
const nowMs = now.getTime();
|
|
752
|
+
const conductors = [];
|
|
753
|
+
for (const entry of result.value["runs"]) {
|
|
754
|
+
if (!isRecord(entry))
|
|
755
|
+
continue;
|
|
756
|
+
const status = entry["status"];
|
|
757
|
+
if (typeof status !== "string" || !ACTIVE_EPIC_RUN_STATUSES.includes(status))
|
|
758
|
+
continue;
|
|
759
|
+
const leaseRaw = nullableString(entry["lease_expires_at"]);
|
|
760
|
+
if (typeof leaseRaw !== "string")
|
|
761
|
+
continue;
|
|
762
|
+
const leaseMs = Date.parse(leaseRaw);
|
|
763
|
+
if (!Number.isFinite(leaseMs) || leaseMs <= nowMs)
|
|
764
|
+
continue;
|
|
765
|
+
conductors.push({
|
|
766
|
+
kind: "v2",
|
|
767
|
+
epicKey: nullableString(entry["epic_key"]) ?? null,
|
|
768
|
+
state: status,
|
|
769
|
+
livenessSource: "lease",
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
return { ok: true, value: { observation: conductors.length > 0 ? "observed" : "absent", conductors } };
|
|
773
|
+
}
|