@daghis/teamcity-mcp 1.3.2 → 1.3.3

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.3.3](https://github.com/Daghis/teamcity-mcp/compare/v1.3.2...v1.3.3) (2025-09-19)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **teamcity:** retain adapter api credentials ([#138](https://github.com/Daghis/teamcity-mcp/issues/138)) ([3ad31ed](https://github.com/Daghis/teamcity-mcp/commit/3ad31edf227eac904eee84ec3a80d16455ae37fc)), closes [#135](https://github.com/Daghis/teamcity-mcp/issues/135)
9
+
3
10
  ## [1.3.2](https://github.com/Daghis/teamcity-mcp/compare/v1.3.1...v1.3.2) (2025-09-18)
4
11
 
5
12
 
package/dist/index.js CHANGED
@@ -57,7 +57,6 @@ function isRetryableError(error2) {
57
57
  var TeamCityAPIError, TeamCityAuthorizationError, TeamCityNotFoundError, TeamCityRateLimitError, TeamCityServerError, TeamCityNetworkError, TeamCityRequestError, TeamCityTimeoutError, BuildNotFoundError, BuildAccessDeniedError;
58
58
  var init_errors = __esm({
59
59
  "src/teamcity/errors.ts"() {
60
- "use strict";
61
60
  TeamCityAPIError = class _TeamCityAPIError extends Error {
62
61
  code;
63
62
  statusCode;
@@ -922,6 +921,7 @@ function debug(message, meta) {
922
921
  }
923
922
 
924
923
  // src/tools.ts
924
+ var import_axios35 = require("axios");
925
925
  var import_zod4 = require("zod");
926
926
 
927
927
  // src/teamcity-client/models/resolution.ts
@@ -942,7 +942,7 @@ var BuildConfigurationUpdateManager = class {
942
942
  */
943
943
  async retrieveConfiguration(configId) {
944
944
  try {
945
- const response = await this.client.buildTypes.getBuildType(
945
+ const response = await this.client.modules.buildTypes.getBuildType(
946
946
  configId,
947
947
  "$long,parameters($long),settings($long),agent-requirements($long)"
948
948
  );
@@ -1125,10 +1125,14 @@ var BuildConfigurationUpdateManager = class {
1125
1125
  try {
1126
1126
  if (updates.name !== void 0 || updates.description !== void 0) {
1127
1127
  if (updates.name) {
1128
- await this.client.buildTypes.setBuildTypeField(currentConfig.id, "name", updates.name);
1128
+ await this.client.modules.buildTypes.setBuildTypeField(
1129
+ currentConfig.id,
1130
+ "name",
1131
+ updates.name
1132
+ );
1129
1133
  }
1130
1134
  if (updates.description !== void 0) {
1131
- await this.client.buildTypes.setBuildTypeField(
1135
+ await this.client.modules.buildTypes.setBuildTypeField(
1132
1136
  currentConfig.id,
1133
1137
  "description",
1134
1138
  updates.description ?? ""
@@ -1137,7 +1141,7 @@ var BuildConfigurationUpdateManager = class {
1137
1141
  }
1138
1142
  if (settings.length > 0) {
1139
1143
  for (const setting of settings) {
1140
- await this.client.buildTypes.setBuildTypeField(
1144
+ await this.client.modules.buildTypes.setBuildTypeField(
1141
1145
  currentConfig.id,
1142
1146
  `settings/${setting.name}`,
1143
1147
  setting.value
@@ -1147,7 +1151,7 @@ var BuildConfigurationUpdateManager = class {
1147
1151
  if (updates.removeParameters) {
1148
1152
  for (const paramName of updates.removeParameters) {
1149
1153
  try {
1150
- await this.client.buildTypes.deleteBuildParameterOfBuildType_2(
1154
+ await this.client.modules.buildTypes.deleteBuildParameterOfBuildType_2(
1151
1155
  paramName,
1152
1156
  currentConfig.id
1153
1157
  );
@@ -1158,7 +1162,7 @@ var BuildConfigurationUpdateManager = class {
1158
1162
  }
1159
1163
  if (updates.parameters) {
1160
1164
  for (const [name, value] of Object.entries(updates.parameters)) {
1161
- await this.client.buildTypes.setBuildTypeField(
1165
+ await this.client.modules.buildTypes.setBuildTypeField(
1162
1166
  currentConfig.id,
1163
1167
  `parameters/${name}`,
1164
1168
  value
@@ -1394,7 +1398,10 @@ var BuildResultsManager = class _BuildResultsManager {
1394
1398
  * Fetch build summary data
1395
1399
  */
1396
1400
  async fetchBuildSummary(buildId) {
1397
- const response = await this.client.builds.getBuild(`id:${buildId}`, _BuildResultsManager.fields);
1401
+ const response = await this.client.modules.builds.getBuild(
1402
+ this.toBuildLocator(buildId),
1403
+ _BuildResultsManager.fields
1404
+ );
1398
1405
  return response.data;
1399
1406
  }
1400
1407
  /**
@@ -1447,7 +1454,9 @@ var BuildResultsManager = class _BuildResultsManager {
1447
1454
  */
1448
1455
  async fetchArtifacts(buildId, options) {
1449
1456
  try {
1450
- const response = await this.client.listBuildArtifacts(buildId);
1457
+ const response = await this.client.modules.builds.getFilesListOfBuild(
1458
+ this.toBuildLocator(buildId)
1459
+ );
1451
1460
  const artifactListing = response.data;
1452
1461
  let artifacts = artifactListing.file ?? [];
1453
1462
  if (options.artifactFilter) {
@@ -1468,11 +1477,8 @@ var BuildResultsManager = class _BuildResultsManager {
1468
1477
  const maxSize = options.maxArtifactSize ?? _BuildResultsManager.defaultMaxArtifactSize;
1469
1478
  if ((artifact.size ?? 0) <= maxSize) {
1470
1479
  try {
1471
- const contentResponse = await this.client.downloadArtifactContent(
1472
- buildId,
1473
- artifactPath
1474
- );
1475
- artifactData.content = Buffer.from(contentResponse.data).toString("base64");
1480
+ const contentResponse = await this.downloadArtifactContent(buildId, artifactPath);
1481
+ artifactData.content = Buffer.from(contentResponse).toString("base64");
1476
1482
  } catch (err) {
1477
1483
  }
1478
1484
  }
@@ -1500,7 +1506,9 @@ var BuildResultsManager = class _BuildResultsManager {
1500
1506
  */
1501
1507
  async fetchStatistics(buildId) {
1502
1508
  try {
1503
- const response = await this.client.getBuildStatistics(buildId);
1509
+ const response = await this.client.modules.builds.getBuildStatisticValues(
1510
+ this.toBuildLocator(buildId)
1511
+ );
1504
1512
  const payload = response.data;
1505
1513
  const properties = payload.property ?? [];
1506
1514
  const stats = {};
@@ -1542,7 +1550,7 @@ var BuildResultsManager = class _BuildResultsManager {
1542
1550
  */
1543
1551
  async fetchChanges(buildId) {
1544
1552
  try {
1545
- const response = await this.client.listChangesForBuild(buildId);
1553
+ const response = await this.client.modules.changes.getAllChanges(`build:(id:${buildId})`);
1546
1554
  const changePayload = response.data;
1547
1555
  const changes = changePayload.change ?? [];
1548
1556
  return changes.map((change) => ({
@@ -1565,7 +1573,9 @@ var BuildResultsManager = class _BuildResultsManager {
1565
1573
  */
1566
1574
  async fetchDependencies(buildId) {
1567
1575
  try {
1568
- const response = await this.client.listSnapshotDependencies(buildId);
1576
+ const response = await this.client.request(
1577
+ (ctx) => ctx.axios.get(`${ctx.baseUrl}/app/rest/builds/id:${buildId}/snapshot-dependencies`)
1578
+ );
1569
1579
  const depsData = response.data;
1570
1580
  const builds = depsData.build ?? [];
1571
1581
  return builds.map((build) => ({
@@ -1586,10 +1596,30 @@ var BuildResultsManager = class _BuildResultsManager {
1586
1596
  if (/^https?:/i.test(path)) {
1587
1597
  return path;
1588
1598
  }
1599
+ const baseUrl = this.getBaseUrl();
1589
1600
  if (path.startsWith("/")) {
1590
- return `${this.client.baseUrl}${path}`;
1601
+ return `${baseUrl}${path}`;
1591
1602
  }
1592
- return `${this.client.baseUrl}/${path}`;
1603
+ return `${baseUrl}/${path}`;
1604
+ }
1605
+ getBaseUrl() {
1606
+ const baseUrl = this.client.getApiConfig().baseUrl;
1607
+ return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
1608
+ }
1609
+ toBuildLocator(buildId) {
1610
+ return buildId.includes(":") ? buildId : `id:${buildId}`;
1611
+ }
1612
+ async downloadArtifactContent(buildId, artifactPath) {
1613
+ const normalizedPath = artifactPath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
1614
+ const response = await this.client.request(
1615
+ (ctx) => ctx.axios.get(
1616
+ `${ctx.baseUrl}/app/rest/builds/id:${buildId}/artifacts/content/${normalizedPath}`,
1617
+ {
1618
+ responseType: "arraybuffer"
1619
+ }
1620
+ )
1621
+ );
1622
+ return response.data;
1593
1623
  }
1594
1624
  /**
1595
1625
  * Parse TeamCity date format
@@ -1707,10 +1737,11 @@ function createAdapterFromTeamCityAPI(api, options = {}) {
1707
1737
  const modules = resolveModules(api);
1708
1738
  const baseUrl = api.getBaseUrl();
1709
1739
  const httpInstance = api.http ?? import_axios.default.create({ baseURL: baseUrl });
1740
+ const fallbackApiConfig = resolveApiClientConfigFromApi(api, httpInstance);
1710
1741
  const resolvedApiConfig = {
1711
- baseUrl: options.apiConfig?.baseUrl ?? baseUrl,
1712
- token: options.apiConfig?.token ?? "",
1713
- timeout: options.apiConfig?.timeout ?? void 0
1742
+ baseUrl: options.apiConfig?.baseUrl ?? fallbackApiConfig.baseUrl,
1743
+ token: options.apiConfig?.token ?? fallbackApiConfig.token,
1744
+ timeout: options.apiConfig?.timeout ?? fallbackApiConfig.timeout
1714
1745
  };
1715
1746
  const resolvedFullConfig = options.fullConfig ?? {
1716
1747
  connection: {
@@ -1751,6 +1782,93 @@ function createAdapterFromTeamCityAPI(api, options = {}) {
1751
1782
  baseUrl
1752
1783
  };
1753
1784
  }
1785
+ var isAxiosHeadersRecord = (value) => typeof value === "object" && value !== null;
1786
+ var resolveApiClientConfigFromApi = (api, http) => {
1787
+ const timeout = resolveTimeout(http);
1788
+ const authHeader = getAuthorizationHeader(http);
1789
+ const token = stripBearerPrefix(authHeader);
1790
+ return {
1791
+ baseUrl: api.getBaseUrl(),
1792
+ token: token ?? "",
1793
+ timeout
1794
+ };
1795
+ };
1796
+ var getAuthorizationHeader = (http) => {
1797
+ const headers = http.defaults.headers;
1798
+ if (!isAxiosHeadersRecord(headers)) {
1799
+ return void 0;
1800
+ }
1801
+ const direct = pickAuthorization(headers);
1802
+ if (direct !== void 0) {
1803
+ return direct;
1804
+ }
1805
+ const commonRecord = isAxiosHeadersRecord(headers.common) ? headers.common : void 0;
1806
+ if (commonRecord) {
1807
+ const common = pickAuthorization(commonRecord);
1808
+ if (common !== void 0) {
1809
+ return common;
1810
+ }
1811
+ }
1812
+ const getter = resolveHeaderGetter(headers) ?? (commonRecord ? resolveHeaderGetter(commonRecord) : void 0);
1813
+ if (getter) {
1814
+ return readAuthorizationViaGetter(getter, headers);
1815
+ }
1816
+ return void 0;
1817
+ };
1818
+ var resolveTimeout = (http) => {
1819
+ const raw = http.defaults.timeout;
1820
+ if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
1821
+ return raw;
1822
+ }
1823
+ return void 0;
1824
+ };
1825
+ var pickAuthorization = (record) => {
1826
+ for (const key of Object.keys(record)) {
1827
+ if (key.toLowerCase() !== "authorization") {
1828
+ continue;
1829
+ }
1830
+ const value = record[key];
1831
+ if (typeof value === "string") {
1832
+ return value;
1833
+ }
1834
+ if (Array.isArray(value)) {
1835
+ const [first] = value;
1836
+ if (typeof first === "string") {
1837
+ return first;
1838
+ }
1839
+ }
1840
+ }
1841
+ return void 0;
1842
+ };
1843
+ var resolveHeaderGetter = (record) => {
1844
+ const candidate = record.get;
1845
+ return typeof candidate === "function" ? candidate : void 0;
1846
+ };
1847
+ var readAuthorizationViaGetter = (getter, context) => {
1848
+ try {
1849
+ const value = getter.call(context, "Authorization");
1850
+ if (typeof value === "string") {
1851
+ return value;
1852
+ }
1853
+ if (Array.isArray(value)) {
1854
+ const [first] = value;
1855
+ return typeof first === "string" ? first : void 0;
1856
+ }
1857
+ } catch {
1858
+ return void 0;
1859
+ }
1860
+ return void 0;
1861
+ };
1862
+ var stripBearerPrefix = (header) => {
1863
+ if (typeof header !== "string") {
1864
+ return void 0;
1865
+ }
1866
+ const match = /^Bearer\s+(.+)$/i.exec(header);
1867
+ return match?.[1] ?? header;
1868
+ };
1869
+
1870
+ // src/tools.ts
1871
+ init_errors();
1754
1872
 
1755
1873
  // src/teamcity/pagination.ts
1756
1874
  function createPaginatedFetcher(baseFetch, extractItems, extractTotal) {
@@ -36225,15 +36343,21 @@ var TeamCityAPI = class _TeamCityAPI {
36225
36343
  return this.builds.getBuildStatisticValues(this.toBuildLocator(buildId), fields);
36226
36344
  }
36227
36345
  async listChangesForBuild(buildId, fields) {
36228
- return this.axiosInstance.get("/app/rest/changes", {
36229
- params: {
36230
- locator: `build:(id:${buildId})`,
36231
- fields
36232
- }
36233
- });
36346
+ return this.changes.getAllChanges(`build:(id:${buildId})`, fields);
36234
36347
  }
36235
36348
  async listSnapshotDependencies(buildId) {
36236
- return this.axiosInstance.get(`/app/rest/builds/id:${buildId}/snapshot-dependencies`);
36349
+ const response = await this.builds.getBuild(
36350
+ this.toBuildLocator(buildId),
36351
+ "snapshot-dependencies"
36352
+ );
36353
+ const dependencies = response.data["snapshot-dependencies"];
36354
+ if (dependencies == null) {
36355
+ return response;
36356
+ }
36357
+ return {
36358
+ ...response,
36359
+ data: dependencies
36360
+ };
36237
36361
  }
36238
36362
  getBaseUrl() {
36239
36363
  return this.baseUrl;
@@ -36746,52 +36870,91 @@ var DEV_TOOLS = [
36746
36870
  if (!effectiveBuildId) {
36747
36871
  throw new Error("Failed to resolve buildId from inputs");
36748
36872
  }
36749
- if (typed.tail) {
36750
- const count = typed.lineCount ?? typed.pageSize ?? 500;
36751
- const full = await api.getBuildLog(effectiveBuildId);
36752
- const allLines = full.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
36753
- if (allLines.length > 0 && allLines[allLines.length - 1] === "") allLines.pop();
36754
- const total = allLines.length;
36755
- const start = Math.max(0, total - count);
36756
- const lines = allLines.slice(start);
36873
+ const shouldRetry2 = (error2) => {
36874
+ if (error2 instanceof TeamCityAPIError) {
36875
+ if (error2.code === "HTTP_404") {
36876
+ return true;
36877
+ }
36878
+ return isRetryableError(error2);
36879
+ }
36880
+ if ((0, import_axios35.isAxiosError)(error2)) {
36881
+ const status = error2.response?.status;
36882
+ if (status === 404) {
36883
+ return true;
36884
+ }
36885
+ if (status != null && status >= 500 && status < 600) {
36886
+ return true;
36887
+ }
36888
+ if (!status) {
36889
+ return true;
36890
+ }
36891
+ }
36892
+ return false;
36893
+ };
36894
+ const wait = (ms) => new Promise((resolve) => {
36895
+ setTimeout(resolve, ms);
36896
+ });
36897
+ const attemptFetch = async () => {
36898
+ if (typed.tail) {
36899
+ const count = typed.lineCount ?? typed.pageSize ?? 500;
36900
+ const full = await api.getBuildLog(effectiveBuildId);
36901
+ const allLines = full.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
36902
+ if (allLines.length > 0 && allLines[allLines.length - 1] === "") allLines.pop();
36903
+ const total = allLines.length;
36904
+ const start = Math.max(0, total - count);
36905
+ const lines = allLines.slice(start);
36906
+ return json({
36907
+ lines,
36908
+ meta: {
36909
+ buildId: effectiveBuildId,
36910
+ buildNumber: typeof typed.buildNumber !== "undefined" ? String(typed.buildNumber) : void 0,
36911
+ buildTypeId: typed.buildTypeId,
36912
+ mode: "tail",
36913
+ pageSize: count,
36914
+ startLine: start,
36915
+ hasMore: start > 0,
36916
+ totalLines: total
36917
+ }
36918
+ });
36919
+ }
36920
+ const effectivePageSize = typed.lineCount ?? typed.pageSize ?? 500;
36921
+ const startLine = typeof typed.startLine === "number" ? typed.startLine : ((typed.page ?? 1) - 1) * effectivePageSize;
36922
+ const chunk = await api.getBuildLogChunk(effectiveBuildId, {
36923
+ startLine,
36924
+ lineCount: effectivePageSize
36925
+ });
36926
+ const page = Math.floor(startLine / effectivePageSize) + 1;
36927
+ const hasMore = chunk.nextStartLine !== void 0;
36757
36928
  return json({
36758
- lines,
36929
+ lines: chunk.lines,
36759
36930
  meta: {
36760
36931
  buildId: effectiveBuildId,
36761
36932
  buildNumber: typeof typed.buildNumber !== "undefined" ? String(typed.buildNumber) : void 0,
36762
36933
  buildTypeId: typed.buildTypeId,
36763
- mode: "tail",
36764
- pageSize: count,
36765
- startLine: start,
36766
- hasMore: start > 0,
36767
- totalLines: total
36934
+ page,
36935
+ pageSize: effectivePageSize,
36936
+ startLine: chunk.startLine,
36937
+ nextPage: hasMore ? page + 1 : void 0,
36938
+ prevPage: page > 1 ? page - 1 : void 0,
36939
+ hasMore,
36940
+ totalLines: chunk.totalLines,
36941
+ nextStartLine: chunk.nextStartLine
36768
36942
  }
36769
36943
  });
36770
- }
36771
- const effectivePageSize = typed.lineCount ?? typed.pageSize ?? 500;
36772
- const startLine = typeof typed.startLine === "number" ? typed.startLine : ((typed.page ?? 1) - 1) * effectivePageSize;
36773
- const chunk = await api.getBuildLogChunk(effectiveBuildId, {
36774
- startLine,
36775
- lineCount: effectivePageSize
36776
- });
36777
- const page = Math.floor(startLine / effectivePageSize) + 1;
36778
- const hasMore = chunk.nextStartLine !== void 0;
36779
- return json({
36780
- lines: chunk.lines,
36781
- meta: {
36782
- buildId: effectiveBuildId,
36783
- buildNumber: typeof typed.buildNumber !== "undefined" ? String(typed.buildNumber) : void 0,
36784
- buildTypeId: typed.buildTypeId,
36785
- page,
36786
- pageSize: effectivePageSize,
36787
- startLine: chunk.startLine,
36788
- nextPage: hasMore ? page + 1 : void 0,
36789
- prevPage: page > 1 ? page - 1 : void 0,
36790
- hasMore,
36791
- totalLines: chunk.totalLines,
36792
- nextStartLine: chunk.nextStartLine
36944
+ };
36945
+ const maxAttempts = typed.tail ? 5 : 3;
36946
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
36947
+ try {
36948
+ return await attemptFetch();
36949
+ } catch (error2) {
36950
+ if (shouldRetry2(error2) && attempt < maxAttempts - 1) {
36951
+ await wait(500 * (attempt + 1));
36952
+ continue;
36953
+ }
36954
+ throw error2;
36793
36955
  }
36794
- });
36956
+ }
36957
+ throw new Error("Unable to fetch build log after retries");
36795
36958
  },
36796
36959
  args
36797
36960
  );
@@ -38638,8 +38801,8 @@ var FULL_MODE_TOOLS = [
38638
38801
  const typedArgs = args;
38639
38802
  const api = TeamCityAPI.getInstance();
38640
38803
  try {
38641
- const clientLike = { buildTypes: api.buildTypes };
38642
- const manager = new BuildConfigurationUpdateManager(clientLike);
38804
+ const adapter = createAdapterFromTeamCityAPI(api);
38805
+ const manager = new BuildConfigurationUpdateManager(adapter);
38643
38806
  const current = await manager.retrieveConfiguration(typedArgs.buildTypeId);
38644
38807
  if (current) {
38645
38808
  const updates = {};