@smartbear/mcp 0.3.0 → 0.5.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 +86 -25
- package/dist/api-hub/client.js +298 -52
- package/dist/{insight-hub → bugsnag}/client/api/CurrentUser.js +2 -1
- package/dist/{insight-hub → bugsnag}/client/api/Error.js +37 -4
- package/dist/{insight-hub → bugsnag}/client/api/Project.js +2 -1
- package/dist/{insight-hub → bugsnag}/client/api/base.js +18 -1
- package/dist/{insight-hub → bugsnag}/client/api/filters.js +2 -2
- package/dist/bugsnag/client.js +687 -0
- package/dist/common/info.js +1 -1
- package/dist/common/server.js +148 -0
- package/dist/index.js +38 -29
- package/dist/pactflow/client/ai.js +146 -0
- package/dist/pactflow/client/base.js +6 -0
- package/dist/pactflow/client/tools.js +55 -0
- package/dist/pactflow/client/utils.js +70 -0
- package/dist/pactflow/client.js +180 -0
- package/dist/reflect/client.js +100 -18
- package/package.json +8 -4
- package/dist/common/templates.js +0 -57
- package/dist/insight-hub/client.js +0 -621
- package/dist/package.json +0 -59
- package/dist/tests/unit/common/templates.test.js +0 -149
- package/dist/tests/unit/insight-hub/api-utilities.test.js +0 -31
- package/dist/tests/unit/insight-hub/client.test.js +0 -920
- package/dist/tests/unit/insight-hub/filters.test.js +0 -93
- package/dist/vitest.config.js +0 -57
- /package/dist/{insight-hub → bugsnag}/client/api/index.js +0 -0
- /package/dist/{insight-hub → bugsnag}/client/configuration.js +0 -0
- /package/dist/{insight-hub → bugsnag}/client/index.js +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
|
|
2
|
+
import { TOOLS } from "./client/tools.js";
|
|
3
|
+
// Tool definitions for PactFlow AI API client
|
|
4
|
+
export class PactflowClient {
|
|
5
|
+
name = "Contract Testing";
|
|
6
|
+
prefix = "contract-testing";
|
|
7
|
+
headers;
|
|
8
|
+
aiBaseUrl;
|
|
9
|
+
baseUrl;
|
|
10
|
+
clientType;
|
|
11
|
+
constructor(auth, baseUrl, clientType) {
|
|
12
|
+
// Set headers based on the type of auth provided
|
|
13
|
+
if (typeof auth === "string") {
|
|
14
|
+
this.headers = {
|
|
15
|
+
Authorization: `Bearer ${auth}`,
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const authString = `${auth.username}:${auth.password}`;
|
|
22
|
+
this.headers = {
|
|
23
|
+
Authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
this.baseUrl = baseUrl;
|
|
29
|
+
this.aiBaseUrl = `${this.baseUrl}/api/ai`;
|
|
30
|
+
this.clientType = clientType;
|
|
31
|
+
}
|
|
32
|
+
// PactFlow AI client methods
|
|
33
|
+
/**
|
|
34
|
+
* Generate new Pact tests based on the provided input.
|
|
35
|
+
*
|
|
36
|
+
* @param toolInput The input data for the generation process.
|
|
37
|
+
* @returns The result of the generation process.
|
|
38
|
+
* @throws Error if the HTTP request fails or the operation times out.
|
|
39
|
+
*/
|
|
40
|
+
async generate(toolInput) {
|
|
41
|
+
// Submit the generation request
|
|
42
|
+
const response = await fetch(`${this.aiBaseUrl}/generate`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: this.headers,
|
|
45
|
+
body: JSON.stringify(toolInput),
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`);
|
|
49
|
+
}
|
|
50
|
+
const status_response = await response.json();
|
|
51
|
+
return await this.pollForCompletion(status_response, "Generation");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Review the provided Pact tests and suggest improvements.
|
|
55
|
+
*
|
|
56
|
+
* @param toolInput The input data for the review process.
|
|
57
|
+
* @returns The result of the review process.
|
|
58
|
+
* @throws Error if the HTTP request fails or the operation times out.
|
|
59
|
+
*/
|
|
60
|
+
async review(toolInput) {
|
|
61
|
+
// Submit review request
|
|
62
|
+
const response = await fetch(`${this.aiBaseUrl}/review`, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: this.headers,
|
|
65
|
+
body: JSON.stringify(toolInput),
|
|
66
|
+
});
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`);
|
|
69
|
+
}
|
|
70
|
+
const status_response = await response.json();
|
|
71
|
+
return await this.pollForCompletion(status_response, "Review Pacts");
|
|
72
|
+
}
|
|
73
|
+
async getStatus(statusUrl) {
|
|
74
|
+
const response = await fetch(statusUrl, {
|
|
75
|
+
method: "HEAD",
|
|
76
|
+
headers: this.headers,
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
status: response.status,
|
|
80
|
+
isComplete: response.status === 200,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async getResult(resultUrl) {
|
|
84
|
+
const response = await fetch(resultUrl, {
|
|
85
|
+
method: "GET",
|
|
86
|
+
headers: this.headers,
|
|
87
|
+
});
|
|
88
|
+
// Check if the response is OK (status 200)
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
91
|
+
}
|
|
92
|
+
return response.json();
|
|
93
|
+
}
|
|
94
|
+
async pollForCompletion(status_response, operationName) {
|
|
95
|
+
// Polling for completion
|
|
96
|
+
const startTime = Date.now();
|
|
97
|
+
const timeout = 120000; // 120 seconds
|
|
98
|
+
const pollInterval = 1000; // 1 second
|
|
99
|
+
while (Date.now() - startTime < timeout) {
|
|
100
|
+
const statusCheck = await this.getStatus(status_response.status_url);
|
|
101
|
+
if (statusCheck.isComplete) {
|
|
102
|
+
// Operation is complete, get the result
|
|
103
|
+
return await this.getResult(status_response.result_url);
|
|
104
|
+
}
|
|
105
|
+
if (statusCheck.status !== 202) {
|
|
106
|
+
throw new Error(`${operationName} failed with status: ${statusCheck.status}`);
|
|
107
|
+
}
|
|
108
|
+
// Wait before next poll
|
|
109
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
110
|
+
}
|
|
111
|
+
throw new Error(`${operationName} timed out after ${timeout / 1000} seconds`);
|
|
112
|
+
}
|
|
113
|
+
// PactFlow / Pact_Broker client methods
|
|
114
|
+
async getProviderStates({ provider }) {
|
|
115
|
+
const uri_encoded_provider_name = encodeURIComponent(provider);
|
|
116
|
+
const response = await fetch(`${this.baseUrl}/pacts/provider/${uri_encoded_provider_name}/provider-states`, {
|
|
117
|
+
method: "GET",
|
|
118
|
+
headers: this.headers,
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(`HTTP error! status: ${response.status} - ${await response.text()}`);
|
|
122
|
+
}
|
|
123
|
+
return response.json();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Checks if a given pacticipant version is safe to deploy
|
|
127
|
+
* to a specified environment.
|
|
128
|
+
*
|
|
129
|
+
* @param body - Input containing:
|
|
130
|
+
* - `pacticipant`: The name of the service (pacticipant).
|
|
131
|
+
* - `version`: The version of the pacticipant being evaluated for deployment.
|
|
132
|
+
* - `environment`: The target environment (e.g., staging, production).
|
|
133
|
+
* @returns CanIDeployResponse containing deployment decision and verification results.
|
|
134
|
+
* @throws Error if the request fails or returns a non-OK response.
|
|
135
|
+
*/
|
|
136
|
+
async canIDeploy(body) {
|
|
137
|
+
const { pacticipant, version, environment } = body;
|
|
138
|
+
const queryParams = new URLSearchParams({
|
|
139
|
+
pacticipant,
|
|
140
|
+
version,
|
|
141
|
+
environment,
|
|
142
|
+
});
|
|
143
|
+
const url = `${this.baseUrl}/can-i-deploy?${queryParams.toString()}`;
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch(url, {
|
|
146
|
+
method: "GET",
|
|
147
|
+
headers: this.headers,
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const errorText = await response.text().catch(() => "");
|
|
151
|
+
throw new Error(`Can-I-Deploy Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
152
|
+
}
|
|
153
|
+
return (await response.json());
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.error("[CanIDeploy] Unexpected error:", error);
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
registerTools(register, _getInput) {
|
|
161
|
+
for (const tool of TOOLS.filter(t => t.clients.includes(this.clientType))) {
|
|
162
|
+
const { handler, clients: _, formatResponse, ...toolparams } = tool; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
163
|
+
register(toolparams, async (args, _extra) => {
|
|
164
|
+
const handler_fn = this[handler];
|
|
165
|
+
if (typeof handler_fn !== "function") {
|
|
166
|
+
throw new Error(`Handler '${handler}' not found on PactClient`);
|
|
167
|
+
}
|
|
168
|
+
const result = await handler_fn.call(this, args);
|
|
169
|
+
// Use custom response formatter if provided
|
|
170
|
+
if (formatResponse) {
|
|
171
|
+
return formatResponse(result);
|
|
172
|
+
}
|
|
173
|
+
// Default fallback
|
|
174
|
+
return {
|
|
175
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
package/dist/reflect/client.js
CHANGED
|
@@ -3,6 +3,8 @@ import { z } from "zod";
|
|
|
3
3
|
// ReflectClient class implementing the Client interface
|
|
4
4
|
export class ReflectClient {
|
|
5
5
|
headers;
|
|
6
|
+
name = "Reflect";
|
|
7
|
+
prefix = "reflect";
|
|
6
8
|
constructor(token) {
|
|
7
9
|
this.headers = {
|
|
8
10
|
"X-API-KEY": `${token}`,
|
|
@@ -66,14 +68,29 @@ export class ReflectClient {
|
|
|
66
68
|
});
|
|
67
69
|
return response.json();
|
|
68
70
|
}
|
|
69
|
-
registerTools(
|
|
70
|
-
|
|
71
|
+
registerTools(register, _getInput) {
|
|
72
|
+
register({
|
|
73
|
+
title: "List Suites",
|
|
74
|
+
summary: "Retrieve a list of all reflect suites available",
|
|
75
|
+
parameters: [],
|
|
76
|
+
}, async (_args, _extra) => {
|
|
71
77
|
const response = await this.listReflectSuites();
|
|
72
78
|
return {
|
|
73
79
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
74
80
|
};
|
|
75
81
|
});
|
|
76
|
-
|
|
82
|
+
register({
|
|
83
|
+
title: "List Suite Executions",
|
|
84
|
+
summary: "List all executions for a given suite",
|
|
85
|
+
parameters: [
|
|
86
|
+
{
|
|
87
|
+
name: "suiteId",
|
|
88
|
+
type: z.string(),
|
|
89
|
+
description: "ID of the reflect suite to list executions for",
|
|
90
|
+
required: true
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
}, async (args, _extra) => {
|
|
77
94
|
if (!args.suiteId)
|
|
78
95
|
throw new Error("suiteId argument is required");
|
|
79
96
|
const response = await this.listSuiteExecutions(args.suiteId);
|
|
@@ -81,9 +98,23 @@ export class ReflectClient {
|
|
|
81
98
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
82
99
|
};
|
|
83
100
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
register({
|
|
102
|
+
title: "Get Suite Execution Status",
|
|
103
|
+
summary: "Get the status of a reflect suite execution",
|
|
104
|
+
parameters: [
|
|
105
|
+
{
|
|
106
|
+
name: "suiteId",
|
|
107
|
+
type: z.string(),
|
|
108
|
+
description: "ID of the reflect suite to get execution status for",
|
|
109
|
+
required: true
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "executionId",
|
|
113
|
+
type: z.string(),
|
|
114
|
+
description: "ID of the reflect suite execution to get status for",
|
|
115
|
+
required: true
|
|
116
|
+
},
|
|
117
|
+
],
|
|
87
118
|
}, async (args, _extra) => {
|
|
88
119
|
if (!args.suiteId || !args.executionId)
|
|
89
120
|
throw new Error("Both suiteId and executionId arguments are required");
|
|
@@ -92,7 +123,18 @@ export class ReflectClient {
|
|
|
92
123
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
93
124
|
};
|
|
94
125
|
});
|
|
95
|
-
|
|
126
|
+
register({
|
|
127
|
+
title: "Execute Suite",
|
|
128
|
+
summary: "Execute a reflect suite",
|
|
129
|
+
parameters: [
|
|
130
|
+
{
|
|
131
|
+
name: "suiteId",
|
|
132
|
+
type: z.string(),
|
|
133
|
+
description: "ID of the reflect suite to list executions for",
|
|
134
|
+
required: true
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}, async (args, _extra) => {
|
|
96
138
|
if (!args.suiteId)
|
|
97
139
|
throw new Error("suiteId argument is required");
|
|
98
140
|
const response = await this.executeSuite(args.suiteId);
|
|
@@ -100,9 +142,23 @@ export class ReflectClient {
|
|
|
100
142
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
101
143
|
};
|
|
102
144
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
145
|
+
register({
|
|
146
|
+
title: "Cancel Suite Execution",
|
|
147
|
+
summary: "Cancel a reflect suite execution",
|
|
148
|
+
parameters: [
|
|
149
|
+
{
|
|
150
|
+
name: "suiteId",
|
|
151
|
+
type: z.string(),
|
|
152
|
+
description: "ID of the reflect suite to cancel execution for",
|
|
153
|
+
required: true
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "executionId",
|
|
157
|
+
type: z.string(),
|
|
158
|
+
description: "ID of the reflect suite execution to cancel",
|
|
159
|
+
required: true
|
|
160
|
+
},
|
|
161
|
+
],
|
|
106
162
|
}, async (args, _extra) => {
|
|
107
163
|
if (!args.suiteId || !args.executionId)
|
|
108
164
|
throw new Error("Both suiteId and executionId arguments are required");
|
|
@@ -111,13 +167,28 @@ export class ReflectClient {
|
|
|
111
167
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
112
168
|
};
|
|
113
169
|
});
|
|
114
|
-
|
|
170
|
+
register({
|
|
171
|
+
title: "List Tests",
|
|
172
|
+
summary: "List all reflect tests",
|
|
173
|
+
parameters: [],
|
|
174
|
+
}, async (_args, _extra) => {
|
|
115
175
|
const response = await this.listReflectTests();
|
|
116
176
|
return {
|
|
117
177
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
118
178
|
};
|
|
119
179
|
});
|
|
120
|
-
|
|
180
|
+
register({
|
|
181
|
+
title: "Run Test",
|
|
182
|
+
summary: "Run a reflect test",
|
|
183
|
+
parameters: [
|
|
184
|
+
{
|
|
185
|
+
name: "testId",
|
|
186
|
+
type: z.string(),
|
|
187
|
+
description: "ID of the reflect test to run",
|
|
188
|
+
required: true
|
|
189
|
+
}
|
|
190
|
+
],
|
|
191
|
+
}, async (args, _extra) => {
|
|
121
192
|
if (!args.testId)
|
|
122
193
|
throw new Error("testId argument is required");
|
|
123
194
|
const response = await this.runReflectTest(args.testId);
|
|
@@ -125,9 +196,23 @@ export class ReflectClient {
|
|
|
125
196
|
content: [{ type: "text", text: JSON.stringify(response) }],
|
|
126
197
|
};
|
|
127
198
|
});
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
199
|
+
register({
|
|
200
|
+
title: "Get Test Status",
|
|
201
|
+
summary: "Get the status of a reflect test execution",
|
|
202
|
+
parameters: [
|
|
203
|
+
{
|
|
204
|
+
name: "testId",
|
|
205
|
+
type: z.string(),
|
|
206
|
+
description: "ID of the reflect test to run",
|
|
207
|
+
required: true
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "executionId",
|
|
211
|
+
type: z.string(),
|
|
212
|
+
description: "ID of the reflect test execution to get status for",
|
|
213
|
+
required: true
|
|
214
|
+
},
|
|
215
|
+
],
|
|
131
216
|
}, async (args, _extra) => {
|
|
132
217
|
if (!args.testId || !args.executionId)
|
|
133
218
|
throw new Error("Both testId and executionId arguments are required");
|
|
@@ -137,7 +222,4 @@ export class ReflectClient {
|
|
|
137
222
|
};
|
|
138
223
|
});
|
|
139
224
|
}
|
|
140
|
-
registerResources(_server) {
|
|
141
|
-
// Reflect does not currently support dynamic resources
|
|
142
|
-
}
|
|
143
225
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartbear/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MCP server for interacting SmartBear Products",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"smartbear",
|
|
7
7
|
"mcp",
|
|
8
|
-
"
|
|
8
|
+
"bugsnag",
|
|
9
9
|
"reflect",
|
|
10
|
-
"api-hub"
|
|
10
|
+
"api-hub",
|
|
11
|
+
"pactflow"
|
|
11
12
|
],
|
|
12
13
|
"homepage": "https://developer.smartbear.com/smartbear-mcp",
|
|
13
14
|
"repository": {
|
|
@@ -43,10 +44,12 @@
|
|
|
43
44
|
"@bugsnag/js": "^8.2.0",
|
|
44
45
|
"@modelcontextprotocol/sdk": "^1.15.0",
|
|
45
46
|
"node-cache": "^5.1.2",
|
|
47
|
+
"swagger-client": "^3.35.6",
|
|
46
48
|
"zod": "^3"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@eslint/js": "^9.29.0",
|
|
52
|
+
"@types/js-yaml": "^4.0.9",
|
|
50
53
|
"@types/node": "^22",
|
|
51
54
|
"@vitest/coverage-v8": "^3.2.4",
|
|
52
55
|
"eslint": "^9.29.0",
|
|
@@ -54,6 +57,7 @@
|
|
|
54
57
|
"shx": "^0.3.4",
|
|
55
58
|
"typescript": "^5.6.2",
|
|
56
59
|
"typescript-eslint": "^8.34.1",
|
|
57
|
-
"vitest": "^3.2.4"
|
|
60
|
+
"vitest": "^3.2.4",
|
|
61
|
+
"vitest-fetch-mock": "^0.4.5"
|
|
58
62
|
}
|
|
59
63
|
}
|
package/dist/common/templates.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
export function toolDescriptionTemplate(params) {
|
|
2
|
-
const { summary, useCases, examples, parameters, hints, outputFormat } = params;
|
|
3
|
-
let description = summary;
|
|
4
|
-
// Parameters (essential)
|
|
5
|
-
if (parameters.length > 0) {
|
|
6
|
-
description += `\n\n**Parameters:** ${parameters.map(p => `${p.name} (${p.type})${p.required ? ' *required*' : ''}`).join(', ')}`;
|
|
7
|
-
}
|
|
8
|
-
if (outputFormat) {
|
|
9
|
-
description += `\n\n**Output Format:** ${outputFormat}`;
|
|
10
|
-
}
|
|
11
|
-
// Use Cases
|
|
12
|
-
if (useCases.length > 0) {
|
|
13
|
-
description += `\n\n**Use Cases:** ${useCases.map((uc, i) => `${i + 1}. ${uc}`).join(' ')}`;
|
|
14
|
-
}
|
|
15
|
-
// Examples
|
|
16
|
-
if (examples.length > 0) {
|
|
17
|
-
description += `\n\n**Examples:**\n` + examples.map((ex, idx) => `${idx + 1}. ${ex.description}\n\`\`\`json\n${JSON.stringify(ex.parameters, null, 2)}\n\`\`\`${ex.expectedOutput ? `\nExpected Output: ${ex.expectedOutput}` : ''}`).join('\n\n');
|
|
18
|
-
}
|
|
19
|
-
// Hints
|
|
20
|
-
if (hints.length > 0) {
|
|
21
|
-
description += `\n\n**Tips:** ${hints.map((hint, i) => `${i + 1}. ${hint}`).join(' ')}`;
|
|
22
|
-
}
|
|
23
|
-
return description.trim();
|
|
24
|
-
}
|
|
25
|
-
// Backward-compatible version of the original function
|
|
26
|
-
export function simpleToolDescriptionTemplate(summary, useCases, examples, hints) {
|
|
27
|
-
return toolDescriptionTemplate({
|
|
28
|
-
summary,
|
|
29
|
-
purpose: summary,
|
|
30
|
-
useCases,
|
|
31
|
-
examples: examples.map(example => ({
|
|
32
|
-
description: example,
|
|
33
|
-
parameters: {}
|
|
34
|
-
})),
|
|
35
|
-
parameters: [],
|
|
36
|
-
hints
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
// Helper function to create parameter descriptions
|
|
40
|
-
export function createParameter(name, type, required, description, options = {}) {
|
|
41
|
-
return {
|
|
42
|
-
name,
|
|
43
|
-
type,
|
|
44
|
-
required,
|
|
45
|
-
description,
|
|
46
|
-
examples: options.examples,
|
|
47
|
-
constraints: options.constraints
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
// Helper function to create examples with proper structure
|
|
51
|
-
export function createExample(description, parameters, expectedOutput) {
|
|
52
|
-
return {
|
|
53
|
-
description,
|
|
54
|
-
parameters,
|
|
55
|
-
expectedOutput
|
|
56
|
-
};
|
|
57
|
-
}
|