@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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.10.7"
2
+ ".": "1.10.9"
3
3
  }
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 baseProps = propertiesToRecord2(existing?.properties);
2588
- const mergedProps = mergeRecords2(baseProps, toStringRecord2(input.properties));
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
- await server.connect(transport);
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
- process.stderr.write("\nShutting down TeamCity MCP Server...\n");
43049
- process.exit(0);
43223
+ void shutdown(0);
43050
43224
  });
43051
43225
  process.on("SIGTERM", () => {
43052
- process.stderr.write("\nShutting down TeamCity MCP Server...\n");
43053
- process.exit(0);
43226
+ void shutdown(0);
43054
43227
  });
43055
43228
  main().catch((error2) => {
43056
43229
  process.stderr.write(`Unhandled error: ${error2}