@smartbear/mcp 0.8.0 → 0.10.0

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 (66) hide show
  1. package/README.md +29 -4
  2. package/dist/api-hub/client/api.js +239 -10
  3. package/dist/api-hub/client/configuration.js +5 -0
  4. package/dist/api-hub/client/index.js +1 -0
  5. package/dist/api-hub/client/portal-types.js +126 -0
  6. package/dist/api-hub/client/registry-types.js +8 -0
  7. package/dist/api-hub/client/tools.js +78 -15
  8. package/dist/api-hub/client/user-management-types.js +24 -0
  9. package/dist/api-hub/client.js +34 -0
  10. package/dist/bugsnag/client/api/CurrentUser.js +12 -49
  11. package/dist/bugsnag/client/api/Error.js +29 -142
  12. package/dist/bugsnag/client/api/Project.js +52 -113
  13. package/dist/bugsnag/client/api/api.js +3743 -0
  14. package/dist/bugsnag/client/api/base.js +97 -34
  15. package/dist/bugsnag/client/api/configuration.js +26 -0
  16. package/dist/bugsnag/client/api/index.js +2 -0
  17. package/dist/bugsnag/client/filters.js +28 -0
  18. package/dist/bugsnag/client.js +104 -155
  19. package/dist/collaborator/client.js +364 -0
  20. package/dist/common/server.js +73 -23
  21. package/dist/common/types.js +6 -1
  22. package/dist/index.js +9 -1
  23. package/dist/pactflow/client/prompt-utils.js +2 -1
  24. package/dist/pactflow/client/tools.js +4 -4
  25. package/dist/pactflow/client/utils.js +5 -4
  26. package/dist/pactflow/client.js +10 -9
  27. package/dist/qmetry/client/api/client-api.js +21 -16
  28. package/dist/qmetry/client/api/error-handler.js +329 -0
  29. package/dist/qmetry/client/auto-resolve.js +96 -0
  30. package/dist/qmetry/client/handlers.js +33 -2
  31. package/dist/qmetry/client/issues.js +123 -0
  32. package/dist/qmetry/client/project.js +73 -0
  33. package/dist/qmetry/client/requirement.js +76 -0
  34. package/dist/qmetry/client/testcase.js +122 -6
  35. package/dist/qmetry/client/testsuite.js +272 -0
  36. package/dist/qmetry/client/tools/index.js +17 -0
  37. package/dist/qmetry/client/tools/issue-tools.js +545 -0
  38. package/dist/qmetry/client/tools/project-tools.js +348 -0
  39. package/dist/qmetry/client/tools/requirement-tools.js +530 -0
  40. package/dist/qmetry/client/tools/testcase-tools.js +526 -0
  41. package/dist/qmetry/client/tools/testsuite-tools.js +772 -0
  42. package/dist/qmetry/client/tools/types.js +1 -0
  43. package/dist/qmetry/client/utils.js +16 -0
  44. package/dist/qmetry/client.js +27 -17
  45. package/dist/qmetry/config/constants.js +28 -0
  46. package/dist/qmetry/config/rest-endpoints.js +30 -0
  47. package/dist/qmetry/types/common.js +599 -9
  48. package/dist/qmetry/types/issues.js +16 -0
  49. package/dist/qmetry/types/project.js +17 -0
  50. package/dist/qmetry/types/requirements.js +19 -0
  51. package/dist/qmetry/types/testcase.js +20 -0
  52. package/dist/qmetry/types/testsuite.js +44 -0
  53. package/dist/reflect/client.js +7 -6
  54. package/dist/zephyr/client.js +7 -1
  55. package/dist/zephyr/common/api-client.js +8 -0
  56. package/dist/zephyr/common/auth-service.js +1 -0
  57. package/dist/zephyr/common/rest-api-schemas.js +5173 -0
  58. package/dist/zephyr/tool/project/get-project.js +39 -0
  59. package/dist/zephyr/tool/project/get-projects.js +7 -13
  60. package/dist/zephyr/tool/test-cycle/get-test-cycles.js +72 -0
  61. package/package.json +1 -1
  62. package/dist/bugsnag/client/api/filters.js +0 -167
  63. package/dist/bugsnag/client/configuration.js +0 -10
  64. package/dist/bugsnag/client/index.js +0 -2
  65. package/dist/qmetry/client/tools.js +0 -222
  66. package/dist/zephyr/common/types.js +0 -35
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  <!-- Badges -->
11
11
  <div>
12
- <a href="https://github.com/SmartBear/smartbear-mcp/actions/workflows/node-ci.yml"><img src="https://github.com/SmartBear/smartbear-mcp/actions/workflows/node-ci.yml/badge.svg?branch=next" alt="Test Status"></a>
12
+ <a href="https://github.com/SmartBear/smartbear-mcp/actions/workflows/test.yml"><img src="https://github.com/SmartBear/smartbear-mcp/actions/workflows/test.yml/badge.svg?branch=main" alt="Test Status"></a>
13
13
  <a href="https://smartbear.github.io/smartbear-mcp/"><img src="https://img.shields.io/badge/coverage-dynamic-brightgreen" alt="Coverage"></a>
14
14
  <a href="https://www.npmjs.com/package/@smartbear/mcp"><img src="https://img.shields.io/npm/v/@smartbear/mcp" alt="npm version"></a>
15
15
  <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Compatible-blue" alt="MCP Compatible"></a>
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  <br />
20
20
 
21
- A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [API Hub](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/), [Pact Broker](https://docs.pact.io/), [QMetry](https://www.qmetry.com/), and [Zephyr](https://smartbear.com/test-management/zephyr/).
21
+ A Model Context Protocol (MCP) server which provides AI assistants with seamless access to SmartBear's suite of testing and monitoring tools, including [BugSnag](https://www.bugsnag.com/), [Reflect](https://reflect.run), [API Hub](https://www.smartbear.com/api-hub), [PactFlow](https://pactflow.io/), [Pact Broker](https://docs.pact.io/), [QMetry](https://www.qmetry.com/), [Zephyr](https://smartbear.com/test-management/zephyr/) and [Collaborator](https://smartbear.com/product/collaborator/).
22
22
 
23
23
  ## What is MCP?
24
24
 
@@ -34,6 +34,7 @@ See individual guides for suggested prompts and supported tools and resources:
34
34
  - [PactFlow](https://developer.smartbear.com/pactflow/default/getting-started) - Contract testing capabilities
35
35
  - [QMetry](https://developer.smartbear.com/smartbear-mcp/docs/qmetry-integration) - QMetry Test Management capabilities
36
36
  - [Zephyr](https://developer.smartbear.com/smartbear-mcp/docs/zephyr-integration) - Zephyr Test Management capabilities
37
+ - [Collaborator](https://developer.smartbear.com/smartbear-mcp/docs/collaborator-integration) - Review and Remote System Configuration management capabilities
37
38
 
38
39
 
39
40
  ## Prerequisites
@@ -79,7 +80,10 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
79
80
  "QMETRY_API_KEY": "${input:qmetry_api_key}",
80
81
  "QMETRY_BASE_URL": "${input:qmetry_base_url}",
81
82
  "ZEPHYR_API_TOKEN": "${input:zephyr_api_token}",
82
- "ZEPHYR_BASE_URL": "${input:zephyr_base_url}"
83
+ "ZEPHYR_BASE_URL": "${input:zephyr_base_url}",
84
+ "COLLAB_BASE_URL": "${input:collab_base_url}",
85
+ "COLLAB_USERNAME": "${input:collab_username}",
86
+ "COLLAB_LOGIN_TICKET": "${input:collab_login_ticket}"
83
87
  }
84
88
  }
85
89
  },
@@ -155,6 +159,24 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
155
159
  "type": "promptString",
156
160
  "description": "Zephyr API base URL. By default, connects to https://api.zephyrscale.smartbear.com/v2. Change to region-specific endpoint if needed.",
157
161
  "password": false
162
+ },
163
+ {
164
+ "id": "collab_base_url",
165
+ "type": "promptString",
166
+ "description": "Collab base url",
167
+ "password": true
168
+ },
169
+ {
170
+ "id": "collab_username",
171
+ "type": "promptString",
172
+ "description": "Collab username",
173
+ "password": true
174
+ },
175
+ {
176
+ "id": "collab_login_ticket",
177
+ "type": "promptString",
178
+ "description": "Collab login ticket",
179
+ "password": true
158
180
  }
159
181
  ]
160
182
  }
@@ -186,7 +208,10 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
186
208
  "QMETRY_API_KEY": "your_qmetry_api_key",
187
209
  "QMETRY_BASE_URL": "https://testmanagement.qmetry.com",
188
210
  "ZEPHYR_API_TOKEN": "your_zephyr_api_token",
189
- "ZEPHYR_BASE_URL": "https://api.zephyrscale.smartbear.com/v2"
211
+ "ZEPHYR_BASE_URL": "https://api.zephyrscale.smartbear.com/v2",
212
+ "COLLAB_BASE_URL": "your collab base url",
213
+ "COLLAB_USERNAME": "your collab user name",
214
+ "COLLAB_LOGIN_TICKET": "your collab login ticket"
190
215
  }
191
216
  }
192
217
  }
@@ -1,3 +1,4 @@
1
+ import { ToolError } from "../../common/types.js";
1
2
  // Regex to extract owner, name, and version from SwaggerHub URLs.
2
3
  // Matches /apis/owner/name/version, /domains/owner/name/version, or /templates/owner/name/version
3
4
  // Example: /apis/acme/petstore/1.0.0
@@ -70,7 +71,7 @@ export class ApiHubAPI {
70
71
  */
71
72
  async handleResponse(response, defaultReturn = {}) {
72
73
  if (!response.ok) {
73
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
74
+ throw new ToolError(`HTTP ${response.status}: ${response.statusText}`);
74
75
  }
75
76
  return this.parseResponse(response, defaultReturn);
76
77
  }
@@ -82,6 +83,34 @@ export class ApiHubAPI {
82
83
  const result = await this.handleResponse(response, []);
83
84
  return result;
84
85
  }
86
+ async getOrganizations(params) {
87
+ // Build query string if parameters are provided
88
+ const searchParams = new URLSearchParams();
89
+ if (params?.q)
90
+ searchParams.append("q", params.q);
91
+ if (params?.sortBy)
92
+ searchParams.append("sortBy", params.sortBy);
93
+ if (params?.order)
94
+ searchParams.append("order", params.order);
95
+ if (params?.page !== undefined)
96
+ searchParams.append("page", params.page.toString());
97
+ if (params?.pageSize)
98
+ searchParams.append("pageSize", params.pageSize.toString());
99
+ const queryString = searchParams.toString();
100
+ const url = `${this.config.userManagementBasePath}/orgs${queryString ? `?${queryString}` : ""}`;
101
+ const response = await fetch(url, {
102
+ method: "GET",
103
+ headers: this.headers,
104
+ });
105
+ const defaultResponse = {
106
+ items: [],
107
+ totalCount: 0,
108
+ pageSize: 50,
109
+ page: 0,
110
+ };
111
+ const result = await this.handleResponse(response, defaultResponse);
112
+ return result;
113
+ }
85
114
  async createPortal(body) {
86
115
  const response = await fetch(`${this.config.portalBasePath}/portals`, {
87
116
  method: "POST",
@@ -101,7 +130,7 @@ export class ApiHubAPI {
101
130
  });
102
131
  const result = await this.handleResponse(response);
103
132
  if (!("id" in result)) {
104
- throw new Error("Portal not found or empty response");
133
+ throw new ToolError("Portal not found or empty response");
105
134
  }
106
135
  return result;
107
136
  }
@@ -112,7 +141,7 @@ export class ApiHubAPI {
112
141
  });
113
142
  if (!response.ok) {
114
143
  const errorText = await response.text().catch(() => "");
115
- throw new Error(`API Hub deletePortal failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
144
+ throw new ToolError(`API Hub deletePortal failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
116
145
  }
117
146
  }
118
147
  async updatePortal(portalId, body) {
@@ -150,7 +179,7 @@ export class ApiHubAPI {
150
179
  });
151
180
  const result = await this.handleResponse(response);
152
181
  if (!("id" in result)) {
153
- throw new Error("Product not found or empty response");
182
+ throw new ToolError("Product not found or empty response");
154
183
  }
155
184
  return result;
156
185
  }
@@ -171,6 +200,166 @@ export class ApiHubAPI {
171
200
  success: true,
172
201
  });
173
202
  }
203
+ async publishPortalProduct(productId, preview = false) {
204
+ const response = await fetch(`${this.config.portalBasePath}/products/${productId}/published-content?preview=${preview}`, {
205
+ method: "PUT",
206
+ headers: this.headers,
207
+ });
208
+ return this.handleResponse(response, {
209
+ success: true,
210
+ });
211
+ }
212
+ async getPortalProductSections(productId, params) {
213
+ const queryParameters = new URLSearchParams();
214
+ if (params.embed) {
215
+ for (const item of params.embed) {
216
+ queryParameters.append("embed", item);
217
+ }
218
+ }
219
+ if (params.page !== undefined) {
220
+ queryParameters.append("page", params.page.toString());
221
+ }
222
+ if (params.size !== undefined) {
223
+ queryParameters.append("size", params.size.toString());
224
+ }
225
+ const url = `${this.config.portalBasePath}/products/${productId}/sections${queryParameters.toString() ? `?${queryParameters.toString()}` : ""}`;
226
+ const response = await fetch(url, {
227
+ method: "GET",
228
+ headers: this.headers,
229
+ });
230
+ const result = await this.handleResponse(response, []);
231
+ return result;
232
+ }
233
+ /**
234
+ * Create a new table of contents item in a portal product section
235
+ * @param sectionId - Section ID where the table of contents item will be created
236
+ * @param body - Table of contents creation parameters
237
+ * @returns Created table of contents item with metadata
238
+ */
239
+ async createTableOfContents(sectionId, body) {
240
+ const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents`;
241
+ const response = await fetch(url, {
242
+ method: "POST",
243
+ headers: this.headers,
244
+ body: JSON.stringify(body),
245
+ });
246
+ if (!response.ok) {
247
+ const errorText = await response.text();
248
+ throw new Error(`API Hub createTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
249
+ }
250
+ const result = await response.json();
251
+ return result;
252
+ }
253
+ /**
254
+ * Get table of contents for a section
255
+ * @param args - Parameters for retrieving table of contents
256
+ * @returns List of table of contents items
257
+ */
258
+ async getTableOfContents(args) {
259
+ const { sectionId, embed, page, size } = args;
260
+ const searchParams = new URLSearchParams();
261
+ if (embed) {
262
+ for (const item of embed) {
263
+ searchParams.append("embed", item);
264
+ }
265
+ }
266
+ if (page !== undefined) {
267
+ searchParams.set("page", page.toString());
268
+ }
269
+ if (size !== undefined) {
270
+ searchParams.set("size", size.toString());
271
+ }
272
+ const url = `${this.config.portalBasePath}/sections/${sectionId}/table-of-contents${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
273
+ const response = await fetch(url, {
274
+ method: "GET",
275
+ headers: this.headers,
276
+ });
277
+ if (!response.ok) {
278
+ const errorText = await response.text();
279
+ throw new Error(`API Hub getTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
280
+ }
281
+ const result = await response.json();
282
+ // The API returns a paginated response, so we extract the items array
283
+ return result.items;
284
+ }
285
+ /**
286
+ * Get document content and metadata
287
+ * @param args - Parameters for retrieving document
288
+ * @returns Document with content and metadata
289
+ */
290
+ async getDocument(args) {
291
+ const { documentId } = args;
292
+ const url = `${this.config.portalBasePath}/documents/${documentId}`;
293
+ const response = await fetch(url, {
294
+ method: "GET",
295
+ headers: this.headers,
296
+ });
297
+ if (!response.ok) {
298
+ const errorText = await response.text();
299
+ throw new Error(`API Hub getDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
300
+ }
301
+ const result = await response.json();
302
+ return result;
303
+ }
304
+ /**
305
+ * Update document content
306
+ * @param args - Parameters for updating document
307
+ * @returns Success response
308
+ */
309
+ async updateDocument(args) {
310
+ const { documentId, ...body } = args;
311
+ const url = `${this.config.portalBasePath}/documents/${documentId}`;
312
+ const response = await fetch(url, {
313
+ method: "PATCH",
314
+ headers: this.headers,
315
+ body: JSON.stringify(body),
316
+ });
317
+ if (!response.ok) {
318
+ const errorText = await response.text();
319
+ throw new Error(`API Hub updateDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
320
+ }
321
+ return { success: true };
322
+ }
323
+ /**
324
+ * Delete document
325
+ * @param args - Parameters for deleting document
326
+ * @returns Success response
327
+ */
328
+ async deleteDocument(args) {
329
+ const { documentId } = args;
330
+ const url = `${this.config.portalBasePath}/documents/${documentId}`;
331
+ const response = await fetch(url, {
332
+ method: "DELETE",
333
+ headers: this.headers,
334
+ });
335
+ if (!response.ok) {
336
+ const errorText = await response.text();
337
+ throw new Error(`API Hub deleteDocument failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
338
+ }
339
+ return { success: true };
340
+ }
341
+ /**
342
+ * Delete table of contents entry
343
+ * @param args - Parameters for deleting table of contents entry
344
+ * @returns Success response
345
+ */
346
+ async deleteTableOfContents(args) {
347
+ const { tableOfContentsId, recursive } = args;
348
+ const searchParams = new URLSearchParams();
349
+ if (recursive !== undefined) {
350
+ searchParams.set("recursive", recursive.toString());
351
+ }
352
+ const url = `${this.config.portalBasePath}/table-of-contents/${tableOfContentsId}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
353
+ const response = await fetch(url, {
354
+ method: "DELETE",
355
+ headers: this.headers,
356
+ });
357
+ if (!response.ok) {
358
+ const errorText = await response.text();
359
+ throw new Error(`API Hub deleteTableOfContents failed - status: ${response.status} ${response.statusText}. Response: ${errorText}`);
360
+ }
361
+ return { success: true };
362
+ }
174
363
  /**
175
364
  * Helper method for handling responses when error checking is already done.
176
365
  * Delegates to parseResponse for the actual parsing logic.
@@ -212,7 +401,7 @@ export class ApiHubAPI {
212
401
  headers: this.headers,
213
402
  });
214
403
  if (!response.ok) {
215
- throw new Error(`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`);
404
+ throw new ToolError(`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`);
216
405
  }
217
406
  const apisJsonResponse = (await response.json());
218
407
  // Transform APIs.json response to our ApiMetadata format
@@ -267,7 +456,7 @@ export class ApiHubAPI {
267
456
  headers: this.headers,
268
457
  });
269
458
  if (!response.ok) {
270
- throw new Error(`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`);
459
+ throw new ToolError(`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`);
271
460
  }
272
461
  // Return the raw API definition (could be JSON or YAML)
273
462
  const contentType = response.headers.get("content-type");
@@ -301,7 +490,7 @@ export class ApiHubAPI {
301
490
  requestBody = JSON.stringify(parsedDefinition);
302
491
  }
303
492
  catch (error) {
304
- throw new Error(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
493
+ throw new ToolError(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
305
494
  }
306
495
  }
307
496
  // Construct the URL with query parameters
@@ -320,7 +509,7 @@ export class ApiHubAPI {
320
509
  });
321
510
  if (!response.ok) {
322
511
  const errorText = await response.text().catch(() => "");
323
- throw new Error(`SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
512
+ throw new ToolError(`SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
324
513
  }
325
514
  // Determine operation type based on HTTP status code
326
515
  const operation = response.status === 201 ? "create" : "update";
@@ -353,7 +542,7 @@ export class ApiHubAPI {
353
542
  });
354
543
  if (!response.ok) {
355
544
  const errorText = await response.text().catch(() => "");
356
- throw new Error(`SwaggerHub Registry API createApiFromTemplate failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
545
+ throw new ToolError(`SwaggerHub Registry API createApiFromTemplate failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
357
546
  }
358
547
  // Determine operation type based on HTTP status code
359
548
  const operation = response.status === 201 ? "create" : "update";
@@ -374,7 +563,7 @@ export class ApiHubAPI {
374
563
  detectDefinitionFormat(definition) {
375
564
  const trimmed = definition.trim();
376
565
  if (!trimmed) {
377
- throw new Error("Empty definition content provided");
566
+ throw new ToolError("Empty definition content provided");
378
567
  }
379
568
  try {
380
569
  JSON.parse(trimmed);
@@ -384,4 +573,44 @@ export class ApiHubAPI {
384
573
  return "yaml";
385
574
  }
386
575
  }
576
+ /**
577
+ * Run a standardization scan against an API definition
578
+ * @param params Parameters including organization name and API definition
579
+ * @returns Standardization result with validation errors
580
+ */
581
+ async scanStandardization(params) {
582
+ // Auto-detect format from the definition content
583
+ const format = this.detectDefinitionFormat(params.definition);
584
+ let contentType;
585
+ let requestBody;
586
+ if (format === "yaml") {
587
+ contentType = "application/yaml";
588
+ requestBody = params.definition; // Send YAML as-is
589
+ }
590
+ else {
591
+ contentType = "application/json";
592
+ // For JSON, parse and stringify to ensure valid JSON
593
+ try {
594
+ const parsedDefinition = JSON.parse(params.definition);
595
+ requestBody = JSON.stringify(parsedDefinition);
596
+ }
597
+ catch (error) {
598
+ throw new Error(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
599
+ }
600
+ }
601
+ const url = `${this.config.registryBasePath}/standardization/${encodeURIComponent(params.orgName)}/scan`;
602
+ const response = await fetch(url, {
603
+ method: "POST",
604
+ headers: {
605
+ ...this.headers,
606
+ "Content-Type": contentType,
607
+ },
608
+ body: requestBody,
609
+ });
610
+ if (!response.ok) {
611
+ const errorText = await response.text().catch(() => "");
612
+ throw new Error(`SwaggerHub Registry API scanStandardization failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
613
+ }
614
+ return await this.handleResponse(response);
615
+ }
387
616
  }
@@ -2,6 +2,7 @@ export class ApiHubConfiguration {
2
2
  token;
3
3
  portalBasePath;
4
4
  registryBasePath;
5
+ userManagementBasePath;
5
6
  headers;
6
7
  constructor(param) {
7
8
  this.token = param.token;
@@ -9,6 +10,10 @@ export class ApiHubConfiguration {
9
10
  param.portalBasePath || "https://api.portal.swaggerhub.com/v1";
10
11
  this.registryBasePath =
11
12
  param.registryBasePath || "https://api.swaggerhub.com";
13
+ this.userManagementBasePath =
14
+ param.userManagementBasePath ||
15
+ "https://api.swaggerhub.com/user-management/v1";
16
+ // Use Bearer token format consistently across all APIs
12
17
  this.headers = {
13
18
  Authorization: `Bearer ${this.token}`,
14
19
  "Content-Type": "application/json",
@@ -3,3 +3,4 @@ export { ApiHubConfiguration, } from "./configuration.js";
3
3
  export * from "./portal-types.js";
4
4
  export * from "./registry-types.js";
5
5
  export { TOOLS } from "./tools.js";
6
+ export * from "./user-management-types.js";
@@ -10,6 +10,93 @@ export const ProductArgsSchema = z.object({
10
10
  .string()
11
11
  .describe("Product UUID or identifier in the format 'portal-subdomain:product-slug' - unique identifier for the product"),
12
12
  });
13
+ export const GetProductSectionsArgsSchema = z.object({
14
+ productId: z
15
+ .string()
16
+ .describe("Product UUID or identifier in the format 'portal-subdomain:product-slug' - unique identifier for the product"),
17
+ embed: z
18
+ .array(z.string())
19
+ .optional()
20
+ .describe("List of related entities to embed in the response - e.g., ['tableOfContents', 'tableOfContents.swaggerhubApi'] to include table of contents and SwaggerHub API details"),
21
+ page: z
22
+ .number()
23
+ .optional()
24
+ .describe("Page number for paginated results - specifies which page of results to retrieve (default is 1)"),
25
+ size: z
26
+ .number()
27
+ .optional()
28
+ .describe("Number of items per page for pagination - controls how many results are returned per page (default is 20)"),
29
+ });
30
+ export const GetTableOfContentsArgsSchema = z.object({
31
+ sectionId: z
32
+ .string()
33
+ .describe("Section ID - unique identifier for the section within the product"),
34
+ embed: z
35
+ .array(z.string())
36
+ .optional()
37
+ .describe("List of related entities to embed in the response - e.g., ['swaggerhubApi'] to include SwaggerHub API details"),
38
+ page: z
39
+ .number()
40
+ .optional()
41
+ .describe("Page number for paginated results - specifies which page of results to retrieve (default is 1)"),
42
+ size: z
43
+ .number()
44
+ .optional()
45
+ .describe("Number of items per page for pagination - controls how many results are returned per page (default is 20)"),
46
+ });
47
+ export const CreateTableOfContentsArgsSchema = z.object({
48
+ sectionId: z
49
+ .string()
50
+ .describe("Section ID - unique identifier for the section within the product"),
51
+ type: z
52
+ .enum(["new", "copy"])
53
+ .describe("Type of table of contents creation - 'new' to create from scratch or 'copy' to duplicate an existing one"),
54
+ title: z
55
+ .string()
56
+ .describe("Title of the table of contents item - will be displayed in navigation (3-40 characters)"),
57
+ slug: z
58
+ .string()
59
+ .describe("URL-friendly identifier for the table of contents item - must be unique within the section (3-22 characters, lowercase, alphanumeric with hyphens/underscores/dots)"),
60
+ order: z
61
+ .number()
62
+ .describe("Order position of the table of contents item within its parent section or item"),
63
+ parentId: z
64
+ .string()
65
+ .nullable()
66
+ .optional()
67
+ .describe("Parent table of contents item ID - null for top-level items, or ID of parent item for nested structure"),
68
+ content: z
69
+ .object({
70
+ type: z
71
+ .enum(["apiUrl", "html", "markdown"])
72
+ .describe("Content type - 'apiUrl' for API references, 'html' for HTML content, or 'markdown' for Markdown content"),
73
+ url: z
74
+ .string()
75
+ .optional()
76
+ .describe("URL for API reference content (required when type is 'apiUrl')"),
77
+ apiSpec: z
78
+ .string()
79
+ .nullable()
80
+ .optional()
81
+ .describe("API specification format for API URL content"),
82
+ documentId: z
83
+ .string()
84
+ .nullable()
85
+ .optional()
86
+ .describe("Document ID for HTML or Markdown content"),
87
+ })
88
+ .optional()
89
+ .describe("Content configuration for the table of contents item")
90
+ .refine((content) => {
91
+ if (content?.type === "apiUrl") {
92
+ return content.url?.endsWith("/swagger.json");
93
+ }
94
+ return true;
95
+ }, {
96
+ message: "URL must end with '/swagger.json' when content type is 'apiUrl'",
97
+ path: ["url"],
98
+ }),
99
+ });
13
100
  export const CreatePortalArgsSchema = z.object({
14
101
  name: z
15
102
  .string()
@@ -129,3 +216,42 @@ export const UpdateProductArgsSchema = ProductArgsSchema.extend({
129
216
  .optional()
130
217
  .describe("Change navigation visibility - true hides from portal landing page menus while keeping the product accessible via direct links"),
131
218
  });
219
+ export const PublishProductArgsSchema = ProductArgsSchema.extend({
220
+ preview: z
221
+ .boolean()
222
+ .optional()
223
+ .default(false)
224
+ .describe("Whether to publish as preview (true) or live (false). Preview allows testing before going live. Defaults to false (live publication)"),
225
+ });
226
+ // Document management schemas
227
+ export const GetDocumentArgsSchema = z.object({
228
+ documentId: z
229
+ .string()
230
+ .describe("Document UUID - unique identifier for the document"),
231
+ });
232
+ export const UpdateDocumentArgsSchema = z.object({
233
+ documentId: z
234
+ .string()
235
+ .describe("Document UUID - unique identifier for the document"),
236
+ content: z
237
+ .string()
238
+ .describe("The document content to update (HTML or Markdown based on document type)"),
239
+ type: z
240
+ .enum(["html", "markdown"])
241
+ .optional()
242
+ .describe("Content type - 'html' for HTML content or 'markdown' for Markdown content"),
243
+ });
244
+ export const DeleteDocumentArgsSchema = z.object({
245
+ documentId: z
246
+ .string()
247
+ .describe("Document UUID - unique identifier for the document to delete"),
248
+ });
249
+ export const DeleteTableOfContentsArgsSchema = z.object({
250
+ tableOfContentsId: z
251
+ .string()
252
+ .describe("The table of contents UUID, or identifier in the format 'portal-subdomain:product-slug:section-slug:table-of-contents-slug'"),
253
+ recursive: z
254
+ .boolean()
255
+ .optional()
256
+ .describe("Flag to include all the nested tables of contents (default: false)"),
257
+ });
@@ -67,3 +67,11 @@ export const CreateApiFromTemplateParamsSchema = z.object({
67
67
  .string()
68
68
  .describe("Template name to use for creating the API. Format: owner/template-name/version (e.g., 'swagger-hub/petstore-template/1.0.0'). API is created with fixed values: private visibility, no project assignment, and reconciliation enabled."),
69
69
  });
70
+ export const ScanStandardizationParamsSchema = z.object({
71
+ orgName: z
72
+ .string()
73
+ .describe("The organization name to use for standardization rules"),
74
+ definition: z
75
+ .string()
76
+ .describe("API definition content (OpenAPI/AsyncAPI specification in JSON or YAML format) to scan for standardization errors"),
77
+ });