@smartbear/mcp 0.12.1 → 0.13.2

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 (89) hide show
  1. package/README.md +30 -6
  2. package/dist/bugsnag/client/api/CurrentUser.js +50 -26
  3. package/dist/bugsnag/client/api/Error.js +156 -93
  4. package/dist/bugsnag/client/api/Project.js +398 -276
  5. package/dist/bugsnag/client/api/api.js +4087 -3837
  6. package/dist/bugsnag/client/api/base.js +155 -173
  7. package/dist/bugsnag/client/api/configuration.js +28 -25
  8. package/dist/bugsnag/client/filters.js +11 -20
  9. package/dist/bugsnag/client.js +1398 -1281
  10. package/dist/bugsnag/input-schemas.js +39 -57
  11. package/dist/collaborator/client.js +335 -371
  12. package/dist/common/bugsnag.js +5 -3
  13. package/dist/common/cache.js +50 -57
  14. package/dist/common/client-registry.js +106 -119
  15. package/dist/common/info.js +7 -3
  16. package/dist/common/register-clients.js +0 -16
  17. package/dist/common/server.js +270 -228
  18. package/dist/common/tools.js +19 -0
  19. package/dist/common/transport-http.js +252 -343
  20. package/dist/common/transport-stdio.js +40 -37
  21. package/dist/common/zod-utils.js +20 -0
  22. package/dist/index.js +18 -23
  23. package/dist/package.json.js +11 -0
  24. package/dist/pactflow/client/ai.js +142 -169
  25. package/dist/pactflow/client/base.js +41 -51
  26. package/dist/pactflow/client/prompt-utils.js +93 -84
  27. package/dist/pactflow/client/prompts.js +95 -92
  28. package/dist/pactflow/client/tools.js +94 -83
  29. package/dist/pactflow/client/utils.js +60 -64
  30. package/dist/pactflow/client.js +399 -320
  31. package/dist/qmetry/client/api/client-api.js +43 -41
  32. package/dist/qmetry/client/api/error-handler.js +264 -310
  33. package/dist/qmetry/client/auto-resolve.js +78 -99
  34. package/dist/qmetry/client/automation.js +139 -162
  35. package/dist/qmetry/client/handlers.js +49 -46
  36. package/dist/qmetry/client/issues.js +133 -115
  37. package/dist/qmetry/client/project.js +153 -174
  38. package/dist/qmetry/client/requirement.js +82 -70
  39. package/dist/qmetry/client/testcase.js +240 -208
  40. package/dist/qmetry/client/testsuite.js +332 -293
  41. package/dist/qmetry/client/tools/automation-tools.js +291 -288
  42. package/dist/qmetry/client/tools/index.js +16 -13
  43. package/dist/qmetry/client/tools/issue-tools.js +534 -543
  44. package/dist/qmetry/client/tools/project-tools.js +635 -656
  45. package/dist/qmetry/client/tools/requirement-tools.js +525 -528
  46. package/dist/qmetry/client/tools/testcase-tools.js +773 -786
  47. package/dist/qmetry/client/tools/testsuite-tools.js +1069 -1083
  48. package/dist/qmetry/client/utils.js +8 -14
  49. package/dist/qmetry/client.js +111 -109
  50. package/dist/qmetry/config/constants.js +48 -44
  51. package/dist/qmetry/config/rest-endpoints.js +51 -48
  52. package/dist/qmetry/types/automation.js +7 -7
  53. package/dist/qmetry/types/common.js +763 -1049
  54. package/dist/qmetry/types/issues.js +26 -19
  55. package/dist/qmetry/types/project.js +32 -25
  56. package/dist/qmetry/types/requirements.js +26 -21
  57. package/dist/qmetry/types/testcase.js +55 -44
  58. package/dist/qmetry/types/testsuite.js +66 -52
  59. package/dist/reflect/client.js +284 -226
  60. package/dist/swagger/client/api.js +645 -662
  61. package/dist/swagger/client/configuration.js +31 -33
  62. package/dist/swagger/client/portal-types.js +204 -244
  63. package/dist/swagger/client/registry-types.js +62 -96
  64. package/dist/swagger/client/tools.js +148 -158
  65. package/dist/swagger/client/user-management-types.js +11 -22
  66. package/dist/swagger/client.js +143 -135
  67. package/dist/swagger/config-utils.js +10 -16
  68. package/dist/zephyr/client.js +43 -42
  69. package/dist/zephyr/common/api-client.js +35 -30
  70. package/dist/zephyr/common/auth-service.js +16 -13
  71. package/dist/zephyr/common/rest-api-schemas.js +3173 -5146
  72. package/dist/zephyr/tool/environment/get-environments.js +66 -66
  73. package/dist/zephyr/tool/priority/get-priorities.js +41 -41
  74. package/dist/zephyr/tool/project/get-project.js +37 -37
  75. package/dist/zephyr/tool/project/get-projects.js +46 -46
  76. package/dist/zephyr/tool/status/get-statuses.js +47 -47
  77. package/dist/zephyr/tool/test-case/get-test-case.js +37 -37
  78. package/dist/zephyr/tool/test-case/get-test-cases.js +62 -62
  79. package/dist/zephyr/tool/test-cycle/get-test-cycle.js +37 -37
  80. package/dist/zephyr/tool/test-cycle/get-test-cycles.js +70 -70
  81. package/dist/zephyr/tool/test-execution/get-test-execution.js +37 -37
  82. package/dist/zephyr/tool/test-execution/get-test-executions.js +43 -43
  83. package/package.json +5 -5
  84. package/dist/bugsnag/client/api/index.js +0 -6
  85. package/dist/common/types.js +0 -6
  86. package/dist/qmetry/client/tools/types.js +0 -1
  87. package/dist/swagger/client/index.js +0 -6
  88. package/dist/tests/unit/bugsnag/utils/factories.js +0 -86
  89. package/dist/zephyr/tool/zephyr-tool.js +0 -1
@@ -1,340 +1,419 @@
1
- import z from "zod";
1
+ import zod__default from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
- import { ToolError, } from "../common/types.js";
4
- import { getOADMatcherRecommendations, getUserMatcherSelection, } from "./client/prompt-utils.js";
3
+ import { ToolError } from "../common/tools.js";
4
+ import { getOADMatcherRecommendations, getUserMatcherSelection } from "./client/prompt-utils.js";
5
5
  import { PROMPTS } from "./client/prompts.js";
6
6
  import { TOOLS } from "./client/tools.js";
7
- const ConfigurationSchema = z.object({
8
- base_url: z.string().url().describe("Pact Broker or PactFlow base URL"),
9
- token: z
10
- .string()
11
- .optional()
12
- .describe("Bearer token for PactFlow authentication (use this OR username/password)"),
13
- username: z.string().optional().describe("Username for Pact Broker"),
14
- password: z.string().optional().describe("Password for Pact Broker"),
7
+ const ConfigurationSchema = zod__default.object({
8
+ base_url: zod__default.url().describe("Pact Broker or PactFlow base URL"),
9
+ token: zod__default.string().optional().describe(
10
+ "Bearer token for PactFlow authentication (use this OR username/password)"
11
+ ),
12
+ username: zod__default.string().optional().describe("Username for Pact Broker"),
13
+ password: zod__default.string().optional().describe("Password for Pact Broker")
15
14
  });
16
- // Tool definitions for PactFlow AI API client
17
- export class PactflowClient {
18
- name = "Contract Testing";
19
- toolPrefix = "contract-testing";
20
- configPrefix = "Pact-Broker";
21
- config = ConfigurationSchema;
22
- headers;
23
- aiBaseUrl;
24
- baseUrl;
25
- _clientType;
26
- _server;
27
- get server() {
28
- if (!this._server)
29
- throw new Error("Server not configured");
30
- return this._server;
15
+ class PactflowClient {
16
+ name = "Contract Testing";
17
+ toolPrefix = "contract-testing";
18
+ configPrefix = "Pact-Broker";
19
+ config = ConfigurationSchema;
20
+ headers;
21
+ aiBaseUrl;
22
+ baseUrl;
23
+ _clientType;
24
+ _server;
25
+ get server() {
26
+ if (!this._server) throw new Error("Server not configured");
27
+ return this._server;
28
+ }
29
+ async configure(server, config) {
30
+ if (typeof config.token === "string") {
31
+ this.headers = {
32
+ Authorization: `Bearer ${config.token}`,
33
+ "Content-Type": "application/json",
34
+ "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
35
+ };
36
+ this._clientType = "pactflow";
37
+ } else if (typeof config.username === "string" && typeof config.password === "string") {
38
+ const authString = `${config.username}:${config.password}`;
39
+ this.headers = {
40
+ Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
41
+ "Content-Type": "application/json",
42
+ "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
43
+ };
44
+ this._clientType = "pact_broker";
45
+ } else {
46
+ return;
31
47
  }
32
- get clientType() {
33
- if (!this._clientType)
34
- throw new Error("Client not configured");
35
- return this._clientType;
48
+ this.baseUrl = config.base_url;
49
+ this.aiBaseUrl = `${this.baseUrl}/api/ai`;
50
+ this._server = server.server;
51
+ }
52
+ isConfigured() {
53
+ return this.baseUrl !== void 0;
54
+ }
55
+ // PactFlow AI client methods
56
+ /**
57
+ * Generate new Pact tests based on the provided input.
58
+ *
59
+ * @param toolInput The input data for the generation process.
60
+ * @param getInput Function to get additional input from the user if needed.
61
+ * @returns The result of the generation process.
62
+ * @throws Error if the HTTP request fails or the operation times out.
63
+ */
64
+ async generate(toolInput, getInput) {
65
+ if (toolInput.openapi?.document && (!toolInput.openapi?.matcher || Object.keys(toolInput.openapi.matcher).length === 0)) {
66
+ const matcherResponse = await getOADMatcherRecommendations(
67
+ toolInput.openapi.document,
68
+ this.server
69
+ );
70
+ const userSelection = await getUserMatcherSelection(
71
+ matcherResponse,
72
+ getInput
73
+ );
74
+ toolInput.openapi.matcher = userSelection;
36
75
  }
37
- async configure(server, config) {
38
- // Set headers based on the type of auth provided
39
- if (typeof config.token === "string") {
40
- this.headers = {
41
- Authorization: `Bearer ${config.token}`,
42
- "Content-Type": "application/json",
43
- "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`,
44
- };
45
- this._clientType = "pactflow";
46
- }
47
- else if (typeof config.username === "string" &&
48
- typeof config.password === "string") {
49
- const authString = `${config.username}:${config.password}`;
50
- this.headers = {
51
- Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
52
- "Content-Type": "application/json",
53
- "User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`,
54
- };
55
- this._clientType = "pact_broker";
56
- }
57
- else {
58
- return false; // Don't configure the client if no auth is provided
59
- }
60
- this.baseUrl = config.base_url;
61
- this.aiBaseUrl = `${this.baseUrl}/api/ai`;
62
- this._server = server.server;
63
- return true;
76
+ const status_response = await this.submitHttpCallback(
77
+ "/generate",
78
+ toolInput
79
+ );
80
+ return await this.pollForCompletion(
81
+ status_response,
82
+ "Generation"
83
+ );
84
+ }
85
+ /**
86
+ * Review the provided Pact tests and suggest improvements.
87
+ *
88
+ * @param toolInput The input data for the review process.
89
+ * @param getInput Function to get additional input from the user if needed.
90
+ * @returns The result of the review process.
91
+ * @throws Error if the HTTP request fails or the operation times out.
92
+ */
93
+ async review(toolInput, getInput) {
94
+ if (toolInput.openapi?.document && (!toolInput.openapi?.matcher || Object.keys(toolInput.openapi.matcher).length === 0)) {
95
+ const matcherResponse = await getOADMatcherRecommendations(
96
+ toolInput.openapi.document,
97
+ this.server
98
+ );
99
+ const userSelection = await getUserMatcherSelection(
100
+ matcherResponse,
101
+ getInput
102
+ );
103
+ toolInput.openapi.matcher = userSelection;
64
104
  }
65
- // PactFlow AI client methods
66
- /**
67
- * Generate new Pact tests based on the provided input.
68
- *
69
- * @param toolInput The input data for the generation process.
70
- * @param getInput Function to get additional input from the user if needed.
71
- * @returns The result of the generation process.
72
- * @throws Error if the HTTP request fails or the operation times out.
73
- */
74
- async generate(toolInput, getInput) {
75
- if (toolInput.openapi?.document &&
76
- (!toolInput.openapi?.matcher ||
77
- Object.keys(toolInput.openapi.matcher).length === 0)) {
78
- const matcherResponse = await getOADMatcherRecommendations(toolInput.openapi.document, this.server);
79
- const userSelection = await getUserMatcherSelection(matcherResponse, getInput);
80
- toolInput.openapi.matcher = userSelection;
81
- }
82
- // Submit the generation request
83
- const response = await fetch(`${this.aiBaseUrl}/generate`, {
84
- method: "POST",
85
- headers: this.headers,
86
- body: JSON.stringify(toolInput),
87
- });
88
- if (!response.ok) {
89
- throw new ToolError(`HTTP error! status: ${response.status} - ${await response.text()}`);
90
- }
91
- const status_response = await response.json();
92
- return await this.pollForCompletion(status_response, "Generation");
105
+ const status_response = await this.submitHttpCallback("/review", toolInput);
106
+ return await this.pollForCompletion(
107
+ status_response,
108
+ "Review Pacts"
109
+ );
110
+ }
111
+ /**
112
+ * Retrieve PactFlow AI entitlement information for the current user
113
+ * and organization when encountering 401 unauthorized errors.
114
+ * Use this to check AI entitlements and credits when AI operations fail.
115
+ *
116
+ * @returns Entitlement containing permissions, organization
117
+ * entitlements, and user entitlements.
118
+ * @throws Error if the request fails or returns a non-OK response.
119
+ */
120
+ async checkAIEntitlements() {
121
+ return await this.fetchJson(`${this.aiBaseUrl}/entitlement`, {
122
+ method: "GET",
123
+ errorContext: "PactFlow AI Entitlements Request"
124
+ });
125
+ }
126
+ async getStatus(statusUrl) {
127
+ const response = await fetch(statusUrl, {
128
+ method: "HEAD",
129
+ headers: this.headers
130
+ });
131
+ return {
132
+ status: response.status,
133
+ isComplete: response.status === 200
134
+ };
135
+ }
136
+ get requestHeaders() {
137
+ return this.headers;
138
+ }
139
+ async getResult(resultUrl) {
140
+ const response = await fetch(resultUrl, {
141
+ method: "GET",
142
+ headers: this.headers
143
+ });
144
+ if (!response.ok) {
145
+ throw new ToolError(`HTTP error! status: ${response.status}`);
93
146
  }
94
- /**
95
- * Review the provided Pact tests and suggest improvements.
96
- *
97
- * @param toolInput The input data for the review process.
98
- * @param getInput Function to get additional input from the user if needed.
99
- * @returns The result of the review process.
100
- * @throws Error if the HTTP request fails or the operation times out.
101
- */
102
- async review(toolInput, getInput) {
103
- if (toolInput.openapi?.document &&
104
- (!toolInput.openapi?.matcher ||
105
- Object.keys(toolInput.openapi.matcher).length === 0)) {
106
- const matcherResponse = await getOADMatcherRecommendations(toolInput.openapi.document, this.server);
107
- const userSelection = await getUserMatcherSelection(matcherResponse, getInput);
108
- toolInput.openapi.matcher = userSelection;
109
- }
110
- // Submit review request
111
- const response = await fetch(`${this.aiBaseUrl}/review`, {
112
- method: "POST",
113
- headers: this.headers,
114
- body: JSON.stringify(toolInput),
115
- });
116
- if (!response.ok) {
117
- throw new ToolError(`HTTP error! status: ${response.status} - ${await response.text()}`);
118
- }
119
- const status_response = await response.json();
120
- return await this.pollForCompletion(status_response, "Review Pacts");
147
+ return response.json();
148
+ }
149
+ async pollForCompletion(status_response, operationName) {
150
+ const startTime = Date.now();
151
+ const timeout = 12e4;
152
+ const pollInterval = 1e3;
153
+ while (Date.now() - startTime < timeout) {
154
+ const statusCheck = await this.getStatus(status_response.status_url);
155
+ if (statusCheck.isComplete) {
156
+ return await this.getResult(status_response.result_url);
157
+ }
158
+ if (statusCheck.status !== 202) {
159
+ throw new ToolError(
160
+ `${operationName} failed with status: ${statusCheck.status}`
161
+ );
162
+ }
163
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
121
164
  }
122
- /**
123
- * Retrieve PactFlow AI entitlement information for the current user
124
- * and organization when encountering 401 unauthorized errors.
125
- * Use this to check AI entitlements and credits when AI operations fail.
126
- *
127
- * @returns Entitlement containing permissions, organization
128
- * entitlements, and user entitlements.
129
- * @throws Error if the request fails or returns a non-OK response.
130
- */
131
- async checkAIEntitlements() {
132
- const url = `${this.aiBaseUrl}/entitlement`;
133
- try {
134
- const response = await fetch(url, {
135
- method: "GET",
136
- headers: this.headers,
137
- });
138
- if (!response.ok) {
139
- const errorText = await response.text().catch(() => "");
140
- throw new ToolError(`PactFlow AI Entitlements Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
141
- }
142
- return (await response.json());
143
- }
144
- catch (error) {
145
- process.stderr.write(`[CheckAIEntitlements] Unexpected error: ${error}\n`);
146
- throw error;
147
- }
165
+ throw new ToolError(
166
+ `${operationName} timed out after ${timeout / 1e3} seconds`
167
+ );
168
+ }
169
+ /**
170
+ * Helper method to fetch JSON data from an API endpoint.
171
+ *
172
+ * @param url The full URL to fetch from.
173
+ * @param options Options including method, body, and error context.
174
+ * @returns The parsed JSON response.
175
+ * @throws ToolError if the request fails.
176
+ */
177
+ async fetchJson(url, options) {
178
+ const { method, body, errorContext = "Request" } = options;
179
+ try {
180
+ const response = await fetch(url, {
181
+ method,
182
+ headers: this.headers,
183
+ ...body && { body: JSON.stringify(body) }
184
+ });
185
+ if (!response.ok) {
186
+ const errorText = await response.text().catch(() => "");
187
+ throw new ToolError(
188
+ `${errorContext} Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`,
189
+ void 0,
190
+ /* @__PURE__ */ new Map([["responseStatus", response.status]])
191
+ );
192
+ }
193
+ return await response.json();
194
+ } catch (error) {
195
+ if (error instanceof ToolError) {
196
+ throw error;
197
+ }
198
+ const errorMessage = error instanceof Error ? error.message : String(error);
199
+ console.error(`[${errorContext}] Unexpected error: ${error}
200
+ `);
201
+ throw new ToolError(
202
+ `${errorContext} Failed - ${errorMessage}`,
203
+ void 0,
204
+ /* @__PURE__ */ new Map([["responseStatus", 500]])
205
+ );
148
206
  }
149
- async getStatus(statusUrl) {
150
- const response = await fetch(statusUrl, {
151
- method: "HEAD",
152
- headers: this.headers,
153
- });
154
- return {
155
- status: response.status,
156
- isComplete: response.status === 200,
157
- };
207
+ }
208
+ /**
209
+ * Submits an HTTP callback request to the PactFlow AI API.
210
+ * @param endpoint The AI API endpoint (relative to aiBaseUrl), e.g., '/generate' or '/review'.
211
+ * @param body The request body specific to the AI operation.
212
+ * @returns StatusResponse with status_url for polling and result_url for fetching results.
213
+ * @throws ToolError if the request fails.
214
+ */
215
+ async submitHttpCallback(endpoint, body) {
216
+ return await this.fetchJson(
217
+ `${this.aiBaseUrl}${endpoint}`,
218
+ {
219
+ method: "POST",
220
+ body,
221
+ errorContext: `HTTP callback submission to ${endpoint}`
222
+ }
223
+ );
224
+ }
225
+ // PactFlow / Pact_Broker client methods
226
+ async getProviderStates({
227
+ provider
228
+ }) {
229
+ const uri_encoded_provider_name = encodeURIComponent(provider);
230
+ return await this.fetchJson(
231
+ `${this.baseUrl}/pacts/provider/${uri_encoded_provider_name}/provider-states`,
232
+ {
233
+ method: "GET",
234
+ errorContext: "Get Provider States"
235
+ }
236
+ );
237
+ }
238
+ /**
239
+ * Checks if a given pacticipant version is safe to deploy
240
+ * to a specified environment.
241
+ *
242
+ * @param body - Input containing:
243
+ * - `pacticipant`: The name of the service (pacticipant).
244
+ * - `version`: The version of the pacticipant being evaluated for deployment.
245
+ * - `environment`: The target environment (e.g., staging, production).
246
+ * @returns CanIDeployResponse containing deployment decision and verification results.
247
+ * @throws Error if the request fails or returns a non-OK response.
248
+ */
249
+ async canIDeploy(body) {
250
+ const { pacticipant, version, environment } = body;
251
+ const queryParams = new URLSearchParams({
252
+ pacticipant,
253
+ version,
254
+ environment
255
+ });
256
+ const url = `${this.baseUrl}/can-i-deploy?${queryParams.toString()}`;
257
+ return await this.fetchJson(url, {
258
+ method: "GET",
259
+ errorContext: "Can-I-Deploy Request"
260
+ });
261
+ }
262
+ /**
263
+ * Retrieves the matrix of pact verification results for the specified pacticipants.
264
+ * This allows you to see which consumer/provider combinations have been verified
265
+ * and make deployment decisions based on contract test results.
266
+ *
267
+ * @param body - Matrix query parameters including pacticipants, versions, environments, etc.
268
+ * @returns MatrixResponse containing the verification matrix, notices, and summary
269
+ * @throws Error if the request fails or returns a non-OK response
270
+ */
271
+ async getMatrix(body) {
272
+ const { q, latestby, limit } = body;
273
+ const queryParts = [];
274
+ if (latestby) {
275
+ queryParts.push(`latestby=${encodeURIComponent(latestby)}`);
158
276
  }
159
- get requestHeaders() {
160
- return this.headers;
277
+ if (limit !== void 0) {
278
+ queryParts.push(`limit=${limit}`);
161
279
  }
162
- async getResult(resultUrl) {
163
- const response = await fetch(resultUrl, {
164
- method: "GET",
165
- headers: this.headers,
166
- });
167
- // Check if the response is OK (status 200)
168
- if (!response.ok) {
169
- throw new ToolError(`HTTP error! status: ${response.status}`);
170
- }
171
- return response.json();
280
+ q.forEach((selector) => {
281
+ queryParts.push(
282
+ `q[]pacticipant=${encodeURIComponent(selector.pacticipant)}`
283
+ );
284
+ if (selector.version) {
285
+ queryParts.push(`q[]version=${encodeURIComponent(selector.version)}`);
286
+ }
287
+ if (selector.branch) {
288
+ queryParts.push(`q[]branch=${encodeURIComponent(selector.branch)}`);
289
+ }
290
+ if (selector.environment) {
291
+ queryParts.push(
292
+ `q[]environment=${encodeURIComponent(selector.environment)}`
293
+ );
294
+ }
295
+ if (selector.latest !== void 0) {
296
+ queryParts.push(`q[]latest=${selector.latest}`);
297
+ }
298
+ if (selector.tag) {
299
+ queryParts.push(`q[]tag=${encodeURIComponent(selector.tag)}`);
300
+ }
301
+ if (selector.mainBranch !== void 0) {
302
+ queryParts.push(`q[]mainBranch=${selector.mainBranch}`);
303
+ }
304
+ });
305
+ const url = `${this.baseUrl}/matrix?${queryParts.join("&")}`;
306
+ return await this.fetchJson(url, {
307
+ method: "GET",
308
+ errorContext: "Matrix Request"
309
+ });
310
+ }
311
+ /**
312
+ * Retrieves metrics across the workspace.
313
+ *
314
+ * @returns MetricsResponse containing workspace-wide metrics
315
+ * @throws Error if the request fails or returns a non-OK response
316
+ */
317
+ async getMetrics() {
318
+ const url = `${this.baseUrl}/metrics`;
319
+ try {
320
+ const response = await fetch(url, {
321
+ method: "GET",
322
+ headers: this.headers
323
+ });
324
+ if (!response.ok) {
325
+ const errorText = await response.text().catch(() => "");
326
+ throw new ToolError(
327
+ `Metrics Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`
328
+ );
329
+ }
330
+ return await response.json();
331
+ } catch (error) {
332
+ console.error("[GetMetrics] Unexpected error:", error);
333
+ throw error;
172
334
  }
173
- async pollForCompletion(status_response, operationName) {
174
- // Polling for completion
175
- const startTime = Date.now();
176
- const timeout = 120000; // 120 seconds
177
- const pollInterval = 1000; // 1 second
178
- while (Date.now() - startTime < timeout) {
179
- const statusCheck = await this.getStatus(status_response.status_url);
180
- if (statusCheck.isComplete) {
181
- // Operation is complete, get the result
182
- return await this.getResult(status_response.result_url);
183
- }
184
- if (statusCheck.status !== 202) {
185
- throw new ToolError(`${operationName} failed with status: ${statusCheck.status}`);
186
- }
187
- // Wait before next poll
188
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
189
- }
190
- throw new ToolError(`${operationName} timed out after ${timeout / 1000} seconds`);
191
- }
192
- // PactFlow / Pact_Broker client methods
193
- async getProviderStates({ provider, }) {
194
- const uri_encoded_provider_name = encodeURIComponent(provider);
195
- const response = await fetch(`${this.baseUrl}/pacts/provider/${uri_encoded_provider_name}/provider-states`, {
196
- method: "GET",
197
- headers: this.headers,
198
- });
199
- if (!response.ok) {
200
- throw new ToolError(`HTTP error! status: ${response.status} - ${await response.text()}`);
201
- }
202
- return response.json();
335
+ }
336
+ /**
337
+ * Retrieves metrics for all teams.
338
+ *
339
+ * @returns TeamMetricsResponse containing metrics for all teams
340
+ * @throws Error if the request fails or returns a non-OK response
341
+ */
342
+ async getTeamMetrics() {
343
+ const url = `${this.baseUrl}/metrics/teams`;
344
+ try {
345
+ const response = await fetch(url, {
346
+ method: "GET",
347
+ headers: this.headers
348
+ });
349
+ if (!response.ok) {
350
+ const errorText = await response.text().catch(() => "");
351
+ throw new ToolError(
352
+ `Team Metrics Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`
353
+ );
354
+ }
355
+ return await response.json();
356
+ } catch (error) {
357
+ console.error("[GetTeamMetrics] Unexpected error:", error);
358
+ throw error;
203
359
  }
204
- /**
205
- * Checks if a given pacticipant version is safe to deploy
206
- * to a specified environment.
207
- *
208
- * @param body - Input containing:
209
- * - `pacticipant`: The name of the service (pacticipant).
210
- * - `version`: The version of the pacticipant being evaluated for deployment.
211
- * - `environment`: The target environment (e.g., staging, production).
212
- * @returns CanIDeployResponse containing deployment decision and verification results.
213
- * @throws Error if the request fails or returns a non-OK response.
214
- */
215
- async canIDeploy(body) {
216
- const { pacticipant, version, environment } = body;
217
- const queryParams = new URLSearchParams({
218
- pacticipant,
219
- version,
220
- environment,
221
- });
222
- const url = `${this.baseUrl}/can-i-deploy?${queryParams.toString()}`;
223
- try {
224
- const response = await fetch(url, {
225
- method: "GET",
226
- headers: this.headers,
227
- });
228
- if (!response.ok) {
229
- const errorText = await response.text().catch(() => "");
230
- throw new ToolError(`Can-I-Deploy Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
231
- }
232
- return (await response.json());
233
- }
234
- catch (error) {
235
- console.error(`[CanIDeploy] Unexpected error: ${error}\n`);
236
- throw error;
237
- }
360
+ }
361
+ /**
362
+ * Registers tools with the provided register function.
363
+ *
364
+ * @param register - The function used to register tools.
365
+ * @param getInput - The function used to get input for tools.
366
+ */
367
+ async registerTools(register, getInput) {
368
+ let disablePactflowAItools = false;
369
+ try {
370
+ const entitlement = await this.checkAIEntitlements();
371
+ if (!entitlement.aiEnabled) {
372
+ disablePactflowAItools = true;
373
+ }
374
+ } catch (error) {
375
+ if (error instanceof ToolError && error.metadata?.get("responseStatus") === 404) {
376
+ disablePactflowAItools = true;
377
+ }
238
378
  }
239
- /**
240
- * Retrieves the matrix of pact verification results for the specified pacticipants.
241
- * This allows you to see which consumer/provider combinations have been verified
242
- * and make deployment decisions based on contract test results.
243
- *
244
- * @param body - Matrix query parameters including pacticipants, versions, environments, etc.
245
- * @returns MatrixResponse containing the verification matrix, notices, and summary
246
- * @throws Error if the request fails or returns a non-OK response
247
- */
248
- async getMatrix(body) {
249
- const { q, latestby, limit } = body;
250
- // Build query parameters manually to avoid URL encoding of square brackets
251
- const queryParts = [];
252
- // Add optional parameters
253
- if (latestby) {
254
- queryParts.push(`latestby=${encodeURIComponent(latestby)}`);
255
- }
256
- if (limit !== undefined) {
257
- queryParts.push(`limit=${limit}`);
379
+ for (const tool of TOOLS.filter(
380
+ (t) => !this._clientType || t.clients.includes(this._clientType)
381
+ )) {
382
+ if (tool.tags && disablePactflowAItools && tool.tags.includes("pactflow-ai")) {
383
+ continue;
384
+ }
385
+ const { handler, clients: _, formatResponse, ...toolparams } = tool;
386
+ register(toolparams, async (args, _extra) => {
387
+ const handler_fn = this[handler];
388
+ if (typeof handler_fn !== "function") {
389
+ throw new Error(`Handler '${handler}' not found on PactClient`);
258
390
  }
259
- // Add the q parameters (pacticipant selectors)
260
- q.forEach((selector) => {
261
- queryParts.push(`q[]pacticipant=${encodeURIComponent(selector.pacticipant)}`);
262
- if (selector.version) {
263
- queryParts.push(`q[]version=${encodeURIComponent(selector.version)}`);
264
- }
265
- if (selector.branch) {
266
- queryParts.push(`q[]branch=${encodeURIComponent(selector.branch)}`);
267
- }
268
- if (selector.environment) {
269
- queryParts.push(`q[]environment=${encodeURIComponent(selector.environment)}`);
270
- }
271
- if (selector.latest !== undefined) {
272
- queryParts.push(`q[]latest=${selector.latest}`);
273
- }
274
- if (selector.tag) {
275
- queryParts.push(`q[]tag=${encodeURIComponent(selector.tag)}`);
276
- }
277
- if (selector.mainBranch !== undefined) {
278
- queryParts.push(`q[]mainBranch=${selector.mainBranch}`);
279
- }
280
- });
281
- const url = `${this.baseUrl}/matrix?${queryParts.join("&")}`;
282
- try {
283
- const response = await fetch(url, {
284
- method: "GET",
285
- headers: this.headers,
286
- });
287
- if (!response.ok) {
288
- const errorText = await response.text().catch(() => "");
289
- throw new ToolError(`Matrix Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
290
- }
291
- return (await response.json());
391
+ let result;
392
+ if (tool.enableElicitation) {
393
+ result = await handler_fn.call(this, args, getInput);
394
+ } else {
395
+ result = await handler_fn.call(this, args);
292
396
  }
293
- catch (error) {
294
- console.error("[GetMatrix] Unexpected error:", error);
295
- throw error;
397
+ if (formatResponse) {
398
+ return formatResponse(result);
296
399
  }
400
+ return {
401
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
402
+ };
403
+ });
297
404
  }
298
- /**
299
- * Registers tools with the provided register function.
300
- *
301
- * @param register - The function used to register tools.
302
- * @param getInput - The function used to get input for tools.
303
- */
304
- registerTools(register, getInput) {
305
- for (const tool of TOOLS.filter((t) => t.clients.includes(this.clientType))) {
306
- const { handler, clients: _, formatResponse, ...toolparams } = tool;
307
- register(toolparams, async (args, _extra) => {
308
- const handler_fn = this[handler];
309
- if (typeof handler_fn !== "function") {
310
- throw new Error(`Handler '${handler}' not found on PactClient`);
311
- }
312
- let result;
313
- if (tool.enableElicitation) {
314
- result = await handler_fn.call(this, args, getInput);
315
- }
316
- else {
317
- result = await handler_fn.call(this, args);
318
- }
319
- // Use custom response formatter if provided
320
- if (formatResponse) {
321
- return formatResponse(result);
322
- }
323
- // Default fallback
324
- return {
325
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
326
- };
327
- });
328
- }
329
- }
330
- /**
331
- * Registers prompts with the provided register function.
332
- *
333
- * @param register - The function used to register prompts.
334
- */
335
- registerPrompts(register) {
336
- PROMPTS.forEach((prompt) => {
337
- register(prompt.name, prompt.params, prompt.callback);
338
- });
339
- }
405
+ }
406
+ /**
407
+ * Registers prompts with the provided register function.
408
+ *
409
+ * @param register - The function used to register prompts.
410
+ */
411
+ registerPrompts(register) {
412
+ PROMPTS.forEach((prompt) => {
413
+ register(prompt.name, prompt.params, prompt.callback);
414
+ });
415
+ }
340
416
  }
417
+ export {
418
+ PactflowClient
419
+ };