@daghis/teamcity-mcp 1.11.13 → 1.11.14

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.11.13"
2
+ ".": "1.11.14"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.11.14](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.11.13...teamcity-mcp-v1.11.14) (2025-12-06)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * Convert agent requirements and artifact dependencies to use XML format ([#294](https://github.com/Daghis/teamcity-mcp/issues/294)) ([a9bd9b7](https://github.com/Daghis/teamcity-mcp/commit/a9bd9b7a5710e03a0b8d2f2f35e703fc08aa5e85))
9
+
3
10
  ## [1.11.13](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v1.11.12...teamcity-mcp-v1.11.13) (2025-12-06)
4
11
 
5
12
 
package/CODEOWNERS ADDED
@@ -0,0 +1,2 @@
1
+ # Default owner for everything in the repo
2
+ * @Daghis
@@ -0,0 +1,128 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ * Demonstrating empathy and kindness toward other people
21
+ * Being respectful of differing opinions, viewpoints, and experiences
22
+ * Giving and gracefully accepting constructive feedback
23
+ * Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ * Focusing on what is best not just for us as individuals, but for the
26
+ overall community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ * The use of sexualized language or imagery, and sexual attention or
31
+ advances of any kind
32
+ * Trolling, insulting or derogatory comments, and personal or political attacks
33
+ * Public or private harassment
34
+ * Publishing others' private information, such as a physical or email
35
+ address, without their explicit permission
36
+ * Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official e-mail address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ @Daghis.
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series
86
+ of actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or
93
+ permanent ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within
113
+ the community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.0, available at
119
+ https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120
+
121
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct
122
+ enforcement ladder](https://github.com/mozilla/diversity).
123
+
124
+ [homepage]: https://www.contributor-covenant.org
125
+
126
+ For answers to common questions about this code of conduct, see the FAQ at
127
+ https://www.contributor-covenant.org/faq. Translations are available at
128
+ https://www.contributor-covenant.org/translations.
package/SECURITY.md ADDED
@@ -0,0 +1,40 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ If you discover a security vulnerability in this project, please report it responsibly:
6
+
7
+ 1. **Do NOT open a public GitHub issue** for security vulnerabilities.
8
+
9
+ 2. **Use GitHub Security Advisories**: Go to the [Security tab](https://github.com/Daghis/teamcity-mcp/security/advisories) and click "Report a
10
+ vulnerability".
11
+
12
+ 3. **Email**: Alternatively, you can email security concerns to the maintainers directly.
13
+
14
+ ## Response Timeline
15
+
16
+ - We aim to acknowledge receipt within 48 hours.
17
+ - We will provide an initial assessment within 7 days.
18
+ - We will work with you to understand and resolve the issue promptly.
19
+
20
+ ## Supported Versions
21
+
22
+ | Version | Supported |
23
+ | ------- | ------------------ |
24
+ | Latest | :white_check_mark: |
25
+ | < 1.0 | :x: |
26
+
27
+ ## Security Measures
28
+
29
+ This repository has the following security measures enabled:
30
+
31
+ - **Secret scanning**: Automatically detects accidentally committed secrets
32
+ - **Push protection**: Blocks pushes containing secrets
33
+ - **Dependabot**: Monitors dependencies for known vulnerabilities
34
+
35
+ ## Best Practices for Users
36
+
37
+ - Never commit your `TEAMCITY_TOKEN` or other credentials to version control
38
+ - Use environment variables or secure secret management for sensitive configuration
39
+ - Regularly rotate your TeamCity access tokens
40
+ - Use tokens with minimal required permissions
package/codecov.yml ADDED
@@ -0,0 +1,15 @@
1
+ coverage:
2
+ status:
3
+ project:
4
+ default:
5
+ target: 80%
6
+ threshold: 2%
7
+ patch:
8
+ default:
9
+ target: 80%
10
+
11
+ comment:
12
+ layout: "reach,diff,flags,files"
13
+ behavior: default
14
+ require_changes: true
15
+ hide_project_coverage: false
package/dist/index.js CHANGED
@@ -954,7 +954,7 @@ function debug2(message, meta) {
954
954
  // package.json
955
955
  var package_default = {
956
956
  name: "@daghis/teamcity-mcp",
957
- version: "1.11.13",
957
+ version: "1.11.14",
958
958
  description: "Model Control Protocol server for TeamCity CI/CD integration with AI coding assistants",
959
959
  mcpName: "io.github.Daghis/teamcity",
960
960
  main: "dist/index.js",
@@ -1013,6 +1013,10 @@ var package_default = {
1013
1013
  type: "git",
1014
1014
  url: "git+https://github.com/Daghis/teamcity-mcp.git"
1015
1015
  },
1016
+ homepage: "https://github.com/Daghis/teamcity-mcp#readme",
1017
+ bugs: {
1018
+ url: "https://github.com/Daghis/teamcity-mcp/issues"
1019
+ },
1016
1020
  dependencies: {
1017
1021
  "@modelcontextprotocol/sdk": "^1.21.0",
1018
1022
  ajv: "^8.17.1",
@@ -1078,9 +1082,9 @@ var ResolutionTypeEnum = {
1078
1082
  };
1079
1083
 
1080
1084
  // src/teamcity/agent-requirements-manager.ts
1081
- var JSON_HEADERS = {
1085
+ var XML_HEADERS = {
1082
1086
  headers: {
1083
- "Content-Type": "application/json",
1087
+ "Content-Type": "application/xml",
1084
1088
  Accept: "application/json"
1085
1089
  }
1086
1090
  };
@@ -1133,6 +1137,45 @@ var mergeRecords = (base, override) => ({
1133
1137
  ...base,
1134
1138
  ...override
1135
1139
  });
1140
+ var escapeXml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1141
+ var attributesToString = (attributes) => {
1142
+ const parts = Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}="${escapeXml(value)}"`);
1143
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
1144
+ };
1145
+ var propertiesToXml = (properties) => {
1146
+ if (!properties) {
1147
+ return void 0;
1148
+ }
1149
+ const entries = properties.property;
1150
+ const list = Array.isArray(entries) ? entries : entries != null ? [entries] : [];
1151
+ if (list.length === 0) {
1152
+ return void 0;
1153
+ }
1154
+ const nodes = list.filter((item) => item?.name).map((item) => {
1155
+ const name = item?.name ?? "";
1156
+ const value = item?.value != null ? String(item.value) : "";
1157
+ return `<property name="${escapeXml(name)}" value="${escapeXml(value)}"/>`;
1158
+ });
1159
+ if (nodes.length === 0) {
1160
+ return void 0;
1161
+ }
1162
+ return `<properties>${nodes.join("")}</properties>`;
1163
+ };
1164
+ var agentRequirementToXml = (requirement) => {
1165
+ const attributes = {
1166
+ id: typeof requirement.id === "string" && requirement.id.trim() !== "" ? requirement.id : void 0,
1167
+ name: typeof requirement.name === "string" && requirement.name.trim() !== "" ? requirement.name : void 0,
1168
+ type: typeof requirement.type === "string" && requirement.type.trim() !== "" ? requirement.type : void 0,
1169
+ disabled: typeof requirement.disabled === "boolean" ? requirement.disabled ? "true" : "false" : void 0,
1170
+ inherited: typeof requirement.inherited === "boolean" ? requirement.inherited ? "true" : "false" : void 0
1171
+ };
1172
+ const fragments = [];
1173
+ const propertiesXml = propertiesToXml(requirement.properties);
1174
+ if (propertiesXml) {
1175
+ fragments.push(propertiesXml);
1176
+ }
1177
+ return `<agent-requirement${attributesToString(attributes)}>${fragments.join("")}</agent-requirement>`;
1178
+ };
1136
1179
  var AgentRequirementsManager = class {
1137
1180
  constructor(client) {
1138
1181
  this.client = client;
@@ -1140,11 +1183,12 @@ var AgentRequirementsManager = class {
1140
1183
  async addRequirement(input) {
1141
1184
  const { buildTypeId } = input;
1142
1185
  const payload = this.buildPayload(void 0, input);
1186
+ const xmlBody = agentRequirementToXml(payload);
1143
1187
  const response = await this.client.modules.buildTypes.addAgentRequirementToBuildType(
1144
1188
  buildTypeId,
1145
1189
  void 0,
1146
- payload,
1147
- JSON_HEADERS
1190
+ xmlBody,
1191
+ XML_HEADERS
1148
1192
  );
1149
1193
  const id = response.data?.id;
1150
1194
  if (!id) {
@@ -1161,12 +1205,13 @@ var AgentRequirementsManager = class {
1161
1205
  );
1162
1206
  }
1163
1207
  const payload = this.buildPayload(existing, input);
1208
+ const xmlBody = agentRequirementToXml(payload);
1164
1209
  await this.client.modules.buildTypes.replaceAgentRequirement(
1165
1210
  buildTypeId,
1166
1211
  requirementId,
1167
1212
  void 0,
1168
- payload,
1169
- JSON_HEADERS
1213
+ xmlBody,
1214
+ XML_HEADERS
1170
1215
  );
1171
1216
  return { id: requirementId };
1172
1217
  }
@@ -1174,7 +1219,7 @@ var AgentRequirementsManager = class {
1174
1219
  await this.client.modules.buildTypes.deleteAgentRequirement(
1175
1220
  buildTypeId,
1176
1221
  requirementId,
1177
- JSON_HEADERS
1222
+ JSON_GET_HEADERS
1178
1223
  );
1179
1224
  }
1180
1225
  async fetchRequirement(buildTypeId, requirementId) {
@@ -2464,13 +2509,7 @@ var BuildConfigurationUpdateManager = class {
2464
2509
  };
2465
2510
 
2466
2511
  // src/teamcity/build-dependency-manager.ts
2467
- var JSON_HEADERS2 = {
2468
- headers: {
2469
- "Content-Type": "application/json",
2470
- Accept: "application/json"
2471
- }
2472
- };
2473
- var XML_HEADERS = {
2512
+ var XML_HEADERS2 = {
2474
2513
  headers: {
2475
2514
  "Content-Type": "application/xml",
2476
2515
  Accept: "application/json"
@@ -2568,12 +2607,12 @@ var mergeRecords2 = (base, override) => {
2568
2607
  }
2569
2608
  return merged;
2570
2609
  };
2571
- var escapeXml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2572
- var attributesToString = (attributes) => {
2573
- const parts = Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}="${escapeXml(value)}"`);
2610
+ var escapeXml2 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2611
+ var attributesToString2 = (attributes) => {
2612
+ const parts = Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}="${escapeXml2(value)}"`);
2574
2613
  return parts.length > 0 ? ` ${parts.join(" ")}` : "";
2575
2614
  };
2576
- var propertiesToXml = (properties) => {
2615
+ var propertiesToXml2 = (properties) => {
2577
2616
  if (!properties) {
2578
2617
  return void 0;
2579
2618
  }
@@ -2585,7 +2624,7 @@ var propertiesToXml = (properties) => {
2585
2624
  const nodes = list.filter((item) => item?.name).map((item) => {
2586
2625
  const name = item?.name ?? "";
2587
2626
  const value = item?.value != null ? String(item.value) : "";
2588
- return `<property name="${escapeXml(name)}" value="${escapeXml(value)}"/>`;
2627
+ return `<property name="${escapeXml2(name)}" value="${escapeXml2(value)}"/>`;
2589
2628
  });
2590
2629
  if (nodes.length === 0) {
2591
2630
  return void 0;
@@ -2604,7 +2643,7 @@ var optionsToXml = (options) => {
2604
2643
  const nodes = list.filter((item) => item?.name).map((item) => {
2605
2644
  const name = item?.name ?? "";
2606
2645
  const value = item?.value != null ? String(item.value) : "";
2607
- return `<option name="${escapeXml(name)}" value="${escapeXml(value)}"/>`;
2646
+ return `<option name="${escapeXml2(name)}" value="${escapeXml2(value)}"/>`;
2608
2647
  });
2609
2648
  if (nodes.length === 0) {
2610
2649
  return void 0;
@@ -2623,7 +2662,7 @@ var sourceBuildTypeToXml = (source) => {
2623
2662
  id,
2624
2663
  name
2625
2664
  };
2626
- return `<source-buildType${attributesToString(attributes)}/>`;
2665
+ return `<source-buildType${attributesToString2(attributes)}/>`;
2627
2666
  };
2628
2667
  var dependencyToXml = (dependencyType, payload) => {
2629
2668
  const root = dependencyType === "artifact" ? "artifact-dependency" : "snapshot-dependency";
@@ -2653,7 +2692,7 @@ var dependencyToXml = (dependencyType, payload) => {
2653
2692
  if (sourceBuildTypeXml) {
2654
2693
  fragments.push(sourceBuildTypeXml);
2655
2694
  }
2656
- const propertiesXml = propertiesToXml(payload.properties);
2695
+ const propertiesXml = propertiesToXml2(payload.properties);
2657
2696
  if (propertiesXml) {
2658
2697
  fragments.push(propertiesXml);
2659
2698
  }
@@ -2661,15 +2700,15 @@ var dependencyToXml = (dependencyType, payload) => {
2661
2700
  if (optionsXml) {
2662
2701
  fragments.push(optionsXml);
2663
2702
  }
2664
- return `<${root}${attributesToString(attributes)}>${fragments.join("")}</${root}>`;
2703
+ return `<${root}${attributesToString2(attributes)}>${fragments.join("")}</${root}>`;
2665
2704
  };
2666
2705
  var prepareArtifactRequest = (payload) => ({
2667
- body: payload,
2668
- headers: JSON_HEADERS2
2706
+ body: dependencyToXml("artifact", payload),
2707
+ headers: XML_HEADERS2
2669
2708
  });
2670
2709
  var prepareSnapshotRequest = (payload) => ({
2671
2710
  body: dependencyToXml("snapshot", payload),
2672
- headers: XML_HEADERS
2711
+ headers: XML_HEADERS2
2673
2712
  });
2674
2713
  var BuildDependencyManager = class {
2675
2714
  constructor(client) {
@@ -2713,14 +2752,14 @@ var BuildDependencyManager = class {
2713
2752
  await this.client.modules.buildTypes.deleteArtifactDependency(
2714
2753
  buildTypeId,
2715
2754
  dependencyId,
2716
- JSON_HEADERS2
2755
+ JSON_GET_HEADERS2
2717
2756
  );
2718
2757
  return;
2719
2758
  }
2720
2759
  await this.client.modules.buildTypes.deleteSnapshotDependency(
2721
2760
  buildTypeId,
2722
2761
  dependencyId,
2723
- JSON_HEADERS2
2762
+ JSON_GET_HEADERS2
2724
2763
  );
2725
2764
  }
2726
2765
  async createDependency(dependencyType, buildTypeId, payload) {
@@ -2860,7 +2899,7 @@ var BuildDependencyManager = class {
2860
2899
  };
2861
2900
 
2862
2901
  // src/teamcity/build-feature-manager.ts
2863
- var JSON_HEADERS3 = {
2902
+ var JSON_HEADERS = {
2864
2903
  headers: {
2865
2904
  "Content-Type": "application/json",
2866
2905
  Accept: "application/json"
@@ -2930,7 +2969,7 @@ var BuildFeatureManager = class {
2930
2969
  buildTypeId,
2931
2970
  void 0,
2932
2971
  payload,
2933
- JSON_HEADERS3
2972
+ JSON_HEADERS
2934
2973
  );
2935
2974
  const id = response.data?.id;
2936
2975
  if (!id) {
@@ -2952,7 +2991,7 @@ var BuildFeatureManager = class {
2952
2991
  featureId,
2953
2992
  void 0,
2954
2993
  payload,
2955
- JSON_HEADERS3
2994
+ JSON_HEADERS
2956
2995
  );
2957
2996
  return { id: featureId };
2958
2997
  }
@@ -2960,7 +2999,7 @@ var BuildFeatureManager = class {
2960
2999
  await this.client.modules.buildTypes.deleteFeatureOfBuildType(
2961
3000
  buildTypeId,
2962
3001
  featureId,
2963
- JSON_HEADERS3
3002
+ JSON_HEADERS
2964
3003
  );
2965
3004
  }
2966
3005
  async fetchFeature(buildTypeId, featureId) {
@@ -39042,13 +39081,13 @@ var DEV_TOOLS = [
39042
39081
  buildRequest.properties = propertiesPayload;
39043
39082
  }
39044
39083
  const sendXmlFallback = async (error3) => {
39045
- const escapeXml2 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
39046
- const branchPart = branchName ? `<branchName>${escapeXml2(branchName)}</branchName>` : "";
39047
- const commentPart = commentText ? `<comment><text>${escapeXml2(commentText)}</text></comment>` : "";
39084
+ const escapeXml3 = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
39085
+ const branchPart = branchName ? `<branchName>${escapeXml3(branchName)}</branchName>` : "";
39086
+ const commentPart = commentText ? `<comment><text>${escapeXml3(commentText)}</text></comment>` : "";
39048
39087
  const propertiesPart = propertiesPayload ? `<properties>${propertiesPayload.property.map(
39049
- (prop) => `<property name="${escapeXml2(prop.name)}" value="${escapeXml2(prop.value)}"/>`
39088
+ (prop) => `<property name="${escapeXml3(prop.name)}" value="${escapeXml3(prop.value)}"/>`
39050
39089
  ).join("")}</properties>` : "";
39051
- const xml = `<?xml version="1.0" encoding="UTF-8"?><build><buildType id="${escapeXml2(
39090
+ const xml = `<?xml version="1.0" encoding="UTF-8"?><build><buildType id="${escapeXml3(
39052
39091
  typed.buildTypeId
39053
39092
  )}"/>${branchPart}${commentPart}${propertiesPart}</build>`;
39054
39093
  const response = await adapter.http.post("/app/rest/buildQueue", xml, {