@daghis/teamcity-mcp 0.1.2 → 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.
Files changed (53) hide show
  1. package/.commitlintrc.mjs +25 -0
  2. package/.github/dependabot.yml +38 -0
  3. package/.github/workflows/ci.yml +26 -22
  4. package/.github/workflows/codeql.yml +1 -1
  5. package/.github/workflows/commitlint.yml +3 -3
  6. package/.github/workflows/publish.yml +22 -2
  7. package/AGENTS.md +61 -0
  8. package/CHANGELOG.md +14 -0
  9. package/README.md +8 -0
  10. package/THIRD_PARTY_NOTICES.md +58 -0
  11. package/dist/index.js +390 -322
  12. package/dist/index.js.map +4 -4
  13. package/dist/src/middleware/global-error-handler.d.ts.map +1 -1
  14. package/dist/src/teamcity/auth.d.ts.map +1 -1
  15. package/dist/src/teamcity-client/api/agent-api.d.ts +15 -15
  16. package/dist/src/teamcity-client/api/agent-pool-api.d.ts +14 -14
  17. package/dist/src/teamcity-client/api/agent-type-api.d.ts +1 -1
  18. package/dist/src/teamcity-client/api/audit-api.d.ts +2 -2
  19. package/dist/src/teamcity-client/api/avatar-api.d.ts +4 -4
  20. package/dist/src/teamcity-client/api/build-api.d.ts +57 -57
  21. package/dist/src/teamcity-client/api/build-queue-api.d.ts +14 -14
  22. package/dist/src/teamcity-client/api/build-type-api.d.ts +109 -109
  23. package/dist/src/teamcity-client/api/change-api.d.ts +10 -10
  24. package/dist/src/teamcity-client/api/cloud-instance-api.d.ts +10 -10
  25. package/dist/src/teamcity-client/api/deployment-dashboard-api.d.ts +9 -9
  26. package/dist/src/teamcity-client/api/global-server-settings-api.d.ts +2 -2
  27. package/dist/src/teamcity-client/api/group-api.d.ts +16 -16
  28. package/dist/src/teamcity-client/api/health-api.d.ts +4 -4
  29. package/dist/src/teamcity-client/api/investigation-api.d.ts +6 -6
  30. package/dist/src/teamcity-client/api/mute-api.d.ts +6 -6
  31. package/dist/src/teamcity-client/api/node-api.d.ts +6 -6
  32. package/dist/src/teamcity-client/api/problem-api.d.ts +2 -2
  33. package/dist/src/teamcity-client/api/problem-occurrence-api.d.ts +2 -2
  34. package/dist/src/teamcity-client/api/project-api.d.ts +48 -48
  35. package/dist/src/teamcity-client/api/role-api.d.ts +8 -8
  36. package/dist/src/teamcity-client/api/root-api.d.ts +4 -4
  37. package/dist/src/teamcity-client/api/server-api.d.ts +18 -18
  38. package/dist/src/teamcity-client/api/server-authentication-settings-api.d.ts +2 -2
  39. package/dist/src/teamcity-client/api/test-api.d.ts +2 -2
  40. package/dist/src/teamcity-client/api/test-occurrence-api.d.ts +2 -2
  41. package/dist/src/teamcity-client/api/user-api.d.ts +28 -28
  42. package/dist/src/teamcity-client/api/vcs-root-api.d.ts +14 -14
  43. package/dist/src/teamcity-client/api/vcs-root-instance-api.d.ts +17 -17
  44. package/dist/src/teamcity-client/api/versioned-settings-api.d.ts +15 -15
  45. package/dist/src/tools.d.ts.map +1 -1
  46. package/package.json +14 -13
  47. package/scripts/build.cjs +25 -6
  48. package/scripts/generate-third-party-notices.cjs +71 -0
  49. package/src/middleware/global-error-handler.ts +11 -0
  50. package/src/teamcity/auth.ts +13 -9
  51. package/src/tools.ts +20 -6
  52. package/.commitlintrc.js +0 -3
  53. package/TODO.md +0 -80
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ Generates THIRD_PARTY_NOTICES.md from installed packages.
4
+ Reads direct deps and devDeps from package.json and extracts
5
+ name, version, and license from node_modules/<pkg>/package.json.
6
+ */
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ function readJSON(p) {
11
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
12
+ }
13
+
14
+ function pkgInfo(name) {
15
+ try {
16
+ const p = path.join('node_modules', ...name.split('/'));
17
+ const data = readJSON(path.join(p, 'package.json'));
18
+ // Normalize license string
19
+ let license = 'UNKNOWN';
20
+ if (typeof data.license === 'string') license = data.license;
21
+ else if (data.license && typeof data.license.type === 'string') license = data.license.type;
22
+ else if (Array.isArray(data.licenses) && data.licenses.length && data.licenses[0].type)
23
+ license = data.licenses[0].type;
24
+ return { name, version: data.version || 'UNKNOWN', license };
25
+ } catch {
26
+ return { name, version: 'UNKNOWN', license: 'UNKNOWN' };
27
+ }
28
+ }
29
+
30
+ function generate() {
31
+ const pkg = readJSON(path.join(process.cwd(), 'package.json'));
32
+ const prod = Object.keys(pkg.dependencies || {}).sort().map(pkgInfo);
33
+ const dev = Object.keys(pkg.devDependencies || {}).sort().map(pkgInfo);
34
+
35
+ const lines = [];
36
+ lines.push('# Third-Party Notices');
37
+ lines.push('');
38
+ lines.push(
39
+ 'This project includes third-party software. The following lists the direct dependencies and their licenses as resolved in this workspace. For full license texts, see each package’s own repository or the copies included in `node_modules/<package>/LICENSE` when present.'
40
+ );
41
+ lines.push('');
42
+ lines.push(
43
+ 'If a dependency is not currently installed in `node_modules`, its version or license may be shown as UNKNOWN below; consult the package’s metadata for definitive terms.'
44
+ );
45
+ lines.push('');
46
+ const now = new Date();
47
+ const isoDate = now.toISOString().slice(0, 10);
48
+ lines.push(`Last updated: ${isoDate}`);
49
+ lines.push('');
50
+
51
+ lines.push('## Production Dependencies');
52
+ lines.push('');
53
+ for (const i of prod) lines.push(`- ${i.name} ${i.version} — ${i.license}`);
54
+ lines.push('');
55
+
56
+ lines.push('## Development Dependencies');
57
+ lines.push('');
58
+ for (const i of dev) lines.push(`- ${i.name} ${i.version} — ${i.license}`);
59
+ lines.push('');
60
+ lines.push('---');
61
+ lines.push('');
62
+ lines.push(
63
+ 'Note: This document is provided for convenience and does not modify any license terms. All third-party packages remain the property of their respective copyright holders and are licensed under their own terms.'
64
+ );
65
+ lines.push('');
66
+
67
+ fs.writeFileSync('THIRD_PARTY_NOTICES.md', lines.join('\n'));
68
+ }
69
+
70
+ generate();
71
+
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { AxiosError } from 'axios';
6
6
 
7
+ import { TeamCityAPIError } from '@/teamcity/errors';
7
8
  import { ErrorContext, errorLogger } from '@/utils/error-logger';
8
9
 
9
10
  import {
@@ -98,6 +99,16 @@ export class GlobalErrorHandler {
98
99
  * Transform raw errors into structured MCP errors
99
100
  */
100
101
  private transformError(error: unknown, context: ErrorContext): Error {
102
+ // Errors already normalized by our TeamCity client
103
+ if (error instanceof TeamCityAPIError) {
104
+ return new MCPTeamCityError(
105
+ this.sanitizeErrorMessage(error.message),
106
+ error.statusCode ?? 500,
107
+ error.code,
108
+ context.requestId
109
+ );
110
+ }
111
+
101
112
  // Already an MCP error
102
113
  if (error instanceof MCPToolError) {
103
114
  // Still sanitize the message if needed
@@ -7,15 +7,16 @@ import type {
7
7
  AxiosResponse,
8
8
  InternalAxiosRequestConfig,
9
9
  } from 'axios';
10
- import { v4 as uuidv4 } from 'uuid';
10
+ import { randomUUID } from 'crypto';
11
11
 
12
+ import { TeamCityAPIError } from '@/teamcity/errors';
12
13
  import { info, error as logError } from '@/utils/logger';
13
14
 
14
15
  /**
15
16
  * Generate a unique request ID for tracing
16
17
  */
17
18
  export function generateRequestId(): string {
18
- return uuidv4();
19
+ return randomUUID();
19
20
  }
20
21
 
21
22
  /**
@@ -126,7 +127,10 @@ export function logResponse(response: AxiosResponse): AxiosResponse {
126
127
  * Log error with request ID and transform
127
128
  */
128
129
  export function logAndTransformError(error: AxiosError): Promise<never> {
129
- const teamcityError = extractErrorDetails(error);
130
+ // Build a rich TeamCityAPIError instance so downstream handlers
131
+ // see an Error subclass (not a plain object)
132
+ const requestId = (error.config as AxiosRequestConfig & { requestId?: string })?.requestId;
133
+ const tcError = TeamCityAPIError.fromAxiosError(error, requestId);
130
134
  const meta = (error.config as unknown as { _tcMeta?: { start: number } })?._tcMeta;
131
135
  const duration = meta?.start ? Date.now() - meta.start : undefined;
132
136
 
@@ -148,15 +152,15 @@ export function logAndTransformError(error: AxiosError): Promise<never> {
148
152
  };
149
153
 
150
154
  logError('TeamCity API request failed', undefined, {
151
- requestId: teamcityError.requestId,
152
- code: teamcityError.code,
153
- message: sanitize(teamcityError.message) as string,
154
- statusCode: teamcityError.statusCode,
155
- details: sanitize(teamcityError.details),
155
+ requestId: tcError.requestId,
156
+ code: tcError.code,
157
+ message: sanitize(tcError.message) as string,
158
+ statusCode: tcError.statusCode,
159
+ details: sanitize(tcError.details),
156
160
  duration,
157
161
  });
158
162
 
159
- return Promise.reject(teamcityError);
163
+ return Promise.reject(tcError);
160
164
  }
161
165
 
162
166
  /**
package/src/tools.ts CHANGED
@@ -1014,9 +1014,16 @@ const DEV_TOOLS: ToolDefinition[] = [
1014
1014
  description: 'Fetch server metrics (CPU/memory/disk/load) if available',
1015
1015
  inputSchema: { type: 'object', properties: {} },
1016
1016
  handler: async (_args: unknown) => {
1017
- const api = TeamCityAPI.getInstance();
1018
- const metrics = await api.server.getAllMetrics();
1019
- return json(metrics.data);
1017
+ return runTool(
1018
+ 'get_server_metrics',
1019
+ null,
1020
+ async () => {
1021
+ const api = TeamCityAPI.getInstance();
1022
+ const metrics = await api.server.getAllMetrics();
1023
+ return json(metrics.data);
1024
+ },
1025
+ {}
1026
+ );
1020
1027
  },
1021
1028
  mode: 'full',
1022
1029
  },
@@ -1025,9 +1032,16 @@ const DEV_TOOLS: ToolDefinition[] = [
1025
1032
  description: 'Get TeamCity server info (version, build number, state)',
1026
1033
  inputSchema: { type: 'object', properties: {} },
1027
1034
  handler: async (_args: unknown) => {
1028
- const api = TeamCityAPI.getInstance();
1029
- const info = await api.server.getServerInfo();
1030
- return json(info.data);
1035
+ return runTool(
1036
+ 'get_server_info',
1037
+ null,
1038
+ async () => {
1039
+ const api = TeamCityAPI.getInstance();
1040
+ const info = await api.server.getServerInfo();
1041
+ return json(info.data);
1042
+ },
1043
+ {}
1044
+ );
1031
1045
  },
1032
1046
  },
1033
1047
  {
package/.commitlintrc.js DELETED
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- extends: ['@commitlint/config-conventional'],
3
- };
package/TODO.md DELETED
@@ -1,80 +0,0 @@
1
- # OSS Launch TODO
2
-
3
- High-level checklist to complete before publishing the repository to GitHub.
4
-
5
- ## Legal & Notices
6
- - Create `THIRD_PARTY_NOTICES.md`:
7
- - Note use of JetBrains TeamCity name/trademark; clarify no affiliation or endorsement.
8
- - Mention TeamCity REST API usage and link to relevant terms.
9
- - Acknowledge project crafting via Anthropic’s Claude Code and OpenAI Codex CLI.
10
- - List notable third‑party deps/tools: `@modelcontextprotocol/sdk`, `axios`, `zod`, `jest`, `ts-jest`, `prettier`, `eslint`, OpenAPI generator.
11
- - Verify LICENSE (MIT) is correct and reflected in README.
12
-
13
- ## TeamCity Self‑Integration
14
- - Create a TeamCity project that builds this repo (self‑hosting example):
15
- - Steps: install deps, `npm run lint`, `npm run typecheck`, `npm test`, `npm run build`.
16
- - Parameterize Node version via `.nvmrc` (24.7.0) and agent requirements.
17
- - Configure secure `TEAMCITY_URL`/`TEAMCITY_TOKEN` (dev‑mode, read‑only) as server/agent parameters.
18
- - Add triggers on `main` and PR branches; publish build artifacts (e.g., coverage report) for docs.
19
- - Optional: nightly job to run integration smoke tests against a sandbox TeamCity instance.
20
-
21
- ## GitHub Actions (CI/CD)
22
- - Add workflows (defer until ready to enable CI):
23
- - `ci.yml`: Node 24, cache `~/.npm`, run `lint:check`, `format:check`, `typecheck`, and `test:coverage` (enforce thresholds); optionally upload coverage to a badge service.
24
- - `release.yml`: On tag, build and publish to npm (if publishing), generate changelog and GitHub Release.
25
- - `codeql.yml`: Code scanning (JavaScript/TypeScript).
26
- - Optional: `e2e.yml` gated by secrets for TeamCity sandbox.
27
- - Configure required checks (branch protection) for `main` when CI is enabled.
28
-
29
- ## OSS Best Practices
30
- - README badges: build status (GH Actions), coverage, npm version (if publishing), license, Node version, Prettier.
31
- - Add Node 24 badge and update README references to Node 24.
32
- - Governance docs:
33
- - `CODE_OF_CONDUCT.md` (Contributor Covenant)
34
- - `SECURITY.md` (reporting policy)
35
- - Ensure `CONTRIBUTING.md` aligns with workflows and labels
36
- - Issue/PR templates and labels (bug, feature, question, good‑first‑issue)
37
- - Release hygiene:
38
- - `CHANGELOG.md` (keep a human‑readable log or use release‑please)
39
- - Semantic versioning policy documented in README
40
- - Repository hygiene:
41
- - `CODEOWNERS` for review routing
42
- - Dependabot or Renovate for deps
43
- - Stale bot policy (optional)
44
-
45
- ## Packaging & Publishing
46
- - `package.json` metadata: `repository`, `homepage`, `bugs`, `license`, `engines`, `exports`/`bin` (if CLI), `files` whitelist.
47
- - Add `.npmignore` (or use `files` field) to exclude tests, local scripts, and docs not needed for package.
48
- - Verify build output under `dist/` matches exports and type definitions.
49
-
50
- ## Codebase Quality (Housekeeping)
51
- - ESLint hygiene: keep `no-await-in-loop` disables narrowly scoped around intentional sequential logic (done in current codebase; verify on new contributions).
52
- - Remove remaining legacy `getTool(...)!` usages by migrating to `getRequiredTool` where applicable.
53
- - Centralize env var validation (zod) in `src/config` and use consistently.
54
- - Silence ESLint multi-project hint by using `tsconfig.lint.json` or enabling `noWarnOnMultipleProjects`.
55
-
56
- ## Test Stability
57
- - Avoid forced Jest exits; rely on natural shutdown. Ensure timers are cleared in `tests/setup.ts` (added) and prefer fake timers in new tests.
58
-
59
- ## Documentation
60
- - README refinements:
61
- - Add badges, succinct Quick Start, and a minimal “Dev vs Full mode” table.
62
- - Link to `docs/` and `THIRD_PARTY_NOTICES.md`.
63
- - Add a short “Security & Privacy” note about token redaction and not committing secrets.
64
- - Docs site (optional): publish GitHub Pages or Docusaurus if scope grows.
65
- - Provide minimal examples in `examples/` to demonstrate common tool calls and MCP client integration.
66
-
67
- ## Security & Compliance
68
- - Enable GitHub secret scanning and push protection.
69
- - Validate token redaction in logs; keep `.env.example` minimal and safe.
70
- - Add threat‑model notes (read‑only defaults in `dev` mode; `full` mode cautions).
71
-
72
- ## Project Setup on GitHub
73
- - Initialize repo, push baseline, add branch protections, enable Discussions (optional).
74
- - Configure Actions permissions (workflows can create releases/tags if needed).
75
- - Configure CI required checks and status badges.
76
-
77
- ## Nice‑to‑Haves (Post‑Launch)
78
- - Benchmarks and performance notes for large TeamCity instances.
79
- - Example TeamCity templates for self‑integration (YAML or screenshots).
80
- - Automated docs generation (OpenAPI → client usage snippets) if we add an API facade.