@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.
- package/README.md +20 -3
- package/dist/api-hub/client/api.js +253 -0
- package/dist/api-hub/client/configuration.js +27 -0
- package/dist/api-hub/client/index.js +5 -0
- package/dist/api-hub/client/portal-types.js +131 -0
- package/dist/api-hub/client/registry-types.js +55 -0
- package/dist/api-hub/client/tools.js +86 -0
- package/dist/api-hub/client.js +64 -404
- package/dist/bugsnag/client/api/CurrentUser.js +16 -10
- package/dist/bugsnag/client/api/Error.js +35 -35
- package/dist/bugsnag/client/api/Project.js +21 -9
- package/dist/bugsnag/client/api/base.js +7 -4
- package/dist/bugsnag/client/api/filters.js +9 -9
- package/dist/bugsnag/client.js +165 -140
- package/dist/common/info.js +1 -1
- package/dist/common/server.js +35 -27
- package/dist/index.js +11 -4
- package/dist/pactflow/client/ai.js +20 -20
- package/dist/pactflow/client/base.js +48 -13
- package/dist/pactflow/client/prompts.js +10 -12
- package/dist/pactflow/client/tools.js +10 -10
- package/dist/pactflow/client/utils.js +1 -1
- package/dist/pactflow/client.js +16 -9
- package/dist/qmetry/client/api/client-api.js +39 -0
- package/dist/qmetry/client/handlers.js +11 -0
- package/dist/qmetry/client/project.js +27 -0
- package/dist/qmetry/client/testcase.js +104 -0
- package/dist/qmetry/client/tools.js +222 -0
- package/dist/qmetry/client.js +95 -0
- package/dist/qmetry/config/constants.js +12 -0
- package/dist/qmetry/config/rest-endpoints.js +11 -0
- package/dist/qmetry/types/common.js +174 -0
- package/dist/qmetry/types/testcase.js +19 -0
- package/dist/reflect/client.js +14 -14
- 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/)
|
|
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,
|
|
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,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
|
+
];
|