@daghis/teamcity-mcp 0.2.0 → 0.2.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/.github/workflows/publish.yml +20 -0
- package/CHANGELOG.md +7 -0
- package/dist/index.js +87 -90
- package/dist/index.js.map +4 -4
- package/dist/src/middleware/global-error-handler.d.ts.map +1 -1
- package/dist/src/teamcity/auth.d.ts.map +1 -1
- package/dist/src/tools.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/middleware/global-error-handler.ts +11 -0
- package/src/teamcity/auth.ts +11 -7
- package/src/tools.ts +20 -6
- package/.commitlintrc.js +0 -24
|
@@ -39,7 +39,27 @@ jobs:
|
|
|
39
39
|
- name: Verify package contents
|
|
40
40
|
run: npm pack --dry-run
|
|
41
41
|
|
|
42
|
+
- name: Read package version
|
|
43
|
+
id: pkg
|
|
44
|
+
run: |
|
|
45
|
+
node -e "console.log('version=' + require('./package.json').version)" >> "$GITHUB_OUTPUT"
|
|
46
|
+
|
|
47
|
+
- name: Check if version already exists on npm
|
|
48
|
+
id: exists
|
|
49
|
+
run: |
|
|
50
|
+
set -e
|
|
51
|
+
PKG_NAME=$(node -p -e "require('./package.json').name")
|
|
52
|
+
VERSION=${{ steps.pkg.outputs.version }}
|
|
53
|
+
if npm view "${PKG_NAME}@${VERSION}" version > /dev/null 2>&1; then
|
|
54
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
55
|
+
echo "Version ${VERSION} already published for ${PKG_NAME}; skipping publish."
|
|
56
|
+
else
|
|
57
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
58
|
+
echo "Version ${VERSION} not found on registry; will publish."
|
|
59
|
+
fi
|
|
60
|
+
|
|
42
61
|
- name: Publish to npm
|
|
62
|
+
if: steps.exists.outputs.exists == 'false'
|
|
43
63
|
env:
|
|
44
64
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
45
65
|
NPM_CONFIG_PROVENANCE: 'true'
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.1](https://github.com/Daghis/teamcity-mcp/compare/v0.2.0...v0.2.1) (2025-09-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **mcp:** normalize TeamCity errors and wrap server info/metrics with runTool ([#53](https://github.com/Daghis/teamcity-mcp/issues/53)) ([2c8ab85](https://github.com/Daghis/teamcity-mcp/commit/2c8ab855a85e5faec4216a498c089e3a1a93ed7b))
|
|
9
|
+
|
|
3
10
|
## [0.2.0](https://github.com/Daghis/teamcity-mcp/compare/v0.1.2...v0.2.0) (2025-09-11)
|
|
4
11
|
|
|
5
12
|
|
package/dist/index.js
CHANGED
|
@@ -11652,49 +11652,8 @@ var require_follow_redirects = __commonJS({
|
|
|
11652
11652
|
}
|
|
11653
11653
|
});
|
|
11654
11654
|
|
|
11655
|
-
// node_modules/is-retry-allowed/index.js
|
|
11656
|
-
var require_is_retry_allowed = __commonJS({
|
|
11657
|
-
"node_modules/is-retry-allowed/index.js"(exports2, module2) {
|
|
11658
|
-
"use strict";
|
|
11659
|
-
var denyList = /* @__PURE__ */ new Set([
|
|
11660
|
-
"ENOTFOUND",
|
|
11661
|
-
"ENETUNREACH",
|
|
11662
|
-
// SSL errors from https://github.com/nodejs/node/blob/fc8e3e2cdc521978351de257030db0076d79e0ab/src/crypto/crypto_common.cc#L301-L328
|
|
11663
|
-
"UNABLE_TO_GET_ISSUER_CERT",
|
|
11664
|
-
"UNABLE_TO_GET_CRL",
|
|
11665
|
-
"UNABLE_TO_DECRYPT_CERT_SIGNATURE",
|
|
11666
|
-
"UNABLE_TO_DECRYPT_CRL_SIGNATURE",
|
|
11667
|
-
"UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
|
|
11668
|
-
"CERT_SIGNATURE_FAILURE",
|
|
11669
|
-
"CRL_SIGNATURE_FAILURE",
|
|
11670
|
-
"CERT_NOT_YET_VALID",
|
|
11671
|
-
"CERT_HAS_EXPIRED",
|
|
11672
|
-
"CRL_NOT_YET_VALID",
|
|
11673
|
-
"CRL_HAS_EXPIRED",
|
|
11674
|
-
"ERROR_IN_CERT_NOT_BEFORE_FIELD",
|
|
11675
|
-
"ERROR_IN_CERT_NOT_AFTER_FIELD",
|
|
11676
|
-
"ERROR_IN_CRL_LAST_UPDATE_FIELD",
|
|
11677
|
-
"ERROR_IN_CRL_NEXT_UPDATE_FIELD",
|
|
11678
|
-
"OUT_OF_MEM",
|
|
11679
|
-
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
11680
|
-
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
11681
|
-
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
11682
|
-
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
11683
|
-
"CERT_CHAIN_TOO_LONG",
|
|
11684
|
-
"CERT_REVOKED",
|
|
11685
|
-
"INVALID_CA",
|
|
11686
|
-
"PATH_LENGTH_EXCEEDED",
|
|
11687
|
-
"INVALID_PURPOSE",
|
|
11688
|
-
"CERT_UNTRUSTED",
|
|
11689
|
-
"CERT_REJECTED",
|
|
11690
|
-
"HOSTNAME_MISMATCH"
|
|
11691
|
-
]);
|
|
11692
|
-
module2.exports = (error2) => !denyList.has(error2 && error2.code);
|
|
11693
|
-
}
|
|
11694
|
-
});
|
|
11695
|
-
|
|
11696
11655
|
// src/teamcity/errors.ts
|
|
11697
|
-
function
|
|
11656
|
+
function isRetryableError(error2) {
|
|
11698
11657
|
if (error2 instanceof TeamCityAPIError) {
|
|
11699
11658
|
if (error2 instanceof TeamCityNetworkError) {
|
|
11700
11659
|
return true;
|
|
@@ -11836,6 +11795,47 @@ var init_errors = __esm({
|
|
|
11836
11795
|
}
|
|
11837
11796
|
});
|
|
11838
11797
|
|
|
11798
|
+
// node_modules/is-retry-allowed/index.js
|
|
11799
|
+
var require_is_retry_allowed = __commonJS({
|
|
11800
|
+
"node_modules/is-retry-allowed/index.js"(exports2, module2) {
|
|
11801
|
+
"use strict";
|
|
11802
|
+
var denyList = /* @__PURE__ */ new Set([
|
|
11803
|
+
"ENOTFOUND",
|
|
11804
|
+
"ENETUNREACH",
|
|
11805
|
+
// SSL errors from https://github.com/nodejs/node/blob/fc8e3e2cdc521978351de257030db0076d79e0ab/src/crypto/crypto_common.cc#L301-L328
|
|
11806
|
+
"UNABLE_TO_GET_ISSUER_CERT",
|
|
11807
|
+
"UNABLE_TO_GET_CRL",
|
|
11808
|
+
"UNABLE_TO_DECRYPT_CERT_SIGNATURE",
|
|
11809
|
+
"UNABLE_TO_DECRYPT_CRL_SIGNATURE",
|
|
11810
|
+
"UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
|
|
11811
|
+
"CERT_SIGNATURE_FAILURE",
|
|
11812
|
+
"CRL_SIGNATURE_FAILURE",
|
|
11813
|
+
"CERT_NOT_YET_VALID",
|
|
11814
|
+
"CERT_HAS_EXPIRED",
|
|
11815
|
+
"CRL_NOT_YET_VALID",
|
|
11816
|
+
"CRL_HAS_EXPIRED",
|
|
11817
|
+
"ERROR_IN_CERT_NOT_BEFORE_FIELD",
|
|
11818
|
+
"ERROR_IN_CERT_NOT_AFTER_FIELD",
|
|
11819
|
+
"ERROR_IN_CRL_LAST_UPDATE_FIELD",
|
|
11820
|
+
"ERROR_IN_CRL_NEXT_UPDATE_FIELD",
|
|
11821
|
+
"OUT_OF_MEM",
|
|
11822
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
11823
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
11824
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
11825
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
11826
|
+
"CERT_CHAIN_TOO_LONG",
|
|
11827
|
+
"CERT_REVOKED",
|
|
11828
|
+
"INVALID_CA",
|
|
11829
|
+
"PATH_LENGTH_EXCEEDED",
|
|
11830
|
+
"INVALID_PURPOSE",
|
|
11831
|
+
"CERT_UNTRUSTED",
|
|
11832
|
+
"CERT_REJECTED",
|
|
11833
|
+
"HOSTNAME_MISMATCH"
|
|
11834
|
+
]);
|
|
11835
|
+
module2.exports = (error2) => !denyList.has(error2 && error2.code);
|
|
11836
|
+
}
|
|
11837
|
+
});
|
|
11838
|
+
|
|
11839
11839
|
// src/teamcity/build-status-manager.ts
|
|
11840
11840
|
var build_status_manager_exports = {};
|
|
11841
11841
|
__export(build_status_manager_exports, {
|
|
@@ -16510,6 +16510,9 @@ function formatError(err, context) {
|
|
|
16510
16510
|
};
|
|
16511
16511
|
}
|
|
16512
16512
|
|
|
16513
|
+
// src/middleware/global-error-handler.ts
|
|
16514
|
+
init_errors();
|
|
16515
|
+
|
|
16513
16516
|
// src/utils/error-logger.ts
|
|
16514
16517
|
var ErrorLogger = class _ErrorLogger {
|
|
16515
16518
|
static instance;
|
|
@@ -16648,6 +16651,14 @@ var GlobalErrorHandler = class _GlobalErrorHandler {
|
|
|
16648
16651
|
* Transform raw errors into structured MCP errors
|
|
16649
16652
|
*/
|
|
16650
16653
|
transformError(error2, context) {
|
|
16654
|
+
if (error2 instanceof TeamCityAPIError) {
|
|
16655
|
+
return new MCPTeamCityError(
|
|
16656
|
+
this.sanitizeErrorMessage(error2.message),
|
|
16657
|
+
error2.statusCode ?? 500,
|
|
16658
|
+
error2.code,
|
|
16659
|
+
context.requestId
|
|
16660
|
+
);
|
|
16661
|
+
}
|
|
16651
16662
|
if (error2 instanceof MCPToolError) {
|
|
16652
16663
|
if (this.options.sanitizeErrors) {
|
|
16653
16664
|
const sanitizedMessage = this.sanitizeErrorMessage(error2.message);
|
|
@@ -16787,20 +16798,20 @@ function isNetworkError(error2) {
|
|
|
16787
16798
|
}
|
|
16788
16799
|
var SAFE_HTTP_METHODS = ["get", "head", "options"];
|
|
16789
16800
|
var IDEMPOTENT_HTTP_METHODS = SAFE_HTTP_METHODS.concat(["put", "delete"]);
|
|
16790
|
-
function
|
|
16801
|
+
function isRetryableError2(error2) {
|
|
16791
16802
|
return error2.code !== "ECONNABORTED" && (!error2.response || error2.response.status === 429 || error2.response.status >= 500 && error2.response.status <= 599);
|
|
16792
16803
|
}
|
|
16793
16804
|
function isSafeRequestError(error2) {
|
|
16794
16805
|
if (!error2.config?.method) {
|
|
16795
16806
|
return false;
|
|
16796
16807
|
}
|
|
16797
|
-
return
|
|
16808
|
+
return isRetryableError2(error2) && SAFE_HTTP_METHODS.indexOf(error2.config.method) !== -1;
|
|
16798
16809
|
}
|
|
16799
16810
|
function isIdempotentRequestError(error2) {
|
|
16800
16811
|
if (!error2.config?.method) {
|
|
16801
16812
|
return false;
|
|
16802
16813
|
}
|
|
16803
|
-
return
|
|
16814
|
+
return isRetryableError2(error2) && IDEMPOTENT_HTTP_METHODS.indexOf(error2.config.method) !== -1;
|
|
16804
16815
|
}
|
|
16805
16816
|
function isNetworkOrIdempotentRequestError(error2) {
|
|
16806
16817
|
return isNetworkError(error2) || isIdempotentRequestError(error2);
|
|
@@ -16947,11 +16958,12 @@ axiosRetry.isIdempotentRequestError = isIdempotentRequestError;
|
|
|
16947
16958
|
axiosRetry.isNetworkOrIdempotentRequestError = isNetworkOrIdempotentRequestError;
|
|
16948
16959
|
axiosRetry.exponentialDelay = exponentialDelay;
|
|
16949
16960
|
axiosRetry.linearDelay = linearDelay;
|
|
16950
|
-
axiosRetry.isRetryableError =
|
|
16961
|
+
axiosRetry.isRetryableError = isRetryableError2;
|
|
16951
16962
|
var esm_default = axiosRetry;
|
|
16952
16963
|
|
|
16953
16964
|
// src/teamcity/auth.ts
|
|
16954
16965
|
var import_crypto2 = require("crypto");
|
|
16966
|
+
init_errors();
|
|
16955
16967
|
function generateRequestId() {
|
|
16956
16968
|
return (0, import_crypto2.randomUUID)();
|
|
16957
16969
|
}
|
|
@@ -16972,36 +16984,6 @@ function addRequestId(config2) {
|
|
|
16972
16984
|
});
|
|
16973
16985
|
return config2;
|
|
16974
16986
|
}
|
|
16975
|
-
function extractErrorDetails(error2) {
|
|
16976
|
-
const requestId = error2.config?.requestId;
|
|
16977
|
-
if (error2.response != null) {
|
|
16978
|
-
const data = error2.response.data;
|
|
16979
|
-
return {
|
|
16980
|
-
code: data?.code ?? `HTTP_${error2.response.status}`,
|
|
16981
|
-
message: data?.message ?? error2.message,
|
|
16982
|
-
details: data?.details ?? JSON.stringify(data),
|
|
16983
|
-
requestId,
|
|
16984
|
-
statusCode: error2.response.status,
|
|
16985
|
-
originalError: error2
|
|
16986
|
-
};
|
|
16987
|
-
} else if (error2.request != null) {
|
|
16988
|
-
return {
|
|
16989
|
-
code: "NO_RESPONSE",
|
|
16990
|
-
message: "No response received from TeamCity server",
|
|
16991
|
-
details: error2.message,
|
|
16992
|
-
requestId,
|
|
16993
|
-
originalError: error2
|
|
16994
|
-
};
|
|
16995
|
-
} else {
|
|
16996
|
-
return {
|
|
16997
|
-
code: "REQUEST_SETUP_ERROR",
|
|
16998
|
-
message: "Error setting up the request",
|
|
16999
|
-
details: error2.message,
|
|
17000
|
-
requestId,
|
|
17001
|
-
originalError: error2
|
|
17002
|
-
};
|
|
17003
|
-
}
|
|
17004
|
-
}
|
|
17005
16987
|
function logResponse(response) {
|
|
17006
16988
|
const requestId = response.config?.requestId;
|
|
17007
16989
|
const meta = response.config._tcMeta;
|
|
@@ -17018,7 +17000,8 @@ function logResponse(response) {
|
|
|
17018
17000
|
return response;
|
|
17019
17001
|
}
|
|
17020
17002
|
function logAndTransformError(error2) {
|
|
17021
|
-
const
|
|
17003
|
+
const requestId = error2.config?.requestId;
|
|
17004
|
+
const tcError = TeamCityAPIError.fromAxiosError(error2, requestId);
|
|
17022
17005
|
const meta = error2.config?._tcMeta;
|
|
17023
17006
|
const duration = meta?.start ? Date.now() - meta.start : void 0;
|
|
17024
17007
|
const sanitize = (val) => {
|
|
@@ -17032,14 +17015,14 @@ function logAndTransformError(error2) {
|
|
|
17032
17015
|
}
|
|
17033
17016
|
};
|
|
17034
17017
|
error("TeamCity API request failed", void 0, {
|
|
17035
|
-
requestId:
|
|
17036
|
-
code:
|
|
17037
|
-
message: sanitize(
|
|
17038
|
-
statusCode:
|
|
17039
|
-
details: sanitize(
|
|
17018
|
+
requestId: tcError.requestId,
|
|
17019
|
+
code: tcError.code,
|
|
17020
|
+
message: sanitize(tcError.message),
|
|
17021
|
+
statusCode: tcError.statusCode,
|
|
17022
|
+
details: sanitize(tcError.details),
|
|
17040
17023
|
duration
|
|
17041
17024
|
});
|
|
17042
|
-
return Promise.reject(
|
|
17025
|
+
return Promise.reject(tcError);
|
|
17043
17026
|
}
|
|
17044
17027
|
function validateToken(token) {
|
|
17045
17028
|
if (!token || token.length === 0) {
|
|
@@ -39438,7 +39421,7 @@ var TeamCityAPI = class _TeamCityAPI {
|
|
|
39438
39421
|
retryCondition: (error2) => {
|
|
39439
39422
|
const reqId = error2?.config?.requestId;
|
|
39440
39423
|
const tcError = TeamCityAPIError.fromAxiosError(error2, reqId);
|
|
39441
|
-
return
|
|
39424
|
+
return isRetryableError(tcError);
|
|
39442
39425
|
}
|
|
39443
39426
|
});
|
|
39444
39427
|
this.axiosInstance.interceptors.request.use((config2) => addRequestId(config2));
|
|
@@ -40442,9 +40425,16 @@ var DEV_TOOLS = [
|
|
|
40442
40425
|
description: "Fetch server metrics (CPU/memory/disk/load) if available",
|
|
40443
40426
|
inputSchema: { type: "object", properties: {} },
|
|
40444
40427
|
handler: async (_args) => {
|
|
40445
|
-
|
|
40446
|
-
|
|
40447
|
-
|
|
40428
|
+
return runTool(
|
|
40429
|
+
"get_server_metrics",
|
|
40430
|
+
null,
|
|
40431
|
+
async () => {
|
|
40432
|
+
const api = TeamCityAPI.getInstance();
|
|
40433
|
+
const metrics = await api.server.getAllMetrics();
|
|
40434
|
+
return json(metrics.data);
|
|
40435
|
+
},
|
|
40436
|
+
{}
|
|
40437
|
+
);
|
|
40448
40438
|
},
|
|
40449
40439
|
mode: "full"
|
|
40450
40440
|
},
|
|
@@ -40453,9 +40443,16 @@ var DEV_TOOLS = [
|
|
|
40453
40443
|
description: "Get TeamCity server info (version, build number, state)",
|
|
40454
40444
|
inputSchema: { type: "object", properties: {} },
|
|
40455
40445
|
handler: async (_args) => {
|
|
40456
|
-
|
|
40457
|
-
|
|
40458
|
-
|
|
40446
|
+
return runTool(
|
|
40447
|
+
"get_server_info",
|
|
40448
|
+
null,
|
|
40449
|
+
async () => {
|
|
40450
|
+
const api = TeamCityAPI.getInstance();
|
|
40451
|
+
const info2 = await api.server.getServerInfo();
|
|
40452
|
+
return json(info2.data);
|
|
40453
|
+
},
|
|
40454
|
+
{}
|
|
40455
|
+
);
|
|
40459
40456
|
}
|
|
40460
40457
|
},
|
|
40461
40458
|
{
|