@daghis/teamcity-mcp 1.10.2 → 1.10.4
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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/index.js +182 -32
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.10.4](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.10.3...teamcity-mcp-v1.10.4) (2025-10-04)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **tools:** normalize branch locators for list_builds ([#228](https://github.com/Daghis/teamcity-mcp/issues/228)) ([16fe7f0](https://github.com/Daghis/teamcity-mcp/commit/16fe7f0e2b46d04e66253ddb3cd9a06c5febf0b6))
|
|
9
|
+
|
|
10
|
+
## [1.10.3](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.10.2...teamcity-mcp-v1.10.3) (2025-09-27)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **tools:** honor trigger_build branch overrides (210) ([#223](https://github.com/Daghis/teamcity-mcp/issues/223)) ([7222c28](https://github.com/Daghis/teamcity-mcp/commit/7222c28c4fc9a307222ee9a50fa518127f5187de))
|
|
16
|
+
|
|
3
17
|
## [1.10.2](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.10.1...teamcity-mcp-v1.10.2) (2025-09-27)
|
|
4
18
|
|
|
5
19
|
|
package/dist/index.js
CHANGED
|
@@ -610,9 +610,7 @@ function loadConfig() {
|
|
|
610
610
|
}
|
|
611
611
|
var cachedConfig = null;
|
|
612
612
|
function getConfig() {
|
|
613
|
-
|
|
614
|
-
cachedConfig = loadConfig();
|
|
615
|
-
}
|
|
613
|
+
cachedConfig ??= loadConfig();
|
|
616
614
|
return cachedConfig;
|
|
617
615
|
}
|
|
618
616
|
function isTest() {
|
|
@@ -1756,18 +1754,14 @@ var BuildConfigurationCloneManager = class {
|
|
|
1756
1754
|
};
|
|
1757
1755
|
}
|
|
1758
1756
|
if (options.copyBuildCounter && source.buildNumberCounter) {
|
|
1759
|
-
|
|
1760
|
-
configPayload.settings = { property: [] };
|
|
1761
|
-
}
|
|
1757
|
+
configPayload.settings ??= { property: [] };
|
|
1762
1758
|
configPayload.settings.property?.push({
|
|
1763
1759
|
name: "buildNumberCounter",
|
|
1764
1760
|
value: source.buildNumberCounter.toString()
|
|
1765
1761
|
});
|
|
1766
1762
|
}
|
|
1767
1763
|
if (source.buildNumberFormat) {
|
|
1768
|
-
|
|
1769
|
-
configPayload.settings = { property: [] };
|
|
1770
|
-
}
|
|
1764
|
+
configPayload.settings ??= { property: [] };
|
|
1771
1765
|
configPayload.settings.property?.push({
|
|
1772
1766
|
name: "buildNumberPattern",
|
|
1773
1767
|
value: source.buildNumberFormat
|
|
@@ -3586,6 +3580,100 @@ async function fetchAllPages(fetchFn, options = {}) {
|
|
|
3586
3580
|
return allItems;
|
|
3587
3581
|
}
|
|
3588
3582
|
|
|
3583
|
+
// src/utils/list-builds-locator.ts
|
|
3584
|
+
var SIMPLE_BRANCH_VALUES = /* @__PURE__ */ new Set([
|
|
3585
|
+
"default:true",
|
|
3586
|
+
"default:false",
|
|
3587
|
+
"default:any",
|
|
3588
|
+
"unspecified:true",
|
|
3589
|
+
"unspecified:false",
|
|
3590
|
+
"unspecified:any",
|
|
3591
|
+
"branched:true",
|
|
3592
|
+
"branched:false",
|
|
3593
|
+
"branched:any"
|
|
3594
|
+
]);
|
|
3595
|
+
var BRANCH_PREFIXES_ALLOW_UNWRAPPED = ["default:", "unspecified:", "branched:", "policy:"];
|
|
3596
|
+
function splitLocatorParts(locator) {
|
|
3597
|
+
const parts = [];
|
|
3598
|
+
let current = "";
|
|
3599
|
+
let depth = 0;
|
|
3600
|
+
for (const char of locator) {
|
|
3601
|
+
if (char === "," && depth === 0) {
|
|
3602
|
+
const piece = current.trim();
|
|
3603
|
+
if (piece.length > 0) {
|
|
3604
|
+
parts.push(piece);
|
|
3605
|
+
}
|
|
3606
|
+
current = "";
|
|
3607
|
+
continue;
|
|
3608
|
+
}
|
|
3609
|
+
if (char === "(") {
|
|
3610
|
+
depth += 1;
|
|
3611
|
+
} else if (char === ")" && depth > 0) {
|
|
3612
|
+
depth -= 1;
|
|
3613
|
+
}
|
|
3614
|
+
current += char;
|
|
3615
|
+
}
|
|
3616
|
+
const finalPiece = current.trim();
|
|
3617
|
+
if (finalPiece.length > 0) {
|
|
3618
|
+
parts.push(finalPiece);
|
|
3619
|
+
}
|
|
3620
|
+
return parts;
|
|
3621
|
+
}
|
|
3622
|
+
function wrapBranchValue(value) {
|
|
3623
|
+
const trimmed = value.trim();
|
|
3624
|
+
if (trimmed.length === 0) {
|
|
3625
|
+
return trimmed;
|
|
3626
|
+
}
|
|
3627
|
+
if (trimmed.startsWith("(")) {
|
|
3628
|
+
return trimmed;
|
|
3629
|
+
}
|
|
3630
|
+
const lower = trimmed.toLowerCase();
|
|
3631
|
+
if (SIMPLE_BRANCH_VALUES.has(lower)) {
|
|
3632
|
+
return trimmed;
|
|
3633
|
+
}
|
|
3634
|
+
if (BRANCH_PREFIXES_ALLOW_UNWRAPPED.some((prefix) => lower.startsWith(prefix))) {
|
|
3635
|
+
return trimmed;
|
|
3636
|
+
}
|
|
3637
|
+
if (trimmed.includes("*") && !trimmed.includes(":")) {
|
|
3638
|
+
return trimmed;
|
|
3639
|
+
}
|
|
3640
|
+
if (trimmed.includes("/") || trimmed.includes(":") || /\s/.test(trimmed)) {
|
|
3641
|
+
return `(${trimmed})`;
|
|
3642
|
+
}
|
|
3643
|
+
return trimmed;
|
|
3644
|
+
}
|
|
3645
|
+
function normalizeBranchSegment(segment) {
|
|
3646
|
+
const trimmed = segment.trim();
|
|
3647
|
+
if (trimmed.length === 0) {
|
|
3648
|
+
return trimmed;
|
|
3649
|
+
}
|
|
3650
|
+
if (!trimmed.toLowerCase().startsWith("branch:")) {
|
|
3651
|
+
return trimmed;
|
|
3652
|
+
}
|
|
3653
|
+
const rawValue = trimmed.slice("branch:".length).trim();
|
|
3654
|
+
if (rawValue.length === 0) {
|
|
3655
|
+
return trimmed;
|
|
3656
|
+
}
|
|
3657
|
+
if (rawValue.startsWith("(")) {
|
|
3658
|
+
return `branch:${rawValue}`;
|
|
3659
|
+
}
|
|
3660
|
+
return `branch:${wrapBranchValue(rawValue)}`;
|
|
3661
|
+
}
|
|
3662
|
+
function normalizeLocatorSegments(locator) {
|
|
3663
|
+
if (!locator) {
|
|
3664
|
+
return [];
|
|
3665
|
+
}
|
|
3666
|
+
return splitLocatorParts(locator).map((segment) => normalizeBranchSegment(segment)).filter((segment) => segment.length > 0);
|
|
3667
|
+
}
|
|
3668
|
+
function hasBranchSegment(segments) {
|
|
3669
|
+
return segments.some((segment) => segment.toLowerCase().startsWith("branch:"));
|
|
3670
|
+
}
|
|
3671
|
+
function buildBranchSegmentInput(branchInput) {
|
|
3672
|
+
const normalized = branchInput.trim();
|
|
3673
|
+
const withPrefix = normalized.toLowerCase().startsWith("branch:") ? normalized : `branch:${normalized}`;
|
|
3674
|
+
return normalizeBranchSegment(withPrefix);
|
|
3675
|
+
}
|
|
3676
|
+
|
|
3589
3677
|
// src/utils/mcp.ts
|
|
3590
3678
|
var import_zod3 = require("zod");
|
|
3591
3679
|
|
|
@@ -3686,9 +3774,7 @@ init_errors();
|
|
|
3686
3774
|
var ErrorLogger = class _ErrorLogger {
|
|
3687
3775
|
static instance;
|
|
3688
3776
|
static getInstance() {
|
|
3689
|
-
|
|
3690
|
-
_ErrorLogger.instance = new _ErrorLogger();
|
|
3691
|
-
}
|
|
3777
|
+
_ErrorLogger.instance ??= new _ErrorLogger();
|
|
3692
3778
|
return _ErrorLogger.instance;
|
|
3693
3779
|
}
|
|
3694
3780
|
/**
|
|
@@ -3773,9 +3859,7 @@ var GlobalErrorHandler = class _GlobalErrorHandler {
|
|
|
3773
3859
|
}
|
|
3774
3860
|
static instance;
|
|
3775
3861
|
static getInstance(options) {
|
|
3776
|
-
|
|
3777
|
-
_GlobalErrorHandler.instance = new _GlobalErrorHandler(options);
|
|
3778
|
-
}
|
|
3862
|
+
_GlobalErrorHandler.instance ??= new _GlobalErrorHandler(options);
|
|
3779
3863
|
return _GlobalErrorHandler.instance;
|
|
3780
3864
|
}
|
|
3781
3865
|
/**
|
|
@@ -38447,6 +38531,7 @@ var DEV_TOOLS = [
|
|
|
38447
38531
|
locator: { type: "string", description: "Optional build locator to filter builds" },
|
|
38448
38532
|
projectId: { type: "string", description: "Filter by project ID" },
|
|
38449
38533
|
buildTypeId: { type: "string", description: "Filter by build type ID" },
|
|
38534
|
+
branch: { type: "string", description: "Filter by branch (logical or VCS name)" },
|
|
38450
38535
|
status: {
|
|
38451
38536
|
type: "string",
|
|
38452
38537
|
enum: ["SUCCESS", "FAILURE", "ERROR"],
|
|
@@ -38467,6 +38552,7 @@ var DEV_TOOLS = [
|
|
|
38467
38552
|
locator: import_zod4.z.string().min(1).optional(),
|
|
38468
38553
|
projectId: import_zod4.z.string().min(1).optional(),
|
|
38469
38554
|
buildTypeId: import_zod4.z.string().min(1).optional(),
|
|
38555
|
+
branch: import_zod4.z.string().min(1).optional(),
|
|
38470
38556
|
status: import_zod4.z.enum(["SUCCESS", "FAILURE", "ERROR"]).optional(),
|
|
38471
38557
|
count: import_zod4.z.number().int().min(1).max(1e3).default(10).optional(),
|
|
38472
38558
|
pageSize: import_zod4.z.number().int().min(1).max(1e3).optional(),
|
|
@@ -38479,10 +38565,17 @@ var DEV_TOOLS = [
|
|
|
38479
38565
|
schema,
|
|
38480
38566
|
async (typed) => {
|
|
38481
38567
|
const adapter = createAdapterFromTeamCityAPI(TeamCityAPI.getInstance());
|
|
38482
|
-
const
|
|
38483
|
-
|
|
38568
|
+
const locatorSegments = normalizeLocatorSegments(typed.locator);
|
|
38569
|
+
const hasBranchInLocator = hasBranchSegment(locatorSegments);
|
|
38570
|
+
const baseParts = [...locatorSegments];
|
|
38484
38571
|
if (typed.projectId) baseParts.push(`project:(id:${typed.projectId})`);
|
|
38485
38572
|
if (typed.buildTypeId) baseParts.push(`buildType:(id:${typed.buildTypeId})`);
|
|
38573
|
+
if (typed.branch) {
|
|
38574
|
+
const branchSegment = buildBranchSegmentInput(typed.branch);
|
|
38575
|
+
if (!hasBranchInLocator) {
|
|
38576
|
+
baseParts.push(branchSegment);
|
|
38577
|
+
}
|
|
38578
|
+
}
|
|
38486
38579
|
if (typed.status) baseParts.push(`status:${typed.status}`);
|
|
38487
38580
|
const pageSize = typed.pageSize ?? typed.count ?? 100;
|
|
38488
38581
|
const baseFetch = async ({ count, start }) => {
|
|
@@ -38549,7 +38642,12 @@ var DEV_TOOLS = [
|
|
|
38549
38642
|
properties: {
|
|
38550
38643
|
buildTypeId: { type: "string", description: "Build type ID to trigger" },
|
|
38551
38644
|
branchName: { type: "string", description: "Branch to build (optional)" },
|
|
38552
|
-
comment: { type: "string", description: "Build comment (optional)" }
|
|
38645
|
+
comment: { type: "string", description: "Build comment (optional)" },
|
|
38646
|
+
properties: {
|
|
38647
|
+
type: "object",
|
|
38648
|
+
description: "Optional build parameters to set when triggering the build",
|
|
38649
|
+
additionalProperties: { type: "string" }
|
|
38650
|
+
}
|
|
38553
38651
|
},
|
|
38554
38652
|
required: ["buildTypeId"]
|
|
38555
38653
|
},
|
|
@@ -38557,35 +38655,84 @@ var DEV_TOOLS = [
|
|
|
38557
38655
|
const schema = import_zod4.z.object({
|
|
38558
38656
|
buildTypeId: import_zod4.z.string().min(1),
|
|
38559
38657
|
branchName: import_zod4.z.string().min(1).max(255).optional(),
|
|
38560
|
-
comment: import_zod4.z.string().max(500).optional()
|
|
38658
|
+
comment: import_zod4.z.string().max(500).optional(),
|
|
38659
|
+
properties: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.string()).optional()
|
|
38561
38660
|
});
|
|
38562
38661
|
return runTool(
|
|
38563
38662
|
"trigger_build",
|
|
38564
38663
|
schema,
|
|
38565
38664
|
async (typed) => {
|
|
38566
38665
|
const adapter = createAdapterFromTeamCityAPI(TeamCityAPI.getInstance());
|
|
38567
|
-
|
|
38568
|
-
|
|
38569
|
-
|
|
38570
|
-
|
|
38571
|
-
|
|
38666
|
+
const directBranch = typed.branchName?.trim();
|
|
38667
|
+
const normalizedDirectBranch = directBranch && directBranch.length > 0 ? directBranch : void 0;
|
|
38668
|
+
const rawPropertyBranch = typed.properties?.["teamcity.build.branch"];
|
|
38669
|
+
const trimmedPropertyBranch = rawPropertyBranch?.trim();
|
|
38670
|
+
const normalizedPropertyBranch = trimmedPropertyBranch && trimmedPropertyBranch.length > 0 ? trimmedPropertyBranch : void 0;
|
|
38671
|
+
const branchName = normalizedDirectBranch ?? normalizedPropertyBranch;
|
|
38672
|
+
if (normalizedDirectBranch && normalizedPropertyBranch && normalizedDirectBranch !== normalizedPropertyBranch) {
|
|
38673
|
+
const errorPayload = {
|
|
38674
|
+
success: false,
|
|
38675
|
+
action: "trigger_build",
|
|
38676
|
+
error: `Conflicting branch overrides: branchName='${normalizedDirectBranch}' vs properties.teamcity.build.branch='${normalizedPropertyBranch}'.`
|
|
38677
|
+
};
|
|
38678
|
+
return {
|
|
38679
|
+
success: false,
|
|
38680
|
+
error: errorPayload.error,
|
|
38681
|
+
content: [{ type: "text", text: JSON.stringify(errorPayload, null, 2) }]
|
|
38682
|
+
};
|
|
38683
|
+
}
|
|
38684
|
+
const propertyEntries = typed.properties ? Object.entries(typed.properties).map(([name, value]) => ({
|
|
38685
|
+
name,
|
|
38686
|
+
value: name === "teamcity.build.branch" && normalizedPropertyBranch ? normalizedPropertyBranch : value
|
|
38687
|
+
})) : [];
|
|
38688
|
+
const propertiesPayload = propertyEntries.length > 0 ? { property: propertyEntries } : void 0;
|
|
38689
|
+
const buildRequest = {
|
|
38690
|
+
buildType: { id: typed.buildTypeId }
|
|
38691
|
+
};
|
|
38692
|
+
if (branchName) {
|
|
38693
|
+
buildRequest.branchName = branchName;
|
|
38694
|
+
}
|
|
38695
|
+
const commentText = typed.comment?.trim();
|
|
38696
|
+
if (commentText && commentText.length > 0) {
|
|
38697
|
+
buildRequest.comment = { text: commentText };
|
|
38698
|
+
}
|
|
38699
|
+
if (propertiesPayload) {
|
|
38700
|
+
buildRequest.properties = propertiesPayload;
|
|
38701
|
+
}
|
|
38702
|
+
const sendXmlFallback = async (error2) => {
|
|
38703
|
+
const escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
38704
|
+
const branchPart = branchName ? `<branchName>${escapeXml(branchName)}</branchName>` : "";
|
|
38705
|
+
const commentPart = commentText ? `<comment><text>${escapeXml(commentText)}</text></comment>` : "";
|
|
38706
|
+
const propertiesPart = propertiesPayload ? `<properties>${propertiesPayload.property.map(
|
|
38707
|
+
(prop) => `<property name="${escapeXml(prop.name)}" value="${escapeXml(prop.value)}"/>`
|
|
38708
|
+
).join("")}</properties>` : "";
|
|
38709
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?><build><buildType id="${escapeXml(
|
|
38710
|
+
typed.buildTypeId
|
|
38711
|
+
)}"/>${branchPart}${commentPart}${propertiesPart}</build>`;
|
|
38712
|
+
const response = await adapter.modules.buildQueue.addBuildToQueue(
|
|
38713
|
+
false,
|
|
38714
|
+
xml,
|
|
38715
|
+
{
|
|
38716
|
+
headers: { "Content-Type": "application/xml", Accept: "application/json" }
|
|
38717
|
+
}
|
|
38572
38718
|
);
|
|
38719
|
+
const build = response.data;
|
|
38573
38720
|
return json({
|
|
38574
38721
|
success: true,
|
|
38575
38722
|
action: "trigger_build",
|
|
38576
38723
|
buildId: String(build.id ?? ""),
|
|
38577
38724
|
state: build.state ?? void 0,
|
|
38578
|
-
status: build.status ?? void 0
|
|
38725
|
+
status: build.status ?? void 0,
|
|
38726
|
+
branchName: build.branchName ?? branchName,
|
|
38727
|
+
fallback: { mode: "xml", reason: error2?.message }
|
|
38579
38728
|
});
|
|
38580
|
-
}
|
|
38581
|
-
|
|
38582
|
-
const commentPart = typed.comment ? `<comment><text>${typed.comment.replace(/</g, "<").replace(/>/g, ">")}</text></comment>` : "";
|
|
38583
|
-
const xml = `<?xml version="1.0" encoding="UTF-8"?><build><buildType id="${typed.buildTypeId}"/>${branchPart}${commentPart}</build>`;
|
|
38729
|
+
};
|
|
38730
|
+
try {
|
|
38584
38731
|
const response = await adapter.modules.buildQueue.addBuildToQueue(
|
|
38585
38732
|
false,
|
|
38586
|
-
|
|
38733
|
+
buildRequest,
|
|
38587
38734
|
{
|
|
38588
|
-
headers: { "Content-Type": "application/
|
|
38735
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" }
|
|
38589
38736
|
}
|
|
38590
38737
|
);
|
|
38591
38738
|
const build = response.data;
|
|
@@ -38594,8 +38741,11 @@ var DEV_TOOLS = [
|
|
|
38594
38741
|
action: "trigger_build",
|
|
38595
38742
|
buildId: String(build.id ?? ""),
|
|
38596
38743
|
state: build.state ?? void 0,
|
|
38597
|
-
status: build.status ?? void 0
|
|
38744
|
+
status: build.status ?? void 0,
|
|
38745
|
+
branchName: build.branchName ?? branchName
|
|
38598
38746
|
});
|
|
38747
|
+
} catch (error2) {
|
|
38748
|
+
return sendXmlFallback(error2);
|
|
38599
38749
|
}
|
|
38600
38750
|
},
|
|
38601
38751
|
args
|