@daghis/teamcity-mcp 1.10.7 → 1.10.9
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 +16 -0
- package/dist/index.js +181 -8
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.10.9](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.10.8...teamcity-mcp-v1.10.9) (2025-11-04)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **server:** keep stdio transport alive ([#247](https://github.com/Daghis/teamcity-mcp/issues/247)) ([c325cf4](https://github.com/Daghis/teamcity-mcp/commit/c325cf4d436489a91cabf0bb8e0d84087c97a1e4))
|
|
9
|
+
* **server:** keep stdio transport alive (247) ([3b77eea](https://github.com/Daghis/teamcity-mcp/commit/3b77eea2fc771964c78a3b011f67ffb8afdd02e9))
|
|
10
|
+
|
|
11
|
+
## [1.10.8](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.10.7...teamcity-mcp-v1.10.8) (2025-10-07)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **teamcity:** send snapshot options separately (238) ([fc9b313](https://github.com/Daghis/teamcity-mcp/commit/fc9b31379feccded78db5a35e1dc6f5ad13dccbd))
|
|
17
|
+
* **teamcity:** send snapshot options separately (238) ([6af8ea6](https://github.com/Daghis/teamcity-mcp/commit/6af8ea63dcc7dfae915a55287f3e1bc31e0a5c03))
|
|
18
|
+
|
|
3
19
|
## [1.10.7](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.10.6...teamcity-mcp-v1.10.7) (2025-10-06)
|
|
4
20
|
|
|
5
21
|
|
package/dist/index.js
CHANGED
|
@@ -642,6 +642,43 @@ function getTeamCityToken() {
|
|
|
642
642
|
return config2.teamcity.token;
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
// src/server-runner.ts
|
|
646
|
+
async function startServerLifecycle(server, transport) {
|
|
647
|
+
await server.connect(transport);
|
|
648
|
+
return new Promise((resolve2, reject) => {
|
|
649
|
+
const previousOnClose = server.onclose;
|
|
650
|
+
const previousOnError = server.onerror;
|
|
651
|
+
const cleanup = () => {
|
|
652
|
+
server.onclose = previousOnClose;
|
|
653
|
+
server.onerror = previousOnError;
|
|
654
|
+
};
|
|
655
|
+
server.onclose = () => {
|
|
656
|
+
previousOnClose?.();
|
|
657
|
+
cleanup();
|
|
658
|
+
resolve2();
|
|
659
|
+
};
|
|
660
|
+
server.onerror = (rawError) => {
|
|
661
|
+
previousOnError?.(rawError);
|
|
662
|
+
const error2 = rawError instanceof Error ? rawError : new Error(String(rawError));
|
|
663
|
+
const closeTransport = () => {
|
|
664
|
+
if (!transport.close) {
|
|
665
|
+
return Promise.resolve();
|
|
666
|
+
}
|
|
667
|
+
try {
|
|
668
|
+
return Promise.resolve(transport.close());
|
|
669
|
+
} catch (closeError) {
|
|
670
|
+
return Promise.reject(closeError);
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
void closeTransport().catch(() => {
|
|
674
|
+
}).finally(() => {
|
|
675
|
+
cleanup();
|
|
676
|
+
reject(error2);
|
|
677
|
+
});
|
|
678
|
+
};
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
645
682
|
// src/server.ts
|
|
646
683
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
647
684
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
@@ -2342,6 +2379,13 @@ var defaultTypeFor = (dependencyType) => {
|
|
|
2342
2379
|
return void 0;
|
|
2343
2380
|
}
|
|
2344
2381
|
};
|
|
2382
|
+
var SNAPSHOT_DEPENDENCY_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
2383
|
+
"run-build-on-the-same-agent",
|
|
2384
|
+
"sync-revisions",
|
|
2385
|
+
"take-successful-builds-only",
|
|
2386
|
+
"take-started-build-with-same-revisions",
|
|
2387
|
+
"do-not-run-new-build-if-there-is-a-suitable-one"
|
|
2388
|
+
]);
|
|
2345
2389
|
var toStringRecord2 = (input) => {
|
|
2346
2390
|
if (!input) {
|
|
2347
2391
|
return {};
|
|
@@ -2381,6 +2425,30 @@ var recordToProperties2 = (record) => {
|
|
|
2381
2425
|
property: entries.map(([name, value]) => ({ name, value }))
|
|
2382
2426
|
};
|
|
2383
2427
|
};
|
|
2428
|
+
var optionsToRecord = (options) => {
|
|
2429
|
+
if (!options) {
|
|
2430
|
+
return {};
|
|
2431
|
+
}
|
|
2432
|
+
const optionEntries = options?.option;
|
|
2433
|
+
const collection = Array.isArray(optionEntries) ? optionEntries : optionEntries != null ? [optionEntries] : [];
|
|
2434
|
+
const map = {};
|
|
2435
|
+
for (const item of collection) {
|
|
2436
|
+
if (!item?.name) {
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
map[item.name] = item.value != null ? String(item.value) : "";
|
|
2440
|
+
}
|
|
2441
|
+
return map;
|
|
2442
|
+
};
|
|
2443
|
+
var recordToOptions = (record) => {
|
|
2444
|
+
const entries = Object.entries(record);
|
|
2445
|
+
if (entries.length === 0) {
|
|
2446
|
+
return void 0;
|
|
2447
|
+
}
|
|
2448
|
+
return {
|
|
2449
|
+
option: entries.map(([name, value]) => ({ name, value }))
|
|
2450
|
+
};
|
|
2451
|
+
};
|
|
2384
2452
|
var mergeRecords2 = (base, override) => {
|
|
2385
2453
|
const merged = { ...base };
|
|
2386
2454
|
for (const [key, value] of Object.entries(override)) {
|
|
@@ -2412,6 +2480,25 @@ var propertiesToXml = (properties) => {
|
|
|
2412
2480
|
}
|
|
2413
2481
|
return `<properties>${nodes.join("")}</properties>`;
|
|
2414
2482
|
};
|
|
2483
|
+
var optionsToXml = (options) => {
|
|
2484
|
+
if (!options) {
|
|
2485
|
+
return void 0;
|
|
2486
|
+
}
|
|
2487
|
+
const entries = options.option;
|
|
2488
|
+
const list = Array.isArray(entries) ? entries : entries != null ? [entries] : [];
|
|
2489
|
+
if (list.length === 0) {
|
|
2490
|
+
return void 0;
|
|
2491
|
+
}
|
|
2492
|
+
const nodes = list.filter((item) => item?.name).map((item) => {
|
|
2493
|
+
const name = item?.name ?? "";
|
|
2494
|
+
const value = item?.value != null ? String(item.value) : "";
|
|
2495
|
+
return `<option name="${escapeXml(name)}" value="${escapeXml(value)}"/>`;
|
|
2496
|
+
});
|
|
2497
|
+
if (nodes.length === 0) {
|
|
2498
|
+
return void 0;
|
|
2499
|
+
}
|
|
2500
|
+
return `<options>${nodes.join("")}</options>`;
|
|
2501
|
+
};
|
|
2415
2502
|
var sourceBuildTypeToXml = (source) => {
|
|
2416
2503
|
if (!source || typeof source !== "object") {
|
|
2417
2504
|
return void 0;
|
|
@@ -2458,6 +2545,10 @@ var dependencyToXml = (dependencyType, payload) => {
|
|
|
2458
2545
|
if (propertiesXml) {
|
|
2459
2546
|
fragments.push(propertiesXml);
|
|
2460
2547
|
}
|
|
2548
|
+
const optionsXml = optionsToXml(payload.options);
|
|
2549
|
+
if (optionsXml) {
|
|
2550
|
+
fragments.push(optionsXml);
|
|
2551
|
+
}
|
|
2461
2552
|
return `<${root}${attributesToString(attributes)}>${fragments.join("")}</${root}>`;
|
|
2462
2553
|
};
|
|
2463
2554
|
var prepareArtifactRequest = (payload) => ({
|
|
@@ -2572,7 +2663,7 @@ var BuildDependencyManager = class {
|
|
|
2572
2663
|
const response = await this.client.modules.buildTypes.getSnapshotDependency(
|
|
2573
2664
|
buildTypeId,
|
|
2574
2665
|
dependencyId,
|
|
2575
|
-
"id,type,disabled,properties(property(name,value)),'source-buildType'(id)",
|
|
2666
|
+
"id,type,disabled,properties(property(name,value)),options(option(name,value)),'source-buildType'(id)",
|
|
2576
2667
|
JSON_GET_HEADERS2
|
|
2577
2668
|
);
|
|
2578
2669
|
return response.data;
|
|
@@ -2584,9 +2675,42 @@ var BuildDependencyManager = class {
|
|
|
2584
2675
|
}
|
|
2585
2676
|
}
|
|
2586
2677
|
buildPayload(dependencyType, existing, input) {
|
|
2587
|
-
const
|
|
2588
|
-
const
|
|
2678
|
+
const existingSnapshot = existing;
|
|
2679
|
+
const baseProperties = propertiesToRecord2(existing?.properties);
|
|
2680
|
+
const inputPropertyRecord = toStringRecord2(input.properties);
|
|
2681
|
+
const inputExplicitOptions = toStringRecord2(input.options);
|
|
2682
|
+
let optionOverrides = {};
|
|
2683
|
+
let propertyOverrides = inputPropertyRecord;
|
|
2684
|
+
let baseOptions = {};
|
|
2685
|
+
if (dependencyType === "snapshot") {
|
|
2686
|
+
baseOptions = optionsToRecord(existingSnapshot?.options);
|
|
2687
|
+
const knownOptionKeys = /* @__PURE__ */ new Set([
|
|
2688
|
+
...Object.keys(baseOptions),
|
|
2689
|
+
...Object.keys(inputExplicitOptions)
|
|
2690
|
+
]);
|
|
2691
|
+
for (const key of SNAPSHOT_DEPENDENCY_OPTION_KEYS) {
|
|
2692
|
+
knownOptionKeys.add(key);
|
|
2693
|
+
}
|
|
2694
|
+
const derivedOptionOverrides = { ...inputExplicitOptions };
|
|
2695
|
+
const derivedPropertyOverrides = {};
|
|
2696
|
+
for (const [key, value] of Object.entries(inputPropertyRecord)) {
|
|
2697
|
+
if (knownOptionKeys.has(key)) {
|
|
2698
|
+
derivedOptionOverrides[key] = value;
|
|
2699
|
+
} else {
|
|
2700
|
+
derivedPropertyOverrides[key] = value;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
optionOverrides = derivedOptionOverrides;
|
|
2704
|
+
propertyOverrides = derivedPropertyOverrides;
|
|
2705
|
+
} else if (Object.keys(inputExplicitOptions).length > 0) {
|
|
2706
|
+
optionOverrides = inputExplicitOptions;
|
|
2707
|
+
}
|
|
2708
|
+
const mergedProps = mergeRecords2(baseProperties, propertyOverrides);
|
|
2589
2709
|
const properties = recordToProperties2(mergedProps);
|
|
2710
|
+
let mergedOptions = {};
|
|
2711
|
+
if (dependencyType === "snapshot") {
|
|
2712
|
+
mergedOptions = mergeRecords2(baseOptions, optionOverrides);
|
|
2713
|
+
}
|
|
2590
2714
|
const resolvedType = input.type ?? existing?.type ?? defaultTypeFor(dependencyType);
|
|
2591
2715
|
const payload = {
|
|
2592
2716
|
...existing ?? {},
|
|
@@ -2597,6 +2721,16 @@ var BuildDependencyManager = class {
|
|
|
2597
2721
|
}
|
|
2598
2722
|
if (properties) {
|
|
2599
2723
|
payload.properties = properties;
|
|
2724
|
+
} else {
|
|
2725
|
+
delete payload.properties;
|
|
2726
|
+
}
|
|
2727
|
+
if (dependencyType === "snapshot") {
|
|
2728
|
+
const options = recordToOptions(mergedOptions);
|
|
2729
|
+
if (options) {
|
|
2730
|
+
payload.options = options;
|
|
2731
|
+
} else {
|
|
2732
|
+
delete payload.options;
|
|
2733
|
+
}
|
|
2600
2734
|
}
|
|
2601
2735
|
const dependsOn = input.dependsOn ?? existing?.["source-buildType"]?.id;
|
|
2602
2736
|
if (dependsOn) {
|
|
@@ -41535,6 +41669,10 @@ var FULL_MODE_TOOLS = [
|
|
|
41535
41669
|
type: "object",
|
|
41536
41670
|
description: "Dependency properties (e.g. cleanDestinationDirectory, pathRules)"
|
|
41537
41671
|
},
|
|
41672
|
+
options: {
|
|
41673
|
+
type: "object",
|
|
41674
|
+
description: "Snapshot dependency options (e.g. run-build-on-the-same-agent)"
|
|
41675
|
+
},
|
|
41538
41676
|
type: {
|
|
41539
41677
|
type: "string",
|
|
41540
41678
|
description: "Override dependency type value sent to TeamCity"
|
|
@@ -41552,6 +41690,7 @@ var FULL_MODE_TOOLS = [
|
|
|
41552
41690
|
dependencyId: import_zod4.z.string().min(1).optional(),
|
|
41553
41691
|
dependsOn: import_zod4.z.string().min(1).optional(),
|
|
41554
41692
|
properties: import_zod4.z.record(import_zod4.z.string(), propertyValue).optional(),
|
|
41693
|
+
options: import_zod4.z.record(import_zod4.z.string(), propertyValue).optional(),
|
|
41555
41694
|
type: import_zod4.z.string().min(1).optional(),
|
|
41556
41695
|
disabled: import_zod4.z.boolean().optional()
|
|
41557
41696
|
}).superRefine((value, ctx) => {
|
|
@@ -41583,6 +41722,7 @@ var FULL_MODE_TOOLS = [
|
|
|
41583
41722
|
dependencyType: typed.dependencyType,
|
|
41584
41723
|
dependsOn: typed.dependsOn,
|
|
41585
41724
|
properties: typed.properties,
|
|
41725
|
+
options: typed.options,
|
|
41586
41726
|
type: typed.type,
|
|
41587
41727
|
disabled: typed.disabled
|
|
41588
41728
|
});
|
|
@@ -41601,6 +41741,7 @@ var FULL_MODE_TOOLS = [
|
|
|
41601
41741
|
dependencyType: typed.dependencyType,
|
|
41602
41742
|
dependsOn: typed.dependsOn,
|
|
41603
41743
|
properties: typed.properties,
|
|
41744
|
+
options: typed.options,
|
|
41604
41745
|
type: typed.type,
|
|
41605
41746
|
disabled: typed.disabled
|
|
41606
41747
|
});
|
|
@@ -43017,6 +43158,34 @@ function createSimpleServer() {
|
|
|
43017
43158
|
|
|
43018
43159
|
// src/index.ts
|
|
43019
43160
|
dotenv2.config();
|
|
43161
|
+
var activeServer = null;
|
|
43162
|
+
var lifecyclePromise = null;
|
|
43163
|
+
var shuttingDown = false;
|
|
43164
|
+
var clearServerState = () => {
|
|
43165
|
+
activeServer = null;
|
|
43166
|
+
lifecyclePromise = null;
|
|
43167
|
+
};
|
|
43168
|
+
async function shutdown(exitCode) {
|
|
43169
|
+
if (shuttingDown) {
|
|
43170
|
+
process.exit(exitCode);
|
|
43171
|
+
}
|
|
43172
|
+
shuttingDown = true;
|
|
43173
|
+
process.stderr.write("\nShutting down TeamCity MCP Server...\n");
|
|
43174
|
+
try {
|
|
43175
|
+
const serverToClose = activeServer;
|
|
43176
|
+
const lifecycleToAwait = lifecyclePromise;
|
|
43177
|
+
await serverToClose?.close();
|
|
43178
|
+
await lifecycleToAwait;
|
|
43179
|
+
} catch (error2) {
|
|
43180
|
+
process.stderr.write(
|
|
43181
|
+
`Error while closing server: ${error2 instanceof Error ? error2.message : String(error2)}
|
|
43182
|
+
`
|
|
43183
|
+
);
|
|
43184
|
+
} finally {
|
|
43185
|
+
clearServerState();
|
|
43186
|
+
process.exit(exitCode);
|
|
43187
|
+
}
|
|
43188
|
+
}
|
|
43020
43189
|
async function main() {
|
|
43021
43190
|
try {
|
|
43022
43191
|
let hasUrl;
|
|
@@ -43036,21 +43205,25 @@ async function main() {
|
|
|
43036
43205
|
`);
|
|
43037
43206
|
const server = createSimpleServer();
|
|
43038
43207
|
const transport = new import_stdio.StdioServerTransport();
|
|
43039
|
-
|
|
43208
|
+
const lifecycle = startServerLifecycle(server, transport);
|
|
43209
|
+
activeServer = server;
|
|
43210
|
+
lifecyclePromise = lifecycle;
|
|
43040
43211
|
process.stderr.write("TeamCity MCP Server is running and ready to accept connections\n");
|
|
43212
|
+
await lifecycle;
|
|
43213
|
+
clearServerState();
|
|
43214
|
+
process.stderr.write("TeamCity MCP Server connection closed\n");
|
|
43041
43215
|
} catch (error2) {
|
|
43216
|
+
clearServerState();
|
|
43042
43217
|
process.stderr.write(`Failed to start server: ${error2}
|
|
43043
43218
|
`);
|
|
43044
43219
|
process.exit(1);
|
|
43045
43220
|
}
|
|
43046
43221
|
}
|
|
43047
43222
|
process.on("SIGINT", () => {
|
|
43048
|
-
|
|
43049
|
-
process.exit(0);
|
|
43223
|
+
void shutdown(0);
|
|
43050
43224
|
});
|
|
43051
43225
|
process.on("SIGTERM", () => {
|
|
43052
|
-
|
|
43053
|
-
process.exit(0);
|
|
43226
|
+
void shutdown(0);
|
|
43054
43227
|
});
|
|
43055
43228
|
main().catch((error2) => {
|
|
43056
43229
|
process.stderr.write(`Unhandled error: ${error2}
|