@daghis/teamcity-mcp 2.11.0 → 2.12.1
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/CHANGELOG.md +14 -0
- package/README.md +35 -2
- package/dist/index.js +42 -9
- package/package.json +2 -2
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.12.1](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v2.12.0...teamcity-mcp-v2.12.1) (2026-05-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **tools:** mark download_* as readOnly and idempotent ([#507](https://github.com/Daghis/teamcity-mcp/issues/507)) ([0a09663](https://github.com/Daghis/teamcity-mcp/commit/0a09663a5ff39e82f9b471e1aa7102ffc4d68163))
|
|
9
|
+
|
|
10
|
+
## [2.12.0](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v2.11.0...teamcity-mcp-v2.12.0) (2026-05-03)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **http:** forward TEAMCITY_HEADER_* env vars on every request ([#492](https://github.com/Daghis/teamcity-mcp/issues/492)) ([c406511](https://github.com/Daghis/teamcity-mcp/commit/c4065111e2e8c6e807ec9a4383cbbe8d280ca00e))
|
|
16
|
+
|
|
3
17
|
## [2.11.0](https://github.com/Daghis/teamcity-mcp/compare/teamcity-mcp-v2.10.0...teamcity-mcp-v2.11.0) (2026-05-03)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -47,6 +47,24 @@ See the [Tools Mode Matrix](docs/mcp-tools-mode-matrix.md) for the complete list
|
|
|
47
47
|
- Performance-conscious: fast startup with minimal overhead
|
|
48
48
|
- Clean codebase with clear module boundaries
|
|
49
49
|
|
|
50
|
+
## Choosing between teamcity-mcp and the built-in MCP
|
|
51
|
+
|
|
52
|
+
TeamCity 2026.1 ships with a built-in MCP endpoint at `<server-url>/app/mcp` exposing three tools: build log retrieval, a generic REST GET, and a build trigger (forced to `personal=true`). It is server-resident, requires no install, and is a sensible default for read-and-rerun workflows.
|
|
53
|
+
|
|
54
|
+
teamcity-mcp is a different shape: an 87-tool typed surface focused on AI-driven workflows that need writes, multi-server support, or pre-2026.1 compatibility.
|
|
55
|
+
|
|
56
|
+
| Use case | Recommendation |
|
|
57
|
+
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
58
|
+
| Read build logs, trigger builds, simple read flows | Built-in (TeamCity 2026.1+) — zero install |
|
|
59
|
+
| Manage parameters, agents, queue, mutes, build configs, VCS roots | teamcity-mcp |
|
|
60
|
+
| TeamCity server older than 2026.1 | teamcity-mcp |
|
|
61
|
+
| Multi-server / multi-tenant deployment | teamcity-mcp (HTTP transport — [PR #491](https://github.com/Daghis/teamcity-mcp/pull/491)) |
|
|
62
|
+
| Typed tool surface for better agent reliability on chained operations | teamcity-mcp |
|
|
63
|
+
|
|
64
|
+
Both can coexist. The built-in is a good first stop; teamcity-mcp is the power tool for everything the built-in doesn't reach.
|
|
65
|
+
|
|
66
|
+
For the design reasoning, see [docs/strategy.md](docs/strategy.md) and [docs/non-goals.md](docs/non-goals.md).
|
|
67
|
+
|
|
50
68
|
## Installation
|
|
51
69
|
|
|
52
70
|
### Prerequisites
|
|
@@ -110,7 +128,14 @@ On Windows, Claude Code's MCP configuration [may not properly merge environment
|
|
|
110
128
|
"mcpServers": {
|
|
111
129
|
"teamcity": {
|
|
112
130
|
"command": "npx",
|
|
113
|
-
"args": [
|
|
131
|
+
"args": [
|
|
132
|
+
"-y",
|
|
133
|
+
"@daghis/teamcity-mcp",
|
|
134
|
+
"--url",
|
|
135
|
+
"https://teamcity.example.com",
|
|
136
|
+
"--token",
|
|
137
|
+
"YOUR_TOKEN"
|
|
138
|
+
]
|
|
114
139
|
}
|
|
115
140
|
}
|
|
116
141
|
}
|
|
@@ -156,6 +181,14 @@ MCP_MODE=dev
|
|
|
156
181
|
# TEAMCITY_KEEP_ALIVE=true
|
|
157
182
|
# TEAMCITY_COMPRESSION=true
|
|
158
183
|
|
|
184
|
+
# Extra headers attached to every TeamCity request — useful when TeamCity
|
|
185
|
+
# sits behind a reverse proxy that gates access on custom headers (e.g.
|
|
186
|
+
# Cloudflare Zero Trust service tokens). One env var per header; the part
|
|
187
|
+
# after `TEAMCITY_HEADER_` is used verbatim as the HTTP header name.
|
|
188
|
+
# Example (note the literal hyphens — most shells need quoting):
|
|
189
|
+
# TEAMCITY_HEADER_CF-Access-Client-Id=<id>
|
|
190
|
+
# TEAMCITY_HEADER_CF-Access-Client-Secret=<secret>
|
|
191
|
+
|
|
159
192
|
# Retry
|
|
160
193
|
# TEAMCITY_RETRY_ENABLED=true
|
|
161
194
|
# TEAMCITY_MAX_RETRIES=3
|
|
@@ -351,4 +384,4 @@ This repository has GitHub secret scanning and push protection enabled. See [SEC
|
|
|
351
384
|
|
|
352
385
|
---
|
|
353
386
|
|
|
354
|
-
Built with ❤️ for developers who love efficient CI/CD workflows
|
|
387
|
+
Built with ❤️ for developers who love efficient CI/CD workflows
|
package/dist/index.js
CHANGED
|
@@ -691,6 +691,17 @@ function setServerInstance(server) {
|
|
|
691
691
|
function getServerInstance() {
|
|
692
692
|
return serverInstance;
|
|
693
693
|
}
|
|
694
|
+
var HEADER_ENV_PREFIX = "TEAMCITY_HEADER_";
|
|
695
|
+
function getTeamCityExtraHeaders() {
|
|
696
|
+
const headers = {};
|
|
697
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
698
|
+
if (!key.startsWith(HEADER_ENV_PREFIX) || value === void 0) continue;
|
|
699
|
+
const headerName = key.slice(HEADER_ENV_PREFIX.length);
|
|
700
|
+
if (headerName === "") continue;
|
|
701
|
+
headers[headerName] = value;
|
|
702
|
+
}
|
|
703
|
+
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
704
|
+
}
|
|
694
705
|
function getTeamCityUrl() {
|
|
695
706
|
const config2 = getConfig();
|
|
696
707
|
if (!config2.teamcity?.url || config2.teamcity.url.length === 0) {
|
|
@@ -1205,7 +1216,7 @@ function debug2(message, meta) {
|
|
|
1205
1216
|
// package.json
|
|
1206
1217
|
var package_default = {
|
|
1207
1218
|
name: "@daghis/teamcity-mcp",
|
|
1208
|
-
version: "2.
|
|
1219
|
+
version: "2.12.1",
|
|
1209
1220
|
description: "Model Control Protocol server for TeamCity CI/CD integration with AI coding assistants",
|
|
1210
1221
|
mcpName: "io.github.Daghis/teamcity",
|
|
1211
1222
|
main: "dist/index.js",
|
|
@@ -1269,7 +1280,7 @@ var package_default = {
|
|
|
1269
1280
|
url: "https://github.com/Daghis/teamcity-mcp/issues"
|
|
1270
1281
|
},
|
|
1271
1282
|
dependencies: {
|
|
1272
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
1283
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
1273
1284
|
axios: "^1.15.0",
|
|
1274
1285
|
"axios-retry": "^4.5.0",
|
|
1275
1286
|
dotenv: "17.4.2",
|
|
@@ -38459,6 +38470,7 @@ var TeamCityAPI = class _TeamCityAPI {
|
|
|
38459
38470
|
baseURL: basePath,
|
|
38460
38471
|
timeout,
|
|
38461
38472
|
headers: {
|
|
38473
|
+
...config2.extraHeaders ?? {},
|
|
38462
38474
|
Authorization: `Bearer ${config2.token}`,
|
|
38463
38475
|
Accept: "application/json",
|
|
38464
38476
|
"Content-Type": "application/json"
|
|
@@ -38487,6 +38499,7 @@ var TeamCityAPI = class _TeamCityAPI {
|
|
|
38487
38499
|
baseOptions: {
|
|
38488
38500
|
timeout,
|
|
38489
38501
|
headers: {
|
|
38502
|
+
...config2.extraHeaders ?? {},
|
|
38490
38503
|
Authorization: `Bearer ${config2.token}`,
|
|
38491
38504
|
Accept: "application/json"
|
|
38492
38505
|
}
|
|
@@ -38569,7 +38582,8 @@ var TeamCityAPI = class _TeamCityAPI {
|
|
|
38569
38582
|
if (this.instance == null) {
|
|
38570
38583
|
const envConfig = this.normalizeConfig({
|
|
38571
38584
|
baseUrl: getTeamCityUrl(),
|
|
38572
|
-
token: getTeamCityToken()
|
|
38585
|
+
token: getTeamCityToken(),
|
|
38586
|
+
extraHeaders: getTeamCityExtraHeaders()
|
|
38573
38587
|
});
|
|
38574
38588
|
this.instance = new _TeamCityAPI(envConfig);
|
|
38575
38589
|
this.instanceConfig = envConfig;
|
|
@@ -38800,15 +38814,34 @@ var TeamCityAPI = class _TeamCityAPI {
|
|
|
38800
38814
|
return {
|
|
38801
38815
|
baseUrl: config2.baseUrl.replace(/\/$/, ""),
|
|
38802
38816
|
token: config2.token,
|
|
38803
|
-
timeout: config2.timeout
|
|
38817
|
+
timeout: config2.timeout,
|
|
38818
|
+
extraHeaders: normalizeExtraHeaders(config2.extraHeaders)
|
|
38804
38819
|
};
|
|
38805
38820
|
}
|
|
38806
38821
|
static configsEqual(a, b) {
|
|
38807
38822
|
if (a == null || b == null) {
|
|
38808
38823
|
return false;
|
|
38809
38824
|
}
|
|
38810
|
-
return a.baseUrl === b.baseUrl && a.token === b.token && a.timeout === b.timeout;
|
|
38825
|
+
return a.baseUrl === b.baseUrl && a.token === b.token && a.timeout === b.timeout && extraHeadersEqual(a.extraHeaders, b.extraHeaders);
|
|
38826
|
+
}
|
|
38827
|
+
};
|
|
38828
|
+
var normalizeExtraHeaders = (headers) => {
|
|
38829
|
+
if (headers == null) {
|
|
38830
|
+
return void 0;
|
|
38811
38831
|
}
|
|
38832
|
+
const entries = Object.entries(headers);
|
|
38833
|
+
if (entries.length === 0) {
|
|
38834
|
+
return void 0;
|
|
38835
|
+
}
|
|
38836
|
+
return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b)));
|
|
38837
|
+
};
|
|
38838
|
+
var extraHeadersEqual = (a, b) => {
|
|
38839
|
+
if (a === b) return true;
|
|
38840
|
+
if (a == null || b == null) return a == null && b == null;
|
|
38841
|
+
const keysA = Object.keys(a);
|
|
38842
|
+
const keysB = Object.keys(b);
|
|
38843
|
+
if (keysA.length !== keysB.length) return false;
|
|
38844
|
+
return keysA.every((key) => a[key] === b[key]);
|
|
38812
38845
|
};
|
|
38813
38846
|
|
|
38814
38847
|
// src/tools.ts
|
|
@@ -41511,9 +41544,9 @@ var DEV_TOOLS = [
|
|
|
41511
41544
|
{
|
|
41512
41545
|
name: "download_build_artifact",
|
|
41513
41546
|
annotations: {
|
|
41514
|
-
readOnlyHint:
|
|
41547
|
+
readOnlyHint: true,
|
|
41515
41548
|
destructiveHint: false,
|
|
41516
|
-
idempotentHint:
|
|
41549
|
+
idempotentHint: true,
|
|
41517
41550
|
openWorldHint: true
|
|
41518
41551
|
},
|
|
41519
41552
|
description: "Download a single build artifact, with base64, text, or streaming output. Returns the artifact bytes or stream metadata; returns 404 if the build or path is unknown.",
|
|
@@ -41579,9 +41612,9 @@ var DEV_TOOLS = [
|
|
|
41579
41612
|
{
|
|
41580
41613
|
name: "download_build_artifacts",
|
|
41581
41614
|
annotations: {
|
|
41582
|
-
readOnlyHint:
|
|
41615
|
+
readOnlyHint: true,
|
|
41583
41616
|
destructiveHint: false,
|
|
41584
|
-
idempotentHint:
|
|
41617
|
+
idempotentHint: true,
|
|
41585
41618
|
openWorldHint: true
|
|
41586
41619
|
},
|
|
41587
41620
|
description: "Download multiple build artifacts, with base64, text, or streaming output. Returns per-artifact payloads or stream metadata; returns 404 if the build or any path is unknown.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daghis/teamcity-mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.1",
|
|
4
4
|
"description": "Model Control Protocol server for TeamCity CI/CD integration with AI coding assistants",
|
|
5
5
|
"mcpName": "io.github.Daghis/teamcity",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"url": "https://github.com/Daghis/teamcity-mcp/issues"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
67
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
68
68
|
"axios": "^1.15.0",
|
|
69
69
|
"axios-retry": "^4.5.0",
|
|
70
70
|
"dotenv": "17.4.2",
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://github.com/Daghis/teamcity-mcp",
|
|
10
|
-
"version": "2.
|
|
10
|
+
"version": "2.12.1",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@daghis/teamcity-mcp",
|
|
16
|
-
"version": "2.
|
|
16
|
+
"version": "2.12.1",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"runtimeArguments": [
|
|
19
19
|
{
|