@smartbear/mcp 0.7.0 → 0.9.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 +19 -2
- package/dist/api-hub/client/api.js +198 -23
- package/dist/api-hub/client/registry-types.js +22 -0
- package/dist/api-hub/client/tools.js +19 -1
- package/dist/api-hub/client.js +9 -0
- package/dist/bugsnag/client/api/CurrentUser.js +13 -50
- package/dist/bugsnag/client/api/Error.js +30 -155
- package/dist/bugsnag/client/api/Project.js +56 -124
- package/dist/bugsnag/client/api/api.js +3743 -0
- package/dist/bugsnag/client/api/base.js +107 -32
- package/dist/bugsnag/client/api/configuration.js +26 -0
- package/dist/bugsnag/client/api/index.js +2 -0
- package/dist/bugsnag/client/filters.js +28 -0
- package/dist/bugsnag/client.js +157 -325
- package/dist/common/server.js +29 -4
- package/dist/common/types.js +6 -1
- package/dist/index.js +8 -1
- package/dist/pactflow/client/prompt-utils.js +2 -1
- package/dist/pactflow/client/tools.js +9 -9
- package/dist/pactflow/client/utils.js +5 -4
- package/dist/pactflow/client.js +16 -14
- package/dist/qmetry/client/api/client-api.js +21 -16
- package/dist/qmetry/client/api/error-handler.js +329 -0
- package/dist/qmetry/client/auto-resolve.js +74 -0
- package/dist/qmetry/client/handlers.js +19 -2
- package/dist/qmetry/client/issues.js +26 -0
- package/dist/qmetry/client/project.js +56 -0
- package/dist/qmetry/client/requirement.js +76 -0
- package/dist/qmetry/client/testcase.js +46 -8
- package/dist/qmetry/client/testsuite.js +117 -0
- package/dist/qmetry/client/tools.js +1455 -4
- package/dist/qmetry/client/utils.js +16 -0
- package/dist/qmetry/client.js +19 -16
- package/dist/qmetry/config/constants.js +14 -0
- package/dist/qmetry/config/rest-endpoints.js +20 -0
- package/dist/qmetry/types/common.js +313 -8
- package/dist/qmetry/types/issues.js +6 -0
- package/dist/qmetry/types/project.js +10 -0
- package/dist/qmetry/types/requirements.js +19 -0
- package/dist/qmetry/types/testcase.js +14 -0
- package/dist/qmetry/types/testsuite.js +26 -0
- package/dist/reflect/client.js +7 -6
- package/dist/zephyr/client.js +16 -0
- package/dist/zephyr/common/api-client.js +27 -0
- package/dist/zephyr/common/auth-service.js +15 -0
- package/dist/zephyr/common/types.js +35 -0
- package/dist/zephyr/tool/project/get-projects.js +54 -0
- package/dist/zephyr/tool/zephyr-tool.js +1 -0
- package/package.json +3 -2
- package/dist/bugsnag/client/api/filters.js +0 -167
- package/dist/bugsnag/client/configuration.js +0 -10
- package/dist/bugsnag/client/index.js +0 -2
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/), [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/), [QMetry](https://www.qmetry.com/), and [Zephyr](https://smartbear.com/test-management/zephyr/).
|
|
22
22
|
|
|
23
23
|
## What is MCP?
|
|
24
24
|
|
|
@@ -33,12 +33,13 @@ See individual guides for suggested prompts and supported tools and resources:
|
|
|
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
35
|
- [QMetry](https://developer.smartbear.com/smartbear-mcp/docs/qmetry-integration) - QMetry Test Management capabilities
|
|
36
|
+
- [Zephyr](https://developer.smartbear.com/smartbear-mcp/docs/zephyr-integration) - Zephyr Test Management capabilities
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
## Prerequisites
|
|
39
40
|
|
|
40
41
|
- Node.js 20+ and npm
|
|
41
|
-
- Access to SmartBear products (BugSnag, Reflect, API Hub, or
|
|
42
|
+
- Access to SmartBear products (BugSnag, Reflect, API Hub, QMetry, or Zephyr)
|
|
42
43
|
- Valid API tokens for the products you want to integrate
|
|
43
44
|
|
|
44
45
|
## Installation
|
|
@@ -77,6 +78,8 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
77
78
|
"PACT_BROKER_PASSWORD": "${input:pact_broker_password}",
|
|
78
79
|
"QMETRY_API_KEY": "${input:qmetry_api_key}",
|
|
79
80
|
"QMETRY_BASE_URL": "${input:qmetry_base_url}",
|
|
81
|
+
"ZEPHYR_API_TOKEN": "${input:zephyr_api_token}",
|
|
82
|
+
"ZEPHYR_BASE_URL": "${input:zephyr_base_url}"
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
},
|
|
@@ -141,6 +144,18 @@ Alternatively, you can use `npx` (or globally install) the `@smartbear/mcp` pack
|
|
|
141
144
|
"description": "By default, connects to https://testmanagement.qmetry.com. Change to a custom QMetry server URL or a region-specific endpoint if needed.",
|
|
142
145
|
"password": false
|
|
143
146
|
},
|
|
147
|
+
{
|
|
148
|
+
"id": "zephyr_api_token",
|
|
149
|
+
"type": "promptString",
|
|
150
|
+
"description": "Zephyr API token - leave blank to disable Zephyr tools",
|
|
151
|
+
"password": true
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"id": "zephyr_base_url",
|
|
155
|
+
"type": "promptString",
|
|
156
|
+
"description": "Zephyr API base URL. By default, connects to https://api.zephyrscale.smartbear.com/v2. Change to region-specific endpoint if needed.",
|
|
157
|
+
"password": false
|
|
158
|
+
}
|
|
144
159
|
]
|
|
145
160
|
}
|
|
146
161
|
```
|
|
@@ -170,6 +185,8 @@ Add the following configuration to your `claude_desktop_config.json` to launch t
|
|
|
170
185
|
"PACT_BROKER_PASSWORD": "your_pact_broker_password",
|
|
171
186
|
"QMETRY_API_KEY": "your_qmetry_api_key",
|
|
172
187
|
"QMETRY_BASE_URL": "https://testmanagement.qmetry.com",
|
|
188
|
+
"ZEPHYR_API_TOKEN": "your_zephyr_api_token",
|
|
189
|
+
"ZEPHYR_BASE_URL": "https://api.zephyrscale.smartbear.com/v2"
|
|
173
190
|
}
|
|
174
191
|
}
|
|
175
192
|
}
|
|
@@ -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
|
|
@@ -39,13 +40,26 @@ export class ApiHubAPI {
|
|
|
39
40
|
return (await response.json());
|
|
40
41
|
}
|
|
41
42
|
catch (error) {
|
|
42
|
-
console.warn("Failed to parse JSON response:", error);
|
|
43
|
+
console.warn("Failed to parse JSON response (declared JSON):", error);
|
|
43
44
|
return defaultReturn;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
|
-
// Fallback
|
|
47
|
+
// Fallback: read text and attempt heuristic JSON parse
|
|
47
48
|
const text = await response.text();
|
|
48
|
-
|
|
49
|
+
if (!text)
|
|
50
|
+
return defaultReturn;
|
|
51
|
+
const trimmed = text.trim();
|
|
52
|
+
const firstChar = trimmed[0];
|
|
53
|
+
if (firstChar === "{" || firstChar === "[") {
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(trimmed);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.warn("Heuristic JSON parse failed:", error);
|
|
59
|
+
return { message: text };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { message: text };
|
|
49
63
|
}
|
|
50
64
|
/**
|
|
51
65
|
* Handles HTTP responses with smart JSON parsing and fallback handling.
|
|
@@ -57,7 +71,7 @@ export class ApiHubAPI {
|
|
|
57
71
|
*/
|
|
58
72
|
async handleResponse(response, defaultReturn = {}) {
|
|
59
73
|
if (!response.ok) {
|
|
60
|
-
throw new
|
|
74
|
+
throw new ToolError(`HTTP ${response.status}: ${response.statusText}`);
|
|
61
75
|
}
|
|
62
76
|
return this.parseResponse(response, defaultReturn);
|
|
63
77
|
}
|
|
@@ -66,7 +80,8 @@ export class ApiHubAPI {
|
|
|
66
80
|
method: "GET",
|
|
67
81
|
headers: this.headers,
|
|
68
82
|
});
|
|
69
|
-
|
|
83
|
+
const result = await this.handleResponse(response, []);
|
|
84
|
+
return result;
|
|
70
85
|
}
|
|
71
86
|
async createPortal(body) {
|
|
72
87
|
const response = await fetch(`${this.config.portalBasePath}/portals`, {
|
|
@@ -74,20 +89,32 @@ export class ApiHubAPI {
|
|
|
74
89
|
headers: this.headers,
|
|
75
90
|
body: JSON.stringify(body),
|
|
76
91
|
});
|
|
77
|
-
|
|
92
|
+
const result = await this.handleResponse(response);
|
|
93
|
+
if (!("id" in result)) {
|
|
94
|
+
throw new Error("Unexpected empty response creating portal");
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
78
97
|
}
|
|
79
98
|
async getPortal(portalId) {
|
|
80
99
|
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
|
|
81
100
|
method: "GET",
|
|
82
101
|
headers: this.headers,
|
|
83
102
|
});
|
|
84
|
-
|
|
103
|
+
const result = await this.handleResponse(response);
|
|
104
|
+
if (!("id" in result)) {
|
|
105
|
+
throw new ToolError("Portal not found or empty response");
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
85
108
|
}
|
|
86
109
|
async deletePortal(portalId) {
|
|
87
|
-
await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
|
|
110
|
+
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
|
|
88
111
|
method: "DELETE",
|
|
89
112
|
headers: this.headers,
|
|
90
113
|
});
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
const errorText = await response.text().catch(() => "");
|
|
116
|
+
throw new ToolError(`API Hub deletePortal failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
117
|
+
}
|
|
91
118
|
}
|
|
92
119
|
async updatePortal(portalId, body) {
|
|
93
120
|
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}`, {
|
|
@@ -102,7 +129,8 @@ export class ApiHubAPI {
|
|
|
102
129
|
method: "GET",
|
|
103
130
|
headers: this.headers,
|
|
104
131
|
});
|
|
105
|
-
|
|
132
|
+
const result = await this.handleResponse(response, []);
|
|
133
|
+
return result;
|
|
106
134
|
}
|
|
107
135
|
async createPortalProduct(portalId, body) {
|
|
108
136
|
const response = await fetch(`${this.config.portalBasePath}/portals/${portalId}/products`, {
|
|
@@ -110,14 +138,22 @@ export class ApiHubAPI {
|
|
|
110
138
|
headers: this.headers,
|
|
111
139
|
body: JSON.stringify(body),
|
|
112
140
|
});
|
|
113
|
-
|
|
141
|
+
const result = await this.handleResponse(response);
|
|
142
|
+
if (!("id" in result)) {
|
|
143
|
+
throw new Error("Unexpected empty response creating product");
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
114
146
|
}
|
|
115
147
|
async getPortalProduct(productId) {
|
|
116
148
|
const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
|
|
117
149
|
method: "GET",
|
|
118
150
|
headers: this.headers,
|
|
119
151
|
});
|
|
120
|
-
|
|
152
|
+
const result = await this.handleResponse(response);
|
|
153
|
+
if (!("id" in result)) {
|
|
154
|
+
throw new ToolError("Product not found or empty response");
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
121
157
|
}
|
|
122
158
|
async deletePortalProduct(productId) {
|
|
123
159
|
const response = await fetch(`${this.config.portalBasePath}/products/${productId}`, {
|
|
@@ -132,13 +168,9 @@ export class ApiHubAPI {
|
|
|
132
168
|
headers: this.headers,
|
|
133
169
|
body: JSON.stringify(body),
|
|
134
170
|
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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 });
|
|
171
|
+
return this.handleResponse(response, {
|
|
172
|
+
success: true,
|
|
173
|
+
});
|
|
142
174
|
}
|
|
143
175
|
/**
|
|
144
176
|
* Helper method for handling responses when error checking is already done.
|
|
@@ -149,9 +181,6 @@ export class ApiHubAPI {
|
|
|
149
181
|
* @param defaultReturn - Default value to return for empty responses
|
|
150
182
|
* @returns Parsed response data or fallback value
|
|
151
183
|
*/
|
|
152
|
-
async handleResponseWithoutErrorCheck(response, defaultReturn = {}) {
|
|
153
|
-
return this.parseResponse(response, defaultReturn);
|
|
154
|
-
}
|
|
155
184
|
// Registry API methods for SwaggerHub Design functionality
|
|
156
185
|
/**
|
|
157
186
|
* Search APIs and Domains in SwaggerHub Registry using /specs endpoint
|
|
@@ -184,7 +213,7 @@ export class ApiHubAPI {
|
|
|
184
213
|
headers: this.headers,
|
|
185
214
|
});
|
|
186
215
|
if (!response.ok) {
|
|
187
|
-
throw new
|
|
216
|
+
throw new ToolError(`SwaggerHub Registry API searchApis failed - status: ${response.status} ${response.statusText}`);
|
|
188
217
|
}
|
|
189
218
|
const apisJsonResponse = (await response.json());
|
|
190
219
|
// Transform APIs.json response to our ApiMetadata format
|
|
@@ -239,7 +268,7 @@ export class ApiHubAPI {
|
|
|
239
268
|
headers: this.headers,
|
|
240
269
|
});
|
|
241
270
|
if (!response.ok) {
|
|
242
|
-
throw new
|
|
271
|
+
throw new ToolError(`SwaggerHub Registry API getApiDefinition failed - status: ${response.status} ${response.statusText}`);
|
|
243
272
|
}
|
|
244
273
|
// Return the raw API definition (could be JSON or YAML)
|
|
245
274
|
const contentType = response.headers.get("content-type");
|
|
@@ -250,4 +279,150 @@ export class ApiHubAPI {
|
|
|
250
279
|
return response.text();
|
|
251
280
|
}
|
|
252
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Create or Update API in SwaggerHub Registry
|
|
284
|
+
* @param params Parameters for creating or updating the API including owner, name, version, specification, and definition
|
|
285
|
+
* @returns Created or updated API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
|
|
286
|
+
*/
|
|
287
|
+
async createOrUpdateApi(params) {
|
|
288
|
+
// Determine the format of the definition
|
|
289
|
+
let contentType;
|
|
290
|
+
let requestBody;
|
|
291
|
+
// Auto-detect format from the definition content
|
|
292
|
+
const format = this.detectDefinitionFormat(params.definition);
|
|
293
|
+
if (format === "yaml") {
|
|
294
|
+
contentType = "application/yaml";
|
|
295
|
+
requestBody = params.definition; // Send YAML as-is
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
contentType = "application/json";
|
|
299
|
+
// For JSON, parse and stringify to ensure valid JSON
|
|
300
|
+
try {
|
|
301
|
+
const parsedDefinition = JSON.parse(params.definition);
|
|
302
|
+
requestBody = JSON.stringify(parsedDefinition);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
throw new ToolError(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Construct the URL with query parameters
|
|
309
|
+
// Fixed values: visibility=private, automock=false, version=1.0.0
|
|
310
|
+
const searchParams = new URLSearchParams();
|
|
311
|
+
searchParams.append("isPrivate", "true");
|
|
312
|
+
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}?${searchParams.toString()}`;
|
|
313
|
+
// Use POST method with the appropriate content type
|
|
314
|
+
const response = await fetch(url, {
|
|
315
|
+
method: "POST",
|
|
316
|
+
headers: {
|
|
317
|
+
...this.headers,
|
|
318
|
+
"Content-Type": contentType,
|
|
319
|
+
},
|
|
320
|
+
body: requestBody,
|
|
321
|
+
});
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
const errorText = await response.text().catch(() => "");
|
|
324
|
+
throw new ToolError(`SwaggerHub Registry API createOrUpdateApi failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
325
|
+
}
|
|
326
|
+
// Determine operation type based on HTTP status code
|
|
327
|
+
const operation = response.status === 201 ? "create" : "update";
|
|
328
|
+
// Return formatted response with the required fields
|
|
329
|
+
// Fixed version is always 1.0.0
|
|
330
|
+
return {
|
|
331
|
+
owner: params.owner,
|
|
332
|
+
apiName: params.apiName,
|
|
333
|
+
version: "1.0.0",
|
|
334
|
+
url: `https://app.swaggerhub.com/apis/${params.owner}/${params.apiName}/1.0.0`,
|
|
335
|
+
operation,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Create API from Template in SwaggerHub Registry
|
|
340
|
+
* @param params Parameters for creating API from template including owner, api name, and template
|
|
341
|
+
* @returns Created API metadata with URL. HTTP 201 indicates creation, HTTP 200 indicates update
|
|
342
|
+
*/
|
|
343
|
+
async createApiFromTemplate(params) {
|
|
344
|
+
// Construct the URL with query parameters
|
|
345
|
+
// Fixed values: visibility=private, no project, noReconcile=false
|
|
346
|
+
const searchParams = new URLSearchParams();
|
|
347
|
+
searchParams.append("isPrivate", "true");
|
|
348
|
+
searchParams.append("template", params.template);
|
|
349
|
+
const url = `${this.config.registryBasePath}/apis/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.apiName)}/.template?${searchParams.toString()}`;
|
|
350
|
+
// Use POST method for template creation
|
|
351
|
+
const response = await fetch(url, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
headers: this.headers,
|
|
354
|
+
});
|
|
355
|
+
if (!response.ok) {
|
|
356
|
+
const errorText = await response.text().catch(() => "");
|
|
357
|
+
throw new ToolError(`SwaggerHub Registry API createApiFromTemplate failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
358
|
+
}
|
|
359
|
+
// Determine operation type based on HTTP status code
|
|
360
|
+
const operation = response.status === 201 ? "create" : "update";
|
|
361
|
+
// Return formatted response with the required fields
|
|
362
|
+
return {
|
|
363
|
+
owner: params.owner,
|
|
364
|
+
apiName: params.apiName,
|
|
365
|
+
template: params.template,
|
|
366
|
+
url: `https://app.swaggerhub.com/apis/${params.owner}/${params.apiName}`,
|
|
367
|
+
operation,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Auto-detect the format of an API definition string
|
|
372
|
+
* @param definition The API definition content
|
|
373
|
+
* @returns 'json' or 'yaml'
|
|
374
|
+
*/
|
|
375
|
+
detectDefinitionFormat(definition) {
|
|
376
|
+
const trimmed = definition.trim();
|
|
377
|
+
if (!trimmed) {
|
|
378
|
+
throw new ToolError("Empty definition content provided");
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
JSON.parse(trimmed);
|
|
382
|
+
return "json";
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return "yaml";
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Run a standardization scan against an API definition
|
|
390
|
+
* @param params Parameters including organization name and API definition
|
|
391
|
+
* @returns Standardization result with validation errors
|
|
392
|
+
*/
|
|
393
|
+
async scanStandardization(params) {
|
|
394
|
+
// Auto-detect format from the definition content
|
|
395
|
+
const format = this.detectDefinitionFormat(params.definition);
|
|
396
|
+
let contentType;
|
|
397
|
+
let requestBody;
|
|
398
|
+
if (format === "yaml") {
|
|
399
|
+
contentType = "application/yaml";
|
|
400
|
+
requestBody = params.definition; // Send YAML as-is
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
contentType = "application/json";
|
|
404
|
+
// For JSON, parse and stringify to ensure valid JSON
|
|
405
|
+
try {
|
|
406
|
+
const parsedDefinition = JSON.parse(params.definition);
|
|
407
|
+
requestBody = JSON.stringify(parsedDefinition);
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
throw new Error(`Invalid JSON format in definition: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const url = `${this.config.registryBasePath}/standardization/${encodeURIComponent(params.orgName)}/scan`;
|
|
414
|
+
const response = await fetch(url, {
|
|
415
|
+
method: "POST",
|
|
416
|
+
headers: {
|
|
417
|
+
...this.headers,
|
|
418
|
+
"Content-Type": contentType,
|
|
419
|
+
},
|
|
420
|
+
body: requestBody,
|
|
421
|
+
});
|
|
422
|
+
if (!response.ok) {
|
|
423
|
+
const errorText = await response.text().catch(() => "");
|
|
424
|
+
throw new Error(`SwaggerHub Registry API scanStandardization failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}. URL: ${url}`);
|
|
425
|
+
}
|
|
426
|
+
return await this.handleResponse(response);
|
|
427
|
+
}
|
|
253
428
|
}
|
|
@@ -53,3 +53,25 @@ export const ApiDefinitionParamsSchema = z.object({
|
|
|
53
53
|
.optional()
|
|
54
54
|
.describe("Set to true to create models from inline schemas in OpenAPI definition (default false)"),
|
|
55
55
|
});
|
|
56
|
+
export const CreateApiParamsSchema = z.object({
|
|
57
|
+
owner: z.string().describe("Organization name (owner of the API)"),
|
|
58
|
+
apiName: z.string().describe("API name"),
|
|
59
|
+
definition: z
|
|
60
|
+
.string()
|
|
61
|
+
.describe("API definition content (OpenAPI/AsyncAPI specification in JSON or YAML format). Format is automatically detected. API is created with fixed values: version 1.0.0, private visibility, automock disabled, and no project assignment."),
|
|
62
|
+
});
|
|
63
|
+
export const CreateApiFromTemplateParamsSchema = z.object({
|
|
64
|
+
owner: z.string().describe("Organization name (owner of the API)"),
|
|
65
|
+
apiName: z.string().describe("API name"),
|
|
66
|
+
template: z
|
|
67
|
+
.string()
|
|
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
|
+
});
|
|
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
|
+
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* This follows the pattern established in the pactflow module.
|
|
7
7
|
*/
|
|
8
8
|
import { CreatePortalArgsSchema, CreateProductArgsSchema, PortalArgsSchema, ProductArgsSchema, UpdatePortalArgsSchema, UpdateProductArgsSchema, } from "./portal-types.js";
|
|
9
|
-
import { ApiDefinitionParamsSchema, ApiSearchParamsSchema, } from "./registry-types.js";
|
|
9
|
+
import { ApiDefinitionParamsSchema, ApiSearchParamsSchema, CreateApiFromTemplateParamsSchema, CreateApiParamsSchema, ScanStandardizationParamsSchema, } from "./registry-types.js";
|
|
10
10
|
export const TOOLS = [
|
|
11
11
|
{
|
|
12
12
|
title: "List Portals",
|
|
@@ -83,4 +83,22 @@ export const TOOLS = [
|
|
|
83
83
|
zodSchema: ApiDefinitionParamsSchema,
|
|
84
84
|
handler: "getApiDefinition",
|
|
85
85
|
},
|
|
86
|
+
{
|
|
87
|
+
title: "Create or Update API",
|
|
88
|
+
summary: "Create a new API or update an existing API in SwaggerHub Registry for API Hub for Design. The API specification type (OpenAPI, AsyncAPI) is automatically detected from the definition content. APIs are always created with fixed values: version 1.0.0, private visibility, and automock disabled (these values cannot be changed). Returns HTTP 201 for creation, HTTP 200 for update. Response includes 'operation' field indicating whether it was a 'create' or 'update' operation along with API details and SwaggerHub URL.",
|
|
89
|
+
zodSchema: CreateApiParamsSchema,
|
|
90
|
+
handler: "createOrUpdateApi",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: "Create API from Template",
|
|
94
|
+
summary: "Create a new API in SwaggerHub Registry using a predefined template. This endpoint creates APIs based on existing templates without requiring manual definition content. APIs are always created with fixed values: private visibility, no project assignment, and reconciliation enabled (these values cannot be changed). Returns HTTP 201 for creation, HTTP 200 for update. Response includes 'operation' field and API details with SwaggerHub URL.",
|
|
95
|
+
zodSchema: CreateApiFromTemplateParamsSchema,
|
|
96
|
+
handler: "createApiFromTemplate",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
title: "Scan API Standardization",
|
|
100
|
+
summary: "Run a standardization scan against an API definition using the organization's standardization configuration. Accepts a YAML or JSON OpenAPI/AsyncAPI definition and returns a list of standardization errors and validation issues.",
|
|
101
|
+
zodSchema: ScanStandardizationParamsSchema,
|
|
102
|
+
handler: "scanStandardization",
|
|
103
|
+
},
|
|
86
104
|
];
|
package/dist/api-hub/client.js
CHANGED
|
@@ -51,6 +51,15 @@ export class ApiHubClient {
|
|
|
51
51
|
async getApiDefinition(args) {
|
|
52
52
|
return this.api.getApiDefinition(args);
|
|
53
53
|
}
|
|
54
|
+
async createOrUpdateApi(args) {
|
|
55
|
+
return this.api.createOrUpdateApi(args);
|
|
56
|
+
}
|
|
57
|
+
async createApiFromTemplate(args) {
|
|
58
|
+
return this.api.createApiFromTemplate(args);
|
|
59
|
+
}
|
|
60
|
+
async scanStandardization(args) {
|
|
61
|
+
return this.api.scanStandardization(args);
|
|
62
|
+
}
|
|
54
63
|
registerTools(register, _getInput) {
|
|
55
64
|
TOOLS.forEach((tool) => {
|
|
56
65
|
const { handler, formatResponse, ...toolParams } = tool;
|
|
@@ -1,41 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { CurrentUserApiFetchParamCreator, } from "./api.js";
|
|
2
|
+
import { BaseAPI } from "./base.js";
|
|
3
|
+
import { ProjectAPI } from "./Project.js";
|
|
3
4
|
export class CurrentUserAPI extends BaseAPI {
|
|
4
|
-
static
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
static organizationFields = [
|
|
6
|
+
"id",
|
|
7
|
+
"name",
|
|
8
|
+
"slug",
|
|
8
9
|
];
|
|
9
|
-
static organizationFields = ["id", "name", "slug"];
|
|
10
|
-
static projectFields = ["id", "name", "slug", "api_key"];
|
|
11
|
-
constructor(configuration) {
|
|
12
|
-
super(configuration, CurrentUserAPI.filterFields);
|
|
13
|
-
}
|
|
14
10
|
/**
|
|
15
11
|
* List the current user's organizations
|
|
16
12
|
* GET /user/organizations
|
|
17
13
|
*/
|
|
18
|
-
async listUserOrganizations(options = {}) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
if (admin !== undefined)
|
|
22
|
-
params.append("admin", String(admin));
|
|
23
|
-
for (const [key, value] of Object.entries(queryOptions)) {
|
|
24
|
-
if (value !== undefined)
|
|
25
|
-
params.append(key, String(value));
|
|
26
|
-
}
|
|
27
|
-
const url = params.toString()
|
|
28
|
-
? `/user/organizations?${params}`
|
|
29
|
-
: "/user/organizations";
|
|
30
|
-
const data = await this.request({
|
|
31
|
-
method: "GET",
|
|
32
|
-
url,
|
|
33
|
-
}, paginate);
|
|
34
|
-
// Only return allowed fields
|
|
35
|
-
return {
|
|
36
|
-
...data,
|
|
37
|
-
body: pickFieldsFromArray(data.body || [], CurrentUserAPI.organizationFields),
|
|
38
|
-
};
|
|
14
|
+
async listUserOrganizations(admin, perPage, options = {}) {
|
|
15
|
+
const localVarFetchArgs = CurrentUserApiFetchParamCreator(this.configuration).listUserOrganizations(admin, perPage, options);
|
|
16
|
+
return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, true, CurrentUserAPI.organizationFields);
|
|
39
17
|
}
|
|
40
18
|
/**
|
|
41
19
|
* List projects for a given organization
|
|
@@ -44,23 +22,8 @@ export class CurrentUserAPI extends BaseAPI {
|
|
|
44
22
|
* @param options Optional parameters for filtering, pagination, etc.
|
|
45
23
|
* @returns A promise that resolves to the list of projects in the organization
|
|
46
24
|
*/
|
|
47
|
-
async getOrganizationProjects(organizationId,
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
for (const [key, value] of Object.entries(queryOptions)) {
|
|
51
|
-
if (value !== undefined)
|
|
52
|
-
params.append(key, String(value));
|
|
53
|
-
}
|
|
54
|
-
const url = params.toString()
|
|
55
|
-
? `/organizations/${organizationId}/projects?${params}`
|
|
56
|
-
: `/organizations/${organizationId}/projects`;
|
|
57
|
-
const data = await this.request({
|
|
58
|
-
method: "GET",
|
|
59
|
-
url,
|
|
60
|
-
}, true); // Always paginate for projects
|
|
61
|
-
return {
|
|
62
|
-
...data,
|
|
63
|
-
body: pickFieldsFromArray(data.body || [], CurrentUserAPI.projectFields),
|
|
64
|
-
};
|
|
25
|
+
async getOrganizationProjects(organizationId, q, sort, direction, perPage, options) {
|
|
26
|
+
const localVarFetchArgs = CurrentUserApiFetchParamCreator(this.configuration).getOrganizationProjects(organizationId, q, sort, direction, perPage, options);
|
|
27
|
+
return await this.requestArray(localVarFetchArgs.url, localVarFetchArgs.options, true, ProjectAPI.projectFields);
|
|
65
28
|
}
|
|
66
29
|
}
|