@smartbear/mcp 0.12.0 → 0.13.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 (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 +158 -57
  4. package/dist/bugsnag/client/api/Project.js +398 -243
  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 -1277
  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,673 +1,656 @@
1
- import { ToolError } from "../../common/types.js";
2
- // Regex to extract owner, name, and version from SwaggerHub URLs.
3
- // Matches /apis/owner/name/version, /domains/owner/name/version, or /templates/owner/name/version
4
- // Example: /apis/acme/petstore/1.0.0
5
- // - group 1: type (apis|domains|templates)
6
- // - group 2: owner
7
- // - group 3: name
8
- // - group 4: version
1
+ import { ToolError } from "../../common/tools.js";
9
2
  const SWAGGER_URL_REGEX = /\/(apis|domains|templates)\/([^/]+)\/([^/]+)\/([^/]+)/;
10
- /**
11
- * Type guard to check if a value has an 'id' property
12
- */
13
3
  function hasId(value) {
14
- return typeof value === "object" && value !== null && "id" in value;
4
+ return typeof value === "object" && value !== null && "id" in value;
15
5
  }
16
- /**
17
- * Type guard to check if a value has a 'message' property
18
- */
19
6
  function hasMessage(value) {
20
- return typeof value === "object" && value !== null && "message" in value;
7
+ return typeof value === "object" && value !== null && "message" in value;
21
8
  }
22
- /**
23
- * Type guard to check if a value has an 'errorsFound' property
24
- */
25
9
  function hasErrorsFound(value) {
26
- return typeof value === "object" && value !== null && "errorsFound" in value;
10
+ return typeof value === "object" && value !== null && "errorsFound" in value;
27
11
  }
28
- export class SwaggerAPI {
29
- config;
30
- headers;
31
- constructor(config, userAgent) {
32
- this.config = config;
33
- this.headers = config.getHeaders(userAgent);
34
- }
35
- /**
36
- * Core response parsing logic shared between different response handlers.
37
- * Handles 204 No Content, empty responses, JSON parsing, and text fallbacks.
38
- * @template T - Expected JSON response data type
39
- * @template D - Default return type for empty responses
40
- * @param response - The fetch Response object
41
- * @param defaultReturn - Default value to return for empty responses
42
- * @returns Parsed response data or fallback value
43
- */
44
- async parseResponse(response, defaultReturn = {}) {
45
- // Handle 204 No Content responses
46
- if (response.status === 204) {
47
- return defaultReturn;
48
- }
49
- // Check if response has content-length of 0 (empty body)
50
- const contentLength = response.headers.get("content-length");
51
- if (contentLength === "0") {
52
- return defaultReturn;
53
- }
54
- // Check if response is JSON
55
- const contentType = response.headers.get("content-type");
56
- if (contentType?.includes("application/json")) {
57
- try {
58
- return (await response.json());
59
- }
60
- catch (error) {
61
- console.warn("Failed to parse JSON response (declared JSON):", error);
62
- return defaultReturn;
63
- }
64
- }
65
- // Fallback: read text and attempt heuristic JSON parse
66
- const text = await response.text();
67
- if (!text)
68
- return defaultReturn;
69
- const trimmed = text.trim();
70
- const firstChar = trimmed[0];
71
- if (firstChar === "{" || firstChar === "[") {
72
- try {
73
- return JSON.parse(trimmed);
74
- }
75
- catch (error) {
76
- console.warn("Heuristic JSON parse failed:", error);
77
- return { message: text };
78
- }
79
- }
12
+ class SwaggerAPI {
13
+ config;
14
+ headers;
15
+ constructor(config, userAgent) {
16
+ this.config = config;
17
+ this.headers = config.getHeaders(userAgent);
18
+ }
19
+ /**
20
+ * Core response parsing logic shared between different response handlers.
21
+ * Handles 204 No Content, empty responses, JSON parsing, and text fallbacks.
22
+ * @template T - Expected JSON response data type
23
+ * @template D - Default return type for empty responses
24
+ * @param response - The fetch Response object
25
+ * @param defaultReturn - Default value to return for empty responses
26
+ * @returns Parsed response data or fallback value
27
+ */
28
+ async parseResponse(response, defaultReturn = {}) {
29
+ if (response.status === 204) {
30
+ return defaultReturn;
31
+ }
32
+ const contentLength = response.headers.get("content-length");
33
+ if (contentLength === "0") {
34
+ return defaultReturn;
35
+ }
36
+ const contentType = response.headers.get("content-type");
37
+ if (contentType?.includes("application/json")) {
38
+ try {
39
+ return await response.json();
40
+ } catch (error) {
41
+ console.warn("Failed to parse JSON response (declared JSON):", error);
42
+ return defaultReturn;
43
+ }
44
+ }
45
+ const text = await response.text();
46
+ if (!text) return defaultReturn;
47
+ const trimmed = text.trim();
48
+ const firstChar = trimmed[0];
49
+ if (firstChar === "{" || firstChar === "[") {
50
+ try {
51
+ return JSON.parse(trimmed);
52
+ } catch (error) {
53
+ console.warn("Heuristic JSON parse failed:", error);
80
54
  return { message: text };
55
+ }
56
+ }
57
+ return { message: text };
58
+ }
59
+ /**
60
+ * Handles HTTP responses with smart JSON parsing and fallback handling.
61
+ * Includes HTTP error checking before parsing.
62
+ * @template T - Expected response data type
63
+ * @param response - The fetch Response object
64
+ * @param defaultReturn - Default value to return for empty responses
65
+ * @returns Parsed response data or fallback value
66
+ */
67
+ async handleResponse(response, defaultReturn = {}) {
68
+ if (!response.ok) {
69
+ throw new ToolError(`HTTP ${response.status}: ${response.statusText}`);
70
+ }
71
+ return this.parseResponse(
72
+ response,
73
+ defaultReturn
74
+ );
75
+ }
76
+ async getPortals() {
77
+ const response = await fetch(`${this.config.portalBasePath}/portals`, {
78
+ method: "GET",
79
+ headers: this.headers
80
+ });
81
+ const result = await this.handleResponse(
82
+ response,
83
+ []
84
+ );
85
+ return result;
86
+ }
87
+ async getOrganizations(params) {
88
+ const searchParams = new URLSearchParams();
89
+ if (params?.q) searchParams.append("q", params.q);
90
+ if (params?.sortBy) searchParams.append("sortBy", params.sortBy);
91
+ if (params?.order) searchParams.append("order", params.order);
92
+ if (params?.page !== void 0)
93
+ searchParams.append("page", params.page.toString());
94
+ if (params?.pageSize)
95
+ searchParams.append("pageSize", params.pageSize.toString());
96
+ const queryString = searchParams.toString();
97
+ const url = `${this.config.userManagementBasePath}/orgs${queryString ? `?${queryString}` : ""}`;
98
+ const response = await fetch(url, {
99
+ method: "GET",
100
+ headers: this.headers
101
+ });
102
+ const defaultResponse = {
103
+ items: [],
104
+ totalCount: 0,
105
+ pageSize: 50,
106
+ page: 0
107
+ };
108
+ const result = await this.handleResponse(
109
+ response,
110
+ defaultResponse
111
+ );
112
+ return result;
113
+ }
114
+ async createPortal(body) {
115
+ const response = await fetch(`${this.config.portalBasePath}/portals`, {
116
+ method: "POST",
117
+ headers: this.headers,
118
+ body: JSON.stringify(body)
119
+ });
120
+ const result = await this.handleResponse(response);
121
+ if (!hasId(result)) {
122
+ throw new Error("Unexpected empty response creating portal");
123
+ }
124
+ return result;
125
+ }
126
+ async getPortal(portalId) {
127
+ const response = await fetch(
128
+ `${this.config.portalBasePath}/portals/${portalId}`,
129
+ {
130
+ method: "GET",
131
+ headers: this.headers
132
+ }
133
+ );
134
+ const result = await this.handleResponse(response);
135
+ if (!hasId(result)) {
136
+ throw new ToolError("Portal not found or empty response");
137
+ }
138
+ return result;
139
+ }
140
+ async updatePortal(portalId, body) {
141
+ const response = await fetch(
142
+ `${this.config.portalBasePath}/portals/${portalId}`,
143
+ {
144
+ method: "PATCH",
145
+ headers: this.headers,
146
+ body: JSON.stringify(body)
147
+ }
148
+ );
149
+ return this.handleResponse(response);
150
+ }
151
+ async getPortalProducts(portalId) {
152
+ const response = await fetch(
153
+ `${this.config.portalBasePath}/portals/${portalId}/products`,
154
+ {
155
+ method: "GET",
156
+ headers: this.headers
157
+ }
158
+ );
159
+ const result = await this.handleResponse(
160
+ response,
161
+ []
162
+ );
163
+ return result;
164
+ }
165
+ async createPortalProduct(portalId, body) {
166
+ const response = await fetch(
167
+ `${this.config.portalBasePath}/portals/${portalId}/products`,
168
+ {
169
+ method: "POST",
170
+ headers: this.headers,
171
+ body: JSON.stringify(body)
172
+ }
173
+ );
174
+ const result = await this.handleResponse(response);
175
+ if (!hasId(result)) {
176
+ throw new Error("Unexpected empty response creating product");
177
+ }
178
+ return result;
179
+ }
180
+ async getPortalProduct(productId) {
181
+ const response = await fetch(
182
+ `${this.config.portalBasePath}/products/${productId}`,
183
+ {
184
+ method: "GET",
185
+ headers: this.headers
186
+ }
187
+ );
188
+ const result = await this.handleResponse(response);
189
+ if (!hasId(result)) {
190
+ throw new ToolError("Product not found or empty response");
191
+ }
192
+ return result;
193
+ }
194
+ async deletePortalProduct(productId) {
195
+ const response = await fetch(
196
+ `${this.config.portalBasePath}/products/${productId}`,
197
+ {
198
+ method: "DELETE",
199
+ headers: this.headers
200
+ }
201
+ );
202
+ return this.handleResponse(response);
203
+ }
204
+ async updatePortalProduct(productId, body) {
205
+ const response = await fetch(
206
+ `${this.config.portalBasePath}/products/${productId}`,
207
+ {
208
+ method: "PATCH",
209
+ headers: this.headers,
210
+ body: JSON.stringify(body)
211
+ }
212
+ );
213
+ return this.handleResponse(response, {
214
+ success: true
215
+ });
216
+ }
217
+ async publishPortalProduct(productId, preview = false) {
218
+ const response = await fetch(
219
+ `${this.config.portalBasePath}/products/${productId}/published-content?preview=${preview}`,
220
+ {
221
+ method: "PUT",
222
+ headers: this.headers
223
+ }
224
+ );
225
+ return this.handleResponse(response, {
226
+ success: true
227
+ });
228
+ }
229
+ async getPortalProductSections(productId, params) {
230
+ const queryParameters = new URLSearchParams();
231
+ if (params.embed) {
232
+ for (const item of params.embed) {
233
+ queryParameters.append("embed", item);
234
+ }
235
+ }
236
+ if (params.page !== void 0) {
237
+ queryParameters.append("page", params.page.toString());
238
+ }
239
+ if (params.size !== void 0) {
240
+ queryParameters.append("size", params.size.toString());
241
+ }
242
+ const url = `${this.config.portalBasePath}/products/${productId}/sections${queryParameters.toString() ? `?${queryParameters.toString()}` : ""}`;
243
+ const response = await fetch(url, {
244
+ method: "GET",
245
+ headers: this.headers
246
+ });
247
+ const result = await this.handleResponse(
248
+ response,
249
+ []
250
+ );
251
+ return result;
252
+ }
253
+ /**
254
+ * Create a new table of contents item in a portal product section
255
+ * @param sectionId - Section ID where the table of contents item will be created
256
+ * @param body - Table of contents creation parameters
257
+ * @returns Created table of contents item with metadata
258
+ */
259
+ async createTableOfContents(sectionId, body) {
260
+ const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents`;
261
+ const response = await fetch(url, {
262
+ method: "POST",
263
+ headers: this.headers,
264
+ body: JSON.stringify(body)
265
+ });
266
+ if (!response.ok) {
267
+ const errorText = await response.text();
268
+ throw new Error(
269
+ `API Hub createTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
270
+ );
271
+ }
272
+ const result = await response.json();
273
+ return result;
274
+ }
275
+ /**
276
+ * Get table of contents for a section
277
+ * @param args - Parameters for retrieving table of contents
278
+ * @returns List of table of contents items
279
+ */
280
+ async getTableOfContents(args) {
281
+ const { sectionId, embed, page, size } = args;
282
+ const searchParams = new URLSearchParams();
283
+ if (embed) {
284
+ for (const item of embed) {
285
+ searchParams.append("embed", item);
286
+ }
287
+ }
288
+ if (page !== void 0) {
289
+ searchParams.set("page", page.toString());
290
+ }
291
+ if (size !== void 0) {
292
+ searchParams.set("size", size.toString());
293
+ }
294
+ const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
295
+ const response = await fetch(url, {
296
+ method: "GET",
297
+ headers: this.headers
298
+ });
299
+ if (!response.ok) {
300
+ const errorText = await response.text();
301
+ throw new Error(
302
+ `API Hub getTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
303
+ );
304
+ }
305
+ const result = await response.json();
306
+ return result.items;
307
+ }
308
+ /**
309
+ * Get document content and metadata
310
+ * @param args - Parameters for retrieving document
311
+ * @returns Document with content and metadata
312
+ */
313
+ async getDocument(args) {
314
+ const { documentId } = args;
315
+ const url = `${this.config.portalBasePath}/documents/${documentId}`;
316
+ const response = await fetch(url, {
317
+ method: "GET",
318
+ headers: this.headers
319
+ });
320
+ if (!response.ok) {
321
+ const errorText = await response.text();
322
+ throw new Error(
323
+ `API Hub getDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
324
+ );
325
+ }
326
+ const result = await response.json();
327
+ return result;
328
+ }
329
+ /**
330
+ * Update document content
331
+ * @param args - Parameters for updating document
332
+ * @returns Success response
333
+ */
334
+ async updateDocument(args) {
335
+ const { documentId, ...body } = args;
336
+ const url = `${this.config.portalBasePath}/documents/${documentId}`;
337
+ const response = await fetch(url, {
338
+ method: "PATCH",
339
+ headers: this.headers,
340
+ body: JSON.stringify(body)
341
+ });
342
+ if (!response.ok) {
343
+ const errorText = await response.text();
344
+ throw new Error(
345
+ `API Hub updateDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
346
+ );
347
+ }
348
+ return { success: true };
349
+ }
350
+ /**
351
+ * Delete table of contents entry
352
+ * @param args - Parameters for deleting table of contents entry
353
+ * @returns Success response
354
+ */
355
+ async deleteTableOfContents(args) {
356
+ const { tableOfContentsId, recursive } = args;
357
+ const searchParams = new URLSearchParams();
358
+ if (recursive !== void 0) {
359
+ searchParams.set("recursive", recursive.toString());
360
+ }
361
+ const url = `${this.config.portalBasePath}/table-of-contents/${tableOfContentsId}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
362
+ const response = await fetch(url, {
363
+ method: "DELETE",
364
+ headers: this.headers
365
+ });
366
+ if (!response.ok) {
367
+ const errorText = await response.text();
368
+ throw new Error(
369
+ `API Hub deleteTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`
370
+ );
371
+ }
372
+ return { success: true };
373
+ }
374
+ /**
375
+ * Helper method for handling responses when error checking is already done.
376
+ * Delegates to parseResponse for the actual parsing logic.
377
+ * @template T - Expected response data type
378
+ * @template D - Default return type
379
+ * @param response - The fetch Response object
380
+ * @param defaultReturn - Default value to return for empty responses
381
+ * @returns Parsed response data or fallback value
382
+ */
383
+ // Registry API methods for SwaggerHub Design functionality
384
+ /**
385
+ * Search APIs and Domains in SwaggerHub Registry using /specs endpoint
386
+ * @param params Search parameters
387
+ * @returns Array of processed API metadata
388
+ */
389
+ async searchApis(params = {}) {
390
+ const searchParams = new URLSearchParams();
391
+ if (params.query) searchParams.append("query", params.query);
392
+ if (params.state) searchParams.append("state", params.state);
393
+ if (params.tag) searchParams.append("tag", params.tag);
394
+ if (params.offset !== void 0)
395
+ searchParams.append("offset", params.offset.toString());
396
+ if (params.limit !== void 0)
397
+ searchParams.append("limit", params.limit.toString());
398
+ if (params.sort) searchParams.append("sort", params.sort);
399
+ if (params.order) searchParams.append("order", params.order);
400
+ if (params.owner) searchParams.append("owner", params.owner);
401
+ if (params.specType) searchParams.append("specType", params.specType);
402
+ const url = `${this.config.registryBasePath}/specs${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
403
+ const response = await fetch(url, {
404
+ method: "GET",
405
+ headers: this.headers
406
+ });
407
+ if (!response.ok) {
408
+ throw new ToolError(
409
+ `SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`
410
+ );
411
+ }
412
+ const apisJsonResponse = await response.json();
413
+ return this.transformApisJsonToMetadata(apisJsonResponse.apis);
414
+ }
415
+ /**
416
+ * Transform APIs.json specifications to our ApiMetadata format
417
+ * @param specs Array of API specifications from APIs.json
418
+ * @returns Array of processed API metadata
419
+ */
420
+ transformApisJsonToMetadata(specs) {
421
+ return specs.map((spec) => {
422
+ const properties = spec.properties || [];
423
+ const getProperty = (type) => {
424
+ const property = properties.find((p) => p.type === type);
425
+ return property?.value || property?.url;
426
+ };
427
+ const swaggerUrl = getProperty("Swagger") || "";
428
+ const urlMatch = RegExp(SWAGGER_URL_REGEX).exec(swaggerUrl);
429
+ return {
430
+ owner: urlMatch?.[2] || "",
431
+ name: spec.name || "",
432
+ description: spec.description || "",
433
+ summary: spec.summary || "",
434
+ version: getProperty("X-Version") || urlMatch?.[4] || "",
435
+ specification: getProperty("X-Specification") || "",
436
+ created: getProperty("X-Created"),
437
+ modified: getProperty("X-Modified"),
438
+ published: getProperty("X-Published"),
439
+ private: getProperty("X-Private"),
440
+ oasVersion: getProperty("X-OASVersion"),
441
+ url: swaggerUrl
442
+ };
443
+ });
444
+ }
445
+ /**
446
+ * Get API definition from SwaggerHub Registry
447
+ * @param params Parameters including owner, api name, version, and options
448
+ * @returns API definition (OpenAPI/Swagger specification)
449
+ */
450
+ async getApiDefinition(params) {
451
+ const searchParams = new URLSearchParams();
452
+ if (params.resolved !== void 0)
453
+ searchParams.append("resolved", params.resolved.toString());
454
+ if (params.flatten !== void 0)
455
+ searchParams.append("flatten", params.flatten.toString());
456
+ const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
457
+ const response = await fetch(url, {
458
+ method: "GET",
459
+ headers: this.headers
460
+ });
461
+ if (!response.ok) {
462
+ throw new ToolError(
463
+ `SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`
464
+ );
465
+ }
466
+ const contentType = response.headers.get("content-type");
467
+ if (contentType?.includes("application/json")) {
468
+ return response.json();
469
+ } else {
470
+ return response.text();
471
+ }
472
+ }
473
+ /**
474
+ * Create or Update API in SwaggerHub Registry
475
+ * @param params Parameters for creating or updating the API including owner, name, version, specification, and definition
476
+ * @returns Created or updated API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
477
+ */
478
+ async createOrUpdateApi(params) {
479
+ let contentType;
480
+ let requestBody;
481
+ const format = this.detectDefinitionFormat(params.definition);
482
+ if (format === "yaml") {
483
+ contentType = "application/yaml";
484
+ requestBody = params.definition;
485
+ } else {
486
+ contentType = "application/json";
487
+ try {
488
+ const parsedDefinition = JSON.parse(params.definition);
489
+ requestBody = JSON.stringify(parsedDefinition);
490
+ } catch (error) {
491
+ throw new ToolError(
492
+ `Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`
493
+ );
494
+ }
495
+ }
496
+ const searchParams = new URLSearchParams();
497
+ searchParams.append("isPrivate", "true");
498
+ const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
499
+ params.owner
500
+ )}/${encodeURIComponent(params.apiName)}?${searchParams.toString()}`;
501
+ const response = await fetch(url, {
502
+ method: "POST",
503
+ headers: {
504
+ ...this.headers,
505
+ "Content-Type": contentType
506
+ },
507
+ body: requestBody
508
+ });
509
+ if (!response.ok) {
510
+ const errorText = await response.text().catch(() => "");
511
+ throw new ToolError(
512
+ `SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
513
+ );
514
+ }
515
+ const operation = response.status === 201 ? "create" : "update";
516
+ const version = response.headers.get("X-Version") || "1.0.0";
517
+ return {
518
+ owner: params.owner,
519
+ apiName: params.apiName,
520
+ version,
521
+ url: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/${version}`,
522
+ operation
523
+ };
524
+ }
525
+ /**
526
+ * Create API from Prompt using SmartBear AI
527
+ * @param params Parameters for creating API from prompt including owner, api name, prompt, and specification type
528
+ * @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 for update, HTTP 205 for reload
529
+ */
530
+ async createApiFromPrompt(params) {
531
+ const searchParams = new URLSearchParams();
532
+ const specType = params.specType ?? "openapi30x";
533
+ searchParams.append("specType", specType);
534
+ const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
535
+ params.owner
536
+ )}/${encodeURIComponent(params.apiName)}/.ai?${searchParams.toString()}`;
537
+ const response = await fetch(url, {
538
+ method: "POST",
539
+ headers: {
540
+ ...this.headers,
541
+ "Content-Type": "application/json"
542
+ },
543
+ body: JSON.stringify(params.prompt)
544
+ });
545
+ if (!response.ok) {
546
+ const errorText = await response.text().catch(() => "");
547
+ throw new ToolError(
548
+ `SwaggerHub Registry API createApiFromPrompt failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
549
+ );
550
+ }
551
+ const operation = response.status === 201 ? "create" : "update";
552
+ const version = response.headers.get("X-Version");
553
+ return {
554
+ owner: params.owner,
555
+ apiName: params.apiName,
556
+ specType,
557
+ version: version || void 0,
558
+ url: version ? `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/${version}` : `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}`,
559
+ operation
560
+ };
561
+ }
562
+ /**
563
+ * Auto-detect the format of an API definition string
564
+ * @param definition The API definition content
565
+ * @returns 'json' or 'yaml'
566
+ */
567
+ detectDefinitionFormat(definition) {
568
+ const trimmed = definition.trim();
569
+ if (!trimmed) {
570
+ throw new ToolError("Empty definition content provided");
571
+ }
572
+ try {
573
+ JSON.parse(trimmed);
574
+ return "json";
575
+ } catch {
576
+ return "yaml";
577
+ }
578
+ }
579
+ /**
580
+ * Run a standardization scan against an API definition
581
+ * @param params Parameters including organization name and API definition
582
+ * @returns Standardization result with validation errors
583
+ */
584
+ async scanStandardization(params) {
585
+ const format = this.detectDefinitionFormat(params.definition);
586
+ let contentType;
587
+ let requestBody;
588
+ if (format === "yaml") {
589
+ contentType = "application/yaml";
590
+ requestBody = params.definition;
591
+ } else {
592
+ contentType = "application/json";
593
+ try {
594
+ const parsedDefinition = JSON.parse(params.definition);
595
+ requestBody = JSON.stringify(parsedDefinition);
596
+ } catch (error) {
597
+ throw new ToolError(
598
+ `Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`
599
+ );
600
+ }
601
+ }
602
+ const url = `${this.config.registryBasePath}/standardization/${encodeURIComponent(
603
+ params.orgName
604
+ )}/scan`;
605
+ const response = await fetch(url, {
606
+ method: "POST",
607
+ headers: {
608
+ ...this.headers,
609
+ "Content-Type": contentType
610
+ },
611
+ body: requestBody
612
+ });
613
+ if (!response.ok) {
614
+ const errorText = await response.text().catch(() => "");
615
+ throw new ToolError(
616
+ `SwaggerHub Registry API scanStandardization failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
617
+ );
618
+ }
619
+ return await this.handleResponse(response);
620
+ }
621
+ /**
622
+ * Standardize and fix an API definition using AI
623
+ * @param params Parameters including owner, API name, and version
624
+ * @returns Standardization response with status and fixed definition
625
+ */
626
+ async standardizeApi(params) {
627
+ const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(
628
+ params.owner
629
+ )}/${encodeURIComponent(params.api)}/${encodeURIComponent(
630
+ params.version
631
+ )}/standardize`;
632
+ const response = await fetch(url, {
633
+ method: "POST",
634
+ headers: this.headers
635
+ });
636
+ if (!response.ok) {
637
+ const errorText = await response.text().catch(() => "");
638
+ throw new ToolError(
639
+ `SwaggerHub Registry API standardizeApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`
640
+ );
641
+ }
642
+ const result = await this.handleResponse(response);
643
+ if (!hasMessage(result)) {
644
+ throw new ToolError(
645
+ "Unexpected response format from standardizeApi endpoint"
646
+ );
81
647
  }
82
- /**
83
- * Handles HTTP responses with smart JSON parsing and fallback handling.
84
- * Includes HTTP error checking before parsing.
85
- * @template T - Expected response data type
86
- * @param response - The fetch Response object
87
- * @param defaultReturn - Default value to return for empty responses
88
- * @returns Parsed response data or fallback value
89
- */
90
- async handleResponse(response, defaultReturn = {}) {
91
- if (!response.ok) {
92
- throw new ToolError(`HTTP ${response.status}: ${response.statusText}`);
93
- }
94
- return this.parseResponse(response, defaultReturn);
95
- }
96
- async getPortals() {
97
- const response = await fetch(`${this.config.portalBasePath}/portals`, {
98
- method: "GET",
99
- headers: this.headers,
100
- });
101
- const result = await this.handleResponse(response, []);
102
- return result;
103
- }
104
- async getOrganizations(params) {
105
- // Build query string if parameters are provided
106
- const searchParams = new URLSearchParams();
107
- if (params?.q)
108
- searchParams.append("q", params.q);
109
- if (params?.sortBy)
110
- searchParams.append("sortBy", params.sortBy);
111
- if (params?.order)
112
- searchParams.append("order", params.order);
113
- if (params?.page !== undefined)
114
- searchParams.append("page", params.page.toString());
115
- if (params?.pageSize)
116
- searchParams.append("pageSize", params.pageSize.toString());
117
- const queryString = searchParams.toString();
118
- const url = `${this.config.userManagementBasePath}/orgs${queryString ? `?${queryString}` : ""}`;
119
- const response = await fetch(url, {
120
- method: "GET",
121
- headers: this.headers,
122
- });
123
- const defaultResponse = {
124
- items: [],
125
- totalCount: 0,
126
- pageSize: 50,
127
- page: 0,
128
- };
129
- const result = await this.handleResponse(response, defaultResponse);
130
- return result;
131
- }
132
- async createPortal(body) {
133
- const response = await fetch(`${this.config.portalBasePath}/portals`, {
134
- method: "POST",
135
- headers: this.headers,
136
- body: JSON.stringify(body),
137
- });
138
- const result = await this.handleResponse(response);
139
- if (!hasId(result)) {
140
- throw new Error("Unexpected empty response creating portal");
141
- }
142
- return result;
143
- }
144
- async getPortal(portalId) {
145
- const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
146
- method: "GET",
147
- headers: this.headers,
148
- });
149
- const result = await this.handleResponse(response);
150
- if (!hasId(result)) {
151
- throw new ToolError("Portal not found or empty response");
152
- }
153
- return result;
154
- }
155
- async updatePortal(portalId, body) {
156
- const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
157
- method: "PATCH",
158
- headers: this.headers,
159
- body: JSON.stringify(body),
160
- });
161
- return this.handleResponse(response);
162
- }
163
- async getPortalProducts(portalId) {
164
- const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
165
- method: "GET",
166
- headers: this.headers,
167
- });
168
- const result = await this.handleResponse(response, []);
169
- return result;
170
- }
171
- async createPortalProduct(portalId, body) {
172
- const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
173
- method: "POST",
174
- headers: this.headers,
175
- body: JSON.stringify(body),
176
- });
177
- const result = await this.handleResponse(response);
178
- if (!hasId(result)) {
179
- throw new Error("Unexpected empty response creating product");
180
- }
181
- return result;
182
- }
183
- async getPortalProduct(productId) {
184
- const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
185
- method: "GET",
186
- headers: this.headers,
187
- });
188
- const result = await this.handleResponse(response);
189
- if (!hasId(result)) {
190
- throw new ToolError("Product not found or empty response");
191
- }
192
- return result;
193
- }
194
- async deletePortalProduct(productId) {
195
- const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
196
- method: "DELETE",
197
- headers: this.headers,
198
- });
199
- return this.handleResponse(response);
200
- }
201
- async updatePortalProduct(productId, body) {
202
- const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
203
- method: "PATCH",
204
- headers: this.headers,
205
- body: JSON.stringify(body),
206
- });
207
- return this.handleResponse(response, {
208
- success: true,
209
- });
210
- }
211
- async publishPortalProduct(productId, preview = false) {
212
- const response = await fetch(`${this.config.portalBasePath}/products/${productId}/published-content?preview=${preview}`, {
213
- method: "PUT",
214
- headers: this.headers,
215
- });
216
- return this.handleResponse(response, {
217
- success: true,
218
- });
219
- }
220
- async getPortalProductSections(productId, params) {
221
- const queryParameters = new URLSearchParams();
222
- if (params.embed) {
223
- for (const item of params.embed) {
224
- queryParameters.append("embed", item);
225
- }
226
- }
227
- if (params.page !== undefined) {
228
- queryParameters.append("page", params.page.toString());
229
- }
230
- if (params.size !== undefined) {
231
- queryParameters.append("size", params.size.toString());
232
- }
233
- const url = `${this.config.portalBasePath}/products/${productId}/sections${queryParameters.toString() ? `?${queryParameters.toString()}` : ""}`;
234
- const response = await fetch(url, {
235
- method: "GET",
236
- headers: this.headers,
237
- });
238
- const result = await this.handleResponse(response, []);
239
- return result;
240
- }
241
- /**
242
- * Create a new table of contents item in a portal product section
243
- * @param sectionId - Section ID where the table of contents item will be created
244
- * @param body - Table of contents creation parameters
245
- * @returns Created table of contents item with metadata
246
- */
247
- async createTableOfContents(sectionId, body) {
248
- const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents`;
249
- const response = await fetch(url, {
250
- method: "POST",
251
- headers: this.headers,
252
- body: JSON.stringify(body),
253
- });
254
- if (!response.ok) {
255
- const errorText = await response.text();
256
- throw new Error(`API Hub createTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
257
- }
258
- const result = await response.json();
259
- return result;
260
- }
261
- /**
262
- * Get table of contents for a section
263
- * @param args - Parameters for retrieving table of contents
264
- * @returns List of table of contents items
265
- */
266
- async getTableOfContents(args) {
267
- const { sectionId, embed, page, size } = args;
268
- const searchParams = new URLSearchParams();
269
- if (embed) {
270
- for (const item of embed) {
271
- searchParams.append("embed", item);
272
- }
273
- }
274
- if (page !== undefined) {
275
- searchParams.set("page", page.toString());
276
- }
277
- if (size !== undefined) {
278
- searchParams.set("size", size.toString());
279
- }
280
- const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
281
- const response = await fetch(url, {
282
- method: "GET",
283
- headers: this.headers,
284
- });
285
- if (!response.ok) {
286
- const errorText = await response.text();
287
- throw new Error(`API Hub getTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
288
- }
289
- const result = await response.json();
290
- // The API returns a paginated response, so we extract the items array
291
- return result.items;
292
- }
293
- /**
294
- * Get document content and metadata
295
- * @param args - Parameters for retrieving document
296
- * @returns Document with content and metadata
297
- */
298
- async getDocument(args) {
299
- const { documentId } = args;
300
- const url = `${this.config.portalBasePath}/documents/${documentId}`;
301
- const response = await fetch(url, {
302
- method: "GET",
303
- headers: this.headers,
304
- });
305
- if (!response.ok) {
306
- const errorText = await response.text();
307
- throw new Error(`API Hub getDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
308
- }
309
- const result = await response.json();
310
- return result;
311
- }
312
- /**
313
- * Update document content
314
- * @param args - Parameters for updating document
315
- * @returns Success response
316
- */
317
- async updateDocument(args) {
318
- const { documentId, ...body } = args;
319
- const url = `${this.config.portalBasePath}/documents/${documentId}`;
320
- const response = await fetch(url, {
321
- method: "PATCH",
322
- headers: this.headers,
323
- body: JSON.stringify(body),
324
- });
325
- if (!response.ok) {
326
- const errorText = await response.text();
327
- throw new Error(`API Hub updateDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
328
- }
329
- return { success: true };
330
- }
331
- /**
332
- * Delete table of contents entry
333
- * @param args - Parameters for deleting table of contents entry
334
- * @returns Success response
335
- */
336
- async deleteTableOfContents(args) {
337
- const { tableOfContentsId, recursive } = args;
338
- const searchParams = new URLSearchParams();
339
- if (recursive !== undefined) {
340
- searchParams.set("recursive", recursive.toString());
341
- }
342
- const url = `${this.config.portalBasePath}/table-of-contents/${tableOfContentsId}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
343
- const response = await fetch(url, {
344
- method: "DELETE",
345
- headers: this.headers,
346
- });
347
- if (!response.ok) {
348
- const errorText = await response.text();
349
- throw new Error(`API Hub deleteTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
350
- }
351
- return { success: true };
352
- }
353
- /**
354
- * Helper method for handling responses when error checking is already done.
355
- * Delegates to parseResponse for the actual parsing logic.
356
- * @template T - Expected response data type
357
- * @template D - Default return type
358
- * @param response - The fetch Response object
359
- * @param defaultReturn - Default value to return for empty responses
360
- * @returns Parsed response data or fallback value
361
- */
362
- // Registry API methods for SwaggerHub Design functionality
363
- /**
364
- * Search APIs and Domains in SwaggerHub Registry using /specs endpoint
365
- * @param params Search parameters
366
- * @returns Array of processed API metadata
367
- */
368
- async searchApis(params = {}) {
369
- const searchParams = new URLSearchParams();
370
- if (params.query)
371
- searchParams.append("query", params.query);
372
- if (params.state)
373
- searchParams.append("state", params.state);
374
- if (params.tag)
375
- searchParams.append("tag", params.tag);
376
- if (params.offset !== undefined)
377
- searchParams.append("offset", params.offset.toString());
378
- if (params.limit !== undefined)
379
- searchParams.append("limit", params.limit.toString());
380
- if (params.sort)
381
- searchParams.append("sort", params.sort);
382
- if (params.order)
383
- searchParams.append("order", params.order);
384
- if (params.owner)
385
- searchParams.append("owner", params.owner);
386
- if (params.specType)
387
- searchParams.append("specType", params.specType);
388
- const url = `${this.config.registryBasePath}/specs${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
389
- const response = await fetch(url, {
390
- method: "GET",
391
- headers: this.headers,
392
- });
393
- if (!response.ok) {
394
- throw new ToolError(`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`);
395
- }
396
- const apisJsonResponse = (await response.json());
397
- // Transform APIs.json response to our ApiMetadata format
398
- return this.transformApisJsonToMetadata(apisJsonResponse.apis);
399
- }
400
- /**
401
- * Transform APIs.json specifications to our ApiMetadata format
402
- * @param specs Array of API specifications from APIs.json
403
- * @returns Array of processed API metadata
404
- */
405
- transformApisJsonToMetadata(specs) {
406
- return specs.map((spec) => {
407
- // Extract useful properties from the properties array
408
- const properties = spec.properties || [];
409
- const getProperty = (type) => {
410
- const property = properties.find((p) => p.type === type);
411
- return property?.value || property?.url;
412
- };
413
- // Extract owner, name, and version from the Swagger URL using the regex constant
414
- const swaggerUrl = getProperty("Swagger") || "";
415
- const urlMatch = RegExp(SWAGGER_URL_REGEX).exec(swaggerUrl);
416
- return {
417
- owner: urlMatch?.[2] || "",
418
- name: spec.name || "",
419
- description: spec.description || "",
420
- summary: spec.summary || "",
421
- version: getProperty("X-Version") || urlMatch?.[4] || "",
422
- specification: getProperty("X-Specification") || "",
423
- created: getProperty("X-Created"),
424
- modified: getProperty("X-Modified"),
425
- published: getProperty("X-Published"),
426
- private: getProperty("X-Private"),
427
- oasVersion: getProperty("X-OASVersion"),
428
- url: swaggerUrl,
429
- };
430
- });
431
- }
432
- /**
433
- * Get API definition from SwaggerHub Registry
434
- * @param params Parameters including owner, api name, version, and options
435
- * @returns API definition (OpenAPI/Swagger specification)
436
- */
437
- async getApiDefinition(params) {
438
- const searchParams = new URLSearchParams();
439
- if (params.resolved !== undefined)
440
- searchParams.append("resolved", params.resolved.toString());
441
- if (params.flatten !== undefined)
442
- searchParams.append("flatten", params.flatten.toString());
443
- const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
444
- const response = await fetch(url, {
445
- method: "GET",
446
- headers: this.headers,
447
- });
448
- if (!response.ok) {
449
- throw new ToolError(`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`);
450
- }
451
- // Return the raw API definition (could be JSON or YAML)
452
- const contentType = response.headers.get("content-type");
453
- if (contentType?.includes("application/json")) {
454
- return response.json();
455
- }
456
- else {
457
- return response.text();
458
- }
459
- }
460
- /**
461
- * Create or Update API in SwaggerHub Registry
462
- * @param params Parameters for creating or updating the API including owner, name, version, specification, and definition
463
- * @returns Created or updated API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
464
- */
465
- async createOrUpdateApi(params) {
466
- // Determine the format of the definition
467
- let contentType;
468
- let requestBody;
469
- // Auto-detect format from the definition content
470
- const format = this.detectDefinitionFormat(params.definition);
471
- if (format === "yaml") {
472
- contentType = "application/yaml";
473
- requestBody = params.definition; // Send YAML as-is
474
- }
475
- else {
476
- contentType = "application/json";
477
- // For JSON, parse and stringify to ensure valid JSON
478
- try {
479
- const parsedDefinition = JSON.parse(params.definition);
480
- requestBody = JSON.stringify(parsedDefinition);
481
- }
482
- catch (error) {
483
- throw new ToolError(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
484
- }
485
- }
486
- // Construct the URL with query parameters
487
- // Fixed values: visibility=private, automock=false, version=1.0.0
488
- const searchParams = new URLSearchParams();
489
- searchParams.append("isPrivate", "true");
490
- const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}?${searchParams.toString()}`;
491
- // Use POST method with the appropriate content type
492
- const response = await fetch(url, {
493
- method: "POST",
494
- headers: {
495
- ...this.headers,
496
- "Content-Type": contentType,
497
- },
498
- body: requestBody,
499
- });
500
- if (!response.ok) {
501
- const errorText = await response.text().catch(() => "");
502
- throw new ToolError(`SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
503
- }
504
- // Determine operation type based on HTTP status code
505
- const operation = response.status === 201 ? "create" : "update";
506
- // Return formatted response with the required fields
507
- // Fixed version is always 1.0.0
508
- return {
509
- owner: params.owner,
510
- apiName: params.apiName,
511
- version: "1.0.0",
512
- url: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/1.0.0`,
513
- operation,
514
- };
515
- }
516
- /**
517
- * Create API from Template in SwaggerHub Registry
518
- * @param params Parameters for creating API from template including owner, api name, and template
519
- * @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
520
- */
521
- async createApiFromTemplate(params) {
522
- // Construct the URL with query parameters
523
- // Fixed values: visibility=private, no project, noReconcile=false
524
- const searchParams = new URLSearchParams();
525
- searchParams.append("isPrivate", "true");
526
- searchParams.append("template", params.template);
527
- const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}/.template?${searchParams.toString()}`;
528
- // Use POST method for template creation
529
- const response = await fetch(url, {
530
- method: "POST",
531
- headers: this.headers,
532
- });
533
- if (!response.ok) {
534
- const errorText = await response.text().catch(() => "");
535
- throw new ToolError(`SwaggerHub Registry API createApiFromTemplate failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
536
- }
537
- // Determine operation type based on HTTP status code
538
- const operation = response.status === 201 ? "create" : "update";
539
- // Return formatted response with the required fields
540
- return {
541
- owner: params.owner,
542
- apiName: params.apiName,
543
- template: params.template,
544
- url: `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}`,
545
- operation,
546
- };
547
- }
548
- /**
549
- * Create API from Prompt using SmartBear AI
550
- * @param params Parameters for creating API from prompt including owner, api name, prompt, and specification type
551
- * @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 for update, HTTP 205 for reload
552
- */
553
- async createApiFromPrompt(params) {
554
- // Construct the URL with query parameters
555
- const searchParams = new URLSearchParams();
556
- const specType = params.specType ?? "openapi30x";
557
- searchParams.append("specType", specType);
558
- const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}/.ai?${searchParams.toString()}`;
559
- // Use POST method with JSON body containing the prompt
560
- const response = await fetch(url, {
561
- method: "POST",
562
- headers: {
563
- ...this.headers,
564
- "Content-Type": "application/json",
565
- },
566
- body: JSON.stringify(params.prompt),
567
- });
568
- if (!response.ok) {
569
- const errorText = await response.text().catch(() => "");
570
- throw new ToolError(`SwaggerHub Registry API createApiFromPrompt failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
571
- }
572
- // Determine operation type based on HTTP status code
573
- // 201 = new API created, 200 = existing API updated, 205 = API saved and should be reloaded
574
- const operation = response.status === 201 ? "create" : "update";
575
- // Extract version from X-Version header
576
- const version = response.headers.get("X-Version");
577
- // Return formatted response with the required fields
578
- return {
579
- owner: params.owner,
580
- apiName: params.apiName,
581
- specType: specType,
582
- version: version || undefined,
583
- url: version
584
- ? `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}/${version}`
585
- : `${this.config.uiBasePath}/apis/${params.owner}/${params.apiName}`,
586
- operation,
587
- };
588
- }
589
- /**
590
- * Auto-detect the format of an API definition string
591
- * @param definition The API definition content
592
- * @returns 'json' or 'yaml'
593
- */
594
- detectDefinitionFormat(definition) {
595
- const trimmed = definition.trim();
596
- if (!trimmed) {
597
- throw new ToolError("Empty definition content provided");
598
- }
599
- try {
600
- JSON.parse(trimmed);
601
- return "json";
602
- }
603
- catch {
604
- return "yaml";
605
- }
606
- }
607
- /**
608
- * Run a standardization scan against an API definition
609
- * @param params Parameters including organization name and API definition
610
- * @returns Standardization result with validation errors
611
- */
612
- async scanStandardization(params) {
613
- // Auto-detect format from the definition content
614
- const format = this.detectDefinitionFormat(params.definition);
615
- let contentType;
616
- let requestBody;
617
- if (format === "yaml") {
618
- contentType = "application/yaml";
619
- requestBody = params.definition; // Send YAML as-is
620
- }
621
- else {
622
- contentType = "application/json";
623
- // For JSON, parse and stringify to ensure valid JSON
624
- try {
625
- const parsedDefinition = JSON.parse(params.definition);
626
- requestBody = JSON.stringify(parsedDefinition);
627
- }
628
- catch (error) {
629
- throw new ToolError(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
630
- }
631
- }
632
- const url = `${this.config.registryBasePath}/standardization/${encodeURIComponent(params.orgName)}/scan`;
633
- const response = await fetch(url, {
634
- method: "POST",
635
- headers: {
636
- ...this.headers,
637
- "Content-Type": contentType,
638
- },
639
- body: requestBody,
640
- });
641
- if (!response.ok) {
642
- const errorText = await response.text().catch(() => "");
643
- throw new ToolError(`SwaggerHub Registry API scanStandardization failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
644
- }
645
- return await this.handleResponse(response);
646
- }
647
- /**
648
- * Standardize and fix an API definition using AI
649
- * @param params Parameters including owner, API name, and version
650
- * @returns Standardization response with status and fixed definition
651
- */
652
- async standardizeApi(params) {
653
- const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}/standardize`;
654
- const response = await fetch(url, {
655
- method: "POST",
656
- headers: this.headers,
657
- });
658
- if (!response.ok) {
659
- const errorText = await response.text().catch(() => "");
660
- throw new ToolError(`SwaggerHub Registry API standardizeApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
661
- }
662
- const result = await this.handleResponse(response);
663
- // Validate that we have the expected response structure
664
- if (!hasMessage(result)) {
665
- throw new ToolError("Unexpected response format from standardizeApi endpoint");
666
- }
667
- // If errorsFound is not present, default to 0 (no errors found)
668
- if (!hasErrorsFound(result)) {
669
- return { ...result, errorsFound: 0 };
670
- }
671
- return result;
648
+ if (!hasErrorsFound(result)) {
649
+ return { ...result, errorsFound: 0 };
672
650
  }
651
+ return result;
652
+ }
673
653
  }
654
+ export {
655
+ SwaggerAPI
656
+ };