@smartbear/mcp 0.6.0 → 0.7.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 (35) hide show
  1. package/README.md +20 -3
  2. package/dist/api-hub/client/api.js +253 -0
  3. package/dist/api-hub/client/configuration.js +27 -0
  4. package/dist/api-hub/client/index.js +5 -0
  5. package/dist/api-hub/client/portal-types.js +131 -0
  6. package/dist/api-hub/client/registry-types.js +55 -0
  7. package/dist/api-hub/client/tools.js +86 -0
  8. package/dist/api-hub/client.js +64 -404
  9. package/dist/bugsnag/client/api/CurrentUser.js +16 -10
  10. package/dist/bugsnag/client/api/Error.js +35 -35
  11. package/dist/bugsnag/client/api/Project.js +21 -9
  12. package/dist/bugsnag/client/api/base.js +7 -4
  13. package/dist/bugsnag/client/api/filters.js +9 -9
  14. package/dist/bugsnag/client.js +165 -140
  15. package/dist/common/info.js +1 -1
  16. package/dist/common/server.js +35 -27
  17. package/dist/index.js +11 -4
  18. package/dist/pactflow/client/ai.js +20 -20
  19. package/dist/pactflow/client/base.js +48 -13
  20. package/dist/pactflow/client/prompts.js +10 -12
  21. package/dist/pactflow/client/tools.js +10 -10
  22. package/dist/pactflow/client/utils.js +1 -1
  23. package/dist/pactflow/client.js +16 -9
  24. package/dist/qmetry/client/api/client-api.js +39 -0
  25. package/dist/qmetry/client/handlers.js +11 -0
  26. package/dist/qmetry/client/project.js +27 -0
  27. package/dist/qmetry/client/testcase.js +104 -0
  28. package/dist/qmetry/client/tools.js +222 -0
  29. package/dist/qmetry/client.js +95 -0
  30. package/dist/qmetry/config/constants.js +12 -0
  31. package/dist/qmetry/config/rest-endpoints.js +11 -0
  32. package/dist/qmetry/types/common.js +174 -0
  33. package/dist/qmetry/types/testcase.js +19 -0
  34. package/dist/reflect/client.js +14 -14
  35. package/package.json +6 -5
package/README.md CHANGED
@@ -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/) and [Pact Broker](https://docs.pact.io/)
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/), and [QMetry](https://www.qmetry.com/)
22
22
 
23
23
  ## What is MCP?
24
24
 
@@ -32,12 +32,13 @@ See individual guides for suggested prompts and supported tools and resources:
32
32
  - [Test Hub](https://developer.smartbear.com/smartbear-mcp/docs/test-hub-integration) - Test management and execution capabilities
33
33
  - [API Hub](https://developer.smartbear.com/smartbear-mcp/docs/api-hub-integration) - Portal management capabilities
34
34
  - [PactFlow](https://developer.smartbear.com/pactflow/default/getting-started) - Contract testing capabilities
35
+ - [QMetry](https://developer.smartbear.com/smartbear-mcp/docs/qmetry-integration) - QMetry Test Management capabilities
35
36
 
36
37
 
37
38
  ## Prerequisites
38
39
 
39
40
  - Node.js 20+ and npm
40
- - Access to SmartBear products (BugSnag, Reflect, or API Hub)
41
+ - Access to SmartBear products (BugSnag, Reflect, API Hub, or QMetry)
41
42
  - Valid API tokens for the products you want to integrate
42
43
 
43
44
  ## Installation
@@ -73,7 +74,9 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
73
74
  "PACT_BROKER_BASE_URL": "${input:pact_broker_base_url}",
74
75
  "PACT_BROKER_TOKEN": "${input:pact_broker_token}",
75
76
  "PACT_BROKER_USERNAME": "${input:pact_broker_username}",
76
- "PACT_BROKER_PASSWORD": "${input:pact_broker_password}"
77
+ "PACT_BROKER_PASSWORD": "${input:pact_broker_password}",
78
+ "QMETRY_API_KEY": "${input:qmetry_api_key}",
79
+ "QMETRY_BASE_URL": "${input:qmetry_base_url}",
77
80
  }
78
81
  }
79
82
  },
@@ -126,6 +129,18 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
126
129
  "description": "Pact Broker Password",
127
130
  "password": true
128
131
  },
132
+ {
133
+ "id": "qmetry_api_key",
134
+ "type": "promptString",
135
+ "description": "QMetry Open API Key",
136
+ "password": true
137
+ },
138
+ {
139
+ "id": "qmetry_base_url",
140
+ "type": "promptString",
141
+ "description": "By default, connects to https://testmanagement.qmetry.com. Change to a custom QMetry server URL or a region-specific endpoint if needed.",
142
+ "password": false
143
+ },
129
144
  ]
130
145
  }
131
146
  ```
@@ -153,6 +168,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
153
168
  "PACT_BROKER_TOKEN": "your_pactflow_token",
154
169
  "PACT_BROKER_USERNAME": "your_pact_broker_username",
155
170
  "PACT_BROKER_PASSWORD": "your_pact_broker_password",
171
+ "QMETRY_API_KEY": "your_qmetry_api_key",
172
+ "QMETRY_BASE_URL": "https://testmanagement.qmetry.com",
156
173
  }
157
174
  }
158
175
  }
@@ -0,0 +1,253 @@
1
+ // Regex to extract owner, name, and version from SwaggerHub URLs.
2
+ // Matches /apis/owner/name/version, /domains/owner/name/version, or /templates/owner/name/version
3
+ // Example: /apis/acme/petstore/1.0.0
4
+ // - group 1: type (apis|domains|templates)
5
+ // - group 2: owner
6
+ // - group 3: name
7
+ // - group 4: version
8
+ const SWAGGER_URL_REGEX = /\/(apis|domains|templates)\/([^/]+)\/([^/]+)\/([^/]+)/;
9
+ export class ApiHubAPI {
10
+ config;
11
+ headers;
12
+ constructor(config, userAgent) {
13
+ this.config = config;
14
+ this.headers = config.getHeaders(userAgent);
15
+ }
16
+ /**
17
+ * Core response parsing logic shared between different response handlers.
18
+ * Handles 204 No Content, empty responses, JSON parsing, and text fallbacks.
19
+ * @template T - Expected JSON response data type
20
+ * @template D - Default return type for empty responses
21
+ * @param response - The fetch Response object
22
+ * @param defaultReturn - Default value to return for empty responses
23
+ * @returns Parsed response data or fallback value
24
+ */
25
+ async parseResponse(response, defaultReturn = {}) {
26
+ // Handle 204 No Content responses
27
+ if (response.status === 204) {
28
+ return defaultReturn;
29
+ }
30
+ // Check if response has content-length of 0 (empty body)
31
+ const contentLength = response.headers.get("content-length");
32
+ if (contentLength === "0") {
33
+ return defaultReturn;
34
+ }
35
+ // Check if response is JSON
36
+ const contentType = response.headers.get("content-type");
37
+ if (contentType?.includes("application/json")) {
38
+ try {
39
+ return (await response.json());
40
+ }
41
+ catch (error) {
42
+ console.warn("Failed to parse JSON response:", error);
43
+ return defaultReturn;
44
+ }
45
+ }
46
+ // Fallback for non-JSON responses
47
+ const text = await response.text();
48
+ return text ? { message: text } : defaultReturn;
49
+ }
50
+ /**
51
+ * Handles HTTP responses with smart JSON parsing and fallback handling.
52
+ * Includes HTTP error checking before parsing.
53
+ * @template T - Expected response data type
54
+ * @param response - The fetch Response object
55
+ * @param defaultReturn - Default value to return for empty responses
56
+ * @returns Parsed response data or fallback value
57
+ */
58
+ async handleResponse(response, defaultReturn = {}) {
59
+ if (!response.ok) {
60
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
61
+ }
62
+ return this.parseResponse(response, defaultReturn);
63
+ }
64
+ async getPortals() {
65
+ const response = await fetch(`${this.config.portalBasePath}/portals`, {
66
+ method: "GET",
67
+ headers: this.headers,
68
+ });
69
+ return response.json();
70
+ }
71
+ async createPortal(body) {
72
+ const response = await fetch(`${this.config.portalBasePath}/portals`, {
73
+ method: "POST",
74
+ headers: this.headers,
75
+ body: JSON.stringify(body),
76
+ });
77
+ return response.json();
78
+ }
79
+ async getPortal(portalId) {
80
+ const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
81
+ method: "GET",
82
+ headers: this.headers,
83
+ });
84
+ return response.json();
85
+ }
86
+ async deletePortal(portalId) {
87
+ await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
88
+ method: "DELETE",
89
+ headers: this.headers,
90
+ });
91
+ }
92
+ async updatePortal(portalId, body) {
93
+ const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
94
+ method: "PATCH",
95
+ headers: this.headers,
96
+ body: JSON.stringify(body),
97
+ });
98
+ return this.handleResponse(response);
99
+ }
100
+ async getPortalProducts(portalId) {
101
+ const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
102
+ method: "GET",
103
+ headers: this.headers,
104
+ });
105
+ return response.json();
106
+ }
107
+ async createPortalProduct(portalId, body) {
108
+ const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
109
+ method: "POST",
110
+ headers: this.headers,
111
+ body: JSON.stringify(body),
112
+ });
113
+ return response.json();
114
+ }
115
+ async getPortalProduct(productId) {
116
+ const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
117
+ method: "GET",
118
+ headers: this.headers,
119
+ });
120
+ return response.json();
121
+ }
122
+ async deletePortalProduct(productId) {
123
+ const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
124
+ method: "DELETE",
125
+ headers: this.headers,
126
+ });
127
+ return this.handleResponse(response);
128
+ }
129
+ async updatePortalProduct(productId, body) {
130
+ const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
131
+ method: "PATCH",
132
+ headers: this.headers,
133
+ body: JSON.stringify(body),
134
+ });
135
+ // Custom error handling for updatePortalProduct
136
+ if (!response.ok) {
137
+ const errorText = await response.text().catch(() => "");
138
+ throw new Error(`API Hub updatePortalProduct failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
139
+ }
140
+ // Use handleResponse but with custom default return value
141
+ return this.handleResponseWithoutErrorCheck(response, { success: true });
142
+ }
143
+ /**
144
+ * Helper method for handling responses when error checking is already done.
145
+ * Delegates to parseResponse for the actual parsing logic.
146
+ * @template T - Expected response data type
147
+ * @template D - Default return type
148
+ * @param response - The fetch Response object
149
+ * @param defaultReturn - Default value to return for empty responses
150
+ * @returns Parsed response data or fallback value
151
+ */
152
+ async handleResponseWithoutErrorCheck(response, defaultReturn = {}) {
153
+ return this.parseResponse(response, defaultReturn);
154
+ }
155
+ // Registry API methods for SwaggerHub Design functionality
156
+ /**
157
+ * Search APIs and Domains in SwaggerHub Registry using /specs endpoint
158
+ * @param params Search parameters
159
+ * @returns Array of processed API metadata
160
+ */
161
+ async searchApis(params = {}) {
162
+ const searchParams = new URLSearchParams();
163
+ if (params.query)
164
+ searchParams.append("query", params.query);
165
+ if (params.state)
166
+ searchParams.append("state", params.state);
167
+ if (params.tag)
168
+ searchParams.append("tag", params.tag);
169
+ if (params.offset !== undefined)
170
+ searchParams.append("offset", params.offset.toString());
171
+ if (params.limit !== undefined)
172
+ searchParams.append("limit", params.limit.toString());
173
+ if (params.sort)
174
+ searchParams.append("sort", params.sort);
175
+ if (params.order)
176
+ searchParams.append("order", params.order);
177
+ if (params.owner)
178
+ searchParams.append("owner", params.owner);
179
+ if (params.specType)
180
+ searchParams.append("specType", params.specType);
181
+ const url = `${this.config.registryBasePath}/specs${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
182
+ const response = await fetch(url, {
183
+ method: "GET",
184
+ headers: this.headers,
185
+ });
186
+ if (!response.ok) {
187
+ throw new Error(`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`);
188
+ }
189
+ const apisJsonResponse = (await response.json());
190
+ // Transform APIs.json response to our ApiMetadata format
191
+ return this.transformApisJsonToMetadata(apisJsonResponse.apis);
192
+ }
193
+ /**
194
+ * Transform APIs.json specifications to our ApiMetadata format
195
+ * @param specs Array of API specifications from APIs.json
196
+ * @returns Array of processed API metadata
197
+ */
198
+ transformApisJsonToMetadata(specs) {
199
+ return specs.map((spec) => {
200
+ // Extract useful properties from the properties array
201
+ const properties = spec.properties || [];
202
+ const getProperty = (type) => {
203
+ const property = properties.find((p) => p.type === type);
204
+ return property?.value || property?.url;
205
+ };
206
+ // Extract owner, name, and version from the Swagger URL using the regex constant
207
+ const swaggerUrl = getProperty("Swagger") || "";
208
+ const urlMatch = RegExp(SWAGGER_URL_REGEX).exec(swaggerUrl);
209
+ return {
210
+ owner: urlMatch?.[2] || "",
211
+ name: spec.name || "",
212
+ description: spec.description || "",
213
+ summary: spec.summary || "",
214
+ version: getProperty("X-Version") || urlMatch?.[4] || "",
215
+ specification: getProperty("X-Specification") || "",
216
+ created: getProperty("X-Created"),
217
+ modified: getProperty("X-Modified"),
218
+ published: getProperty("X-Published"),
219
+ private: getProperty("X-Private"),
220
+ oasVersion: getProperty("X-OASVersion"),
221
+ url: swaggerUrl,
222
+ };
223
+ });
224
+ }
225
+ /**
226
+ * Get API definition from SwaggerHub Registry
227
+ * @param params Parameters including owner, api name, version, and options
228
+ * @returns API definition (OpenAPI/Swagger specification)
229
+ */
230
+ async getApiDefinition(params) {
231
+ const searchParams = new URLSearchParams();
232
+ if (params.resolved !== undefined)
233
+ searchParams.append("resolved", params.resolved.toString());
234
+ if (params.flatten !== undefined)
235
+ searchParams.append("flatten", params.flatten.toString());
236
+ const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.api)}/${encodeURIComponent(params.version)}${searchParams.toString() ? `?${searchParams.toString()}` : ""}`;
237
+ const response = await fetch(url, {
238
+ method: "GET",
239
+ headers: this.headers,
240
+ });
241
+ if (!response.ok) {
242
+ throw new Error(`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`);
243
+ }
244
+ // Return the raw API definition (could be JSON or YAML)
245
+ const contentType = response.headers.get("content-type");
246
+ if (contentType?.includes("application/json")) {
247
+ return response.json();
248
+ }
249
+ else {
250
+ return response.text();
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,27 @@
1
+ export class ApiHubConfiguration {
2
+ token;
3
+ portalBasePath;
4
+ registryBasePath;
5
+ headers;
6
+ constructor(param) {
7
+ this.token = param.token;
8
+ this.portalBasePath =
9
+ param.portalBasePath || "https://api.portal.swaggerhub.com/v1";
10
+ this.registryBasePath =
11
+ param.registryBasePath || "https://api.swaggerhub.com";
12
+ this.headers = {
13
+ Authorization: `Bearer ${this.token}`,
14
+ "Content-Type": "application/json",
15
+ ...param.headers,
16
+ };
17
+ }
18
+ /**
19
+ * Get headers with User-Agent included
20
+ */
21
+ getHeaders(userAgent) {
22
+ return {
23
+ ...this.headers,
24
+ "User-Agent": userAgent,
25
+ };
26
+ }
27
+ }
@@ -0,0 +1,5 @@
1
+ export { ApiHubAPI } from "./api.js";
2
+ export { ApiHubConfiguration, } from "./configuration.js";
3
+ export * from "./portal-types.js";
4
+ export * from "./registry-types.js";
5
+ export { TOOLS } from "./tools.js";
@@ -0,0 +1,131 @@
1
+ import { z } from "zod";
2
+ // Zod schemas for Portal API validation
3
+ export const PortalArgsSchema = z.object({
4
+ portalId: z
5
+ .string()
6
+ .describe("Portal UUID or subdomain - unique identifier for the portal instance"),
7
+ });
8
+ export const ProductArgsSchema = z.object({
9
+ productId: z
10
+ .string()
11
+ .describe("Product UUID or identifier in the format 'portal-subdomain:product-slug' - unique identifier for the product"),
12
+ });
13
+ export const CreatePortalArgsSchema = z.object({
14
+ name: z
15
+ .string()
16
+ .optional()
17
+ .describe("The display name for the portal - shown to users and in branding (3-40 characters)"),
18
+ subdomain: z
19
+ .string()
20
+ .describe("The portal subdomain - used in the portal URL (e.g., 'myportal' for myportal.example.com). Must be unique, lowercase, 3-20 characters, alphanumeric with hyphens"),
21
+ offline: z
22
+ .boolean()
23
+ .optional()
24
+ .describe("If true, the portal will not be visible to customers - useful for development/staging environments. Defaults to false"),
25
+ routing: z
26
+ .string()
27
+ .optional()
28
+ .describe("Routing strategy for the portal - either 'browser' (client-side routing) or 'proxy' (server-side routing). Defaults to 'browser'"),
29
+ credentialsEnabled: z
30
+ .boolean()
31
+ .optional()
32
+ .describe("Whether authentication credentials are enabled for accessing the portal. When true, users can authenticate to access private content. Defaults to true"),
33
+ swaggerHubOrganizationId: z
34
+ .string()
35
+ .describe("The corresponding SwaggerHub organization UUID - required for portal creation. This links the portal to your SwaggerHub organization"),
36
+ openapiRenderer: z
37
+ .string()
38
+ .optional()
39
+ .describe("OpenAPI renderer type: 'SWAGGER_UI' (Swagger UI), 'ELEMENTS' (Stoplight Elements), or 'TOGGLE' (allows switching between both with Elements as default). Defaults to 'TOGGLE'"),
40
+ pageContentFormat: z
41
+ .string()
42
+ .optional()
43
+ .describe("Format for page content rendering - determines how documentation pages are processed: 'HTML', 'MARKDOWN', or 'BOTH'. Defaults to 'HTML'"),
44
+ });
45
+ export const UpdatePortalArgsSchema = PortalArgsSchema.extend({
46
+ name: z
47
+ .string()
48
+ .optional()
49
+ .describe("Update the portal display name - shown to users and in branding (3-40 characters)"),
50
+ subdomain: z
51
+ .string()
52
+ .optional()
53
+ .describe("Update the portal subdomain - changes the portal URL. Must remain unique across all portals (3-20 characters, lowercase, alphanumeric with hyphens)"),
54
+ customDomain: z
55
+ .boolean()
56
+ .optional()
57
+ .describe("Enable/disable custom domain for the portal - allows using your own domain instead of the default subdomain"),
58
+ gtmKey: z
59
+ .string()
60
+ .optional()
61
+ .describe("Google Tag Manager key for analytics tracking - format: GTM-XXXXXX (max 25 characters)"),
62
+ offline: z
63
+ .boolean()
64
+ .optional()
65
+ .describe("Set portal visibility - true hides portal from customers (useful for maintenance or development)"),
66
+ routing: z
67
+ .string()
68
+ .optional()
69
+ .describe("Update routing strategy - 'browser' for client-side routing or 'proxy' for server-side routing"),
70
+ credentialsEnabled: z
71
+ .boolean()
72
+ .optional()
73
+ .describe("Enable/disable authentication credentials for portal access - controls whether users can authenticate to view private content"),
74
+ openapiRenderer: z
75
+ .string()
76
+ .optional()
77
+ .describe("Change OpenAPI renderer: 'SWAGGER_UI' (Swagger UI), 'ELEMENTS' (Stoplight Elements), or 'TOGGLE' (switch between both)"),
78
+ pageContentFormat: z
79
+ .string()
80
+ .optional()
81
+ .describe("Update page content format for documentation rendering: 'HTML', 'MARKDOWN', or 'BOTH'"),
82
+ });
83
+ export const CreateProductArgsSchema = PortalArgsSchema.extend({
84
+ type: z
85
+ .string()
86
+ .describe("Product creation type - 'new' to create from scratch or 'copy' to duplicate an existing product"),
87
+ name: z
88
+ .string()
89
+ .describe("Product display name - will be shown to users in the portal navigation and product listings (3-40 characters)"),
90
+ slug: z
91
+ .string()
92
+ .describe("URL-friendly identifier for the product - must be unique within the portal, used in URLs (e.g., 'my-api' becomes /my-api). 3-22 characters, lowercase, alphanumeric with hyphens, underscores, or dots"),
93
+ description: z
94
+ .string()
95
+ .optional()
96
+ .describe("Product description - explains what the API/product does, shown in product listings and cards (max 110 characters)"),
97
+ public: z
98
+ .boolean()
99
+ .optional()
100
+ .describe("Whether the product is publicly visible to all portal visitors - false means only authenticated users with appropriate roles can access it"),
101
+ hidden: z
102
+ .boolean()
103
+ .optional()
104
+ .describe("Whether the product is hidden from the portal landing page navigation menus - useful for internal or draft products"),
105
+ role: z
106
+ .boolean()
107
+ .optional()
108
+ .describe("Whether the product has role-based access restrictions - controls if specific user roles are required to access the product"),
109
+ });
110
+ export const UpdateProductArgsSchema = ProductArgsSchema.extend({
111
+ name: z
112
+ .string()
113
+ .optional()
114
+ .describe("Update product display name - changes how it appears to users in navigation and listings (3-40 characters)"),
115
+ slug: z
116
+ .string()
117
+ .optional()
118
+ .describe("Update URL-friendly identifier - must remain unique within the portal, affects product URLs (3-22 characters, lowercase, alphanumeric with hyphens/underscores/dots)"),
119
+ description: z
120
+ .string()
121
+ .optional()
122
+ .describe("Update product description - explains the API/product functionality, shown in listings (max 110 characters)"),
123
+ public: z
124
+ .boolean()
125
+ .optional()
126
+ .describe("Change product visibility - true makes it publicly accessible to all visitors, false restricts to authenticated users with roles"),
127
+ hidden: z
128
+ .boolean()
129
+ .optional()
130
+ .describe("Change navigation visibility - true hides from portal landing page menus while keeping the product accessible via direct links"),
131
+ });
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ // Zod schemas for SwaggerHub Registry API validation
3
+ export const ApiSearchParamsSchema = z.object({
4
+ query: z
5
+ .string()
6
+ .optional()
7
+ .describe("Search query to filter APIs by name, description, or content"),
8
+ state: z
9
+ .enum(["ALL", "PUBLISHED", "UNPUBLISHED"])
10
+ .optional()
11
+ .describe("Filter APIs by publication state - ALL (default), PUBLISHED, or UNPUBLISHED"),
12
+ tag: z.string().optional().describe("Filter APIs by tag"),
13
+ offset: z
14
+ .number()
15
+ .min(0)
16
+ .optional()
17
+ .describe("Offset for pagination (0-based, default 0)"),
18
+ limit: z
19
+ .number()
20
+ .min(1)
21
+ .max(100)
22
+ .optional()
23
+ .describe("Number of results per page (1-100, default 20)"),
24
+ sort: z
25
+ .enum(["NAME", "UPDATED", "CREATED"])
26
+ .optional()
27
+ .describe("Sort field - NAME, UPDATED, or CREATED (default NAME)"),
28
+ order: z
29
+ .enum(["ASC", "DESC"])
30
+ .optional()
31
+ .describe("Sort order - ASC or DESC (default ASC)"),
32
+ owner: z
33
+ .string()
34
+ .optional()
35
+ .describe("Filter APIs by owner (organization or user)"),
36
+ specType: z
37
+ .enum(["API", "DOMAIN"])
38
+ .optional()
39
+ .describe("Filter by specification type - API or DOMAIN (default all types)"),
40
+ });
41
+ export const ApiDefinitionParamsSchema = z.object({
42
+ owner: z
43
+ .string()
44
+ .describe("API owner (organization or user, case-sensitive)"),
45
+ api: z.string().describe("API name (case-sensitive)"),
46
+ version: z.string().describe("Version identifier"),
47
+ resolved: z
48
+ .boolean()
49
+ .optional()
50
+ .describe("Set to true to get the resolved version with all external $refs included (default false)"),
51
+ flatten: z
52
+ .boolean()
53
+ .optional()
54
+ .describe("Set to true to create models from inline schemas in OpenAPI definition (default false)"),
55
+ });
@@ -0,0 +1,86 @@
1
+ /**
2
+ * TOOLS
3
+ *
4
+ * Defines tool configurations for API Hub operations.
5
+ * Each tool includes parameters, descriptions, and handler method names.
6
+ * This follows the pattern established in the pactflow module.
7
+ */
8
+ import { CreatePortalArgsSchema, CreateProductArgsSchema, PortalArgsSchema, ProductArgsSchema, UpdatePortalArgsSchema, UpdateProductArgsSchema, } from "./portal-types.js";
9
+ import { ApiDefinitionParamsSchema, ApiSearchParamsSchema, } from "./registry-types.js";
10
+ export const TOOLS = [
11
+ {
12
+ title: "List Portals",
13
+ summary: "Search for available portals within API Hub. Only portals where you have at least a designer role, either at the product level or organization level, are returned.",
14
+ parameters: [],
15
+ handler: "getPortals",
16
+ },
17
+ {
18
+ title: "Create Portal",
19
+ summary: "Create a new portal within API Hub.",
20
+ zodSchema: CreatePortalArgsSchema,
21
+ handler: "createPortal",
22
+ },
23
+ {
24
+ title: "Get Portal",
25
+ summary: "Retrieve information about a specific portal.",
26
+ zodSchema: PortalArgsSchema,
27
+ handler: "getPortal",
28
+ },
29
+ {
30
+ title: "Delete Portal",
31
+ summary: "Delete a specific portal.",
32
+ zodSchema: PortalArgsSchema,
33
+ handler: "deletePortal",
34
+ formatResponse: () => "Portal deleted successfully.",
35
+ },
36
+ {
37
+ title: "Update Portal",
38
+ summary: "Update a specific portal's configuration.",
39
+ zodSchema: UpdatePortalArgsSchema,
40
+ handler: "updatePortal",
41
+ },
42
+ {
43
+ title: "List Portal Products",
44
+ summary: "Get products for a specific portal that match your criteria.",
45
+ zodSchema: PortalArgsSchema,
46
+ handler: "getPortalProducts",
47
+ },
48
+ {
49
+ title: "Create Portal Product",
50
+ summary: "Create a new product for a specific portal.",
51
+ zodSchema: CreateProductArgsSchema,
52
+ handler: "createPortalProduct",
53
+ },
54
+ {
55
+ title: "Get Portal Product",
56
+ summary: "Retrieve information about a specific product resource.",
57
+ zodSchema: ProductArgsSchema,
58
+ handler: "getPortalProduct",
59
+ },
60
+ {
61
+ title: "Delete Portal Product",
62
+ summary: "Delete a product from a specific portal",
63
+ zodSchema: ProductArgsSchema,
64
+ handler: "deletePortalProduct",
65
+ formatResponse: () => "Product deleted successfully.",
66
+ },
67
+ {
68
+ title: "Update Portal Product",
69
+ summary: "Update a product's settings within a specific portal.",
70
+ zodSchema: UpdateProductArgsSchema,
71
+ handler: "updatePortalProduct",
72
+ },
73
+ // Registry API tools for SwaggerHub Design functionality
74
+ {
75
+ title: "Search APIs and Domains",
76
+ summary: "Search for APIs and Domains in SwaggerHub Registry using the comprehensive /specs endpoint and retrieve metadata including owner, name, description, summary, version, and specification.",
77
+ zodSchema: ApiSearchParamsSchema,
78
+ handler: "searchApis",
79
+ },
80
+ {
81
+ title: "Get API Definition",
82
+ summary: "Fetch resolved API definition from SwaggerHub Registry based on owner, API name, and version.",
83
+ zodSchema: ApiDefinitionParamsSchema,
84
+ handler: "getApiDefinition",
85
+ },
86
+ ];