@smartbear/mcp 0.4.0 → 0.6.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 +15 -121
- package/dist/{insight-hub → bugsnag}/client/api/CurrentUser.js +4 -4
- package/dist/{insight-hub → bugsnag}/client/api/Error.js +37 -4
- package/dist/bugsnag/client/api/Project.js +163 -0
- package/dist/{insight-hub → bugsnag}/client/api/base.js +39 -11
- package/dist/{insight-hub → bugsnag}/client/api/filters.js +2 -2
- package/dist/{insight-hub → bugsnag}/client.js +511 -29
- package/dist/common/info.js +1 -1
- package/dist/common/server.js +17 -5
- package/dist/index.js +11 -11
- package/dist/pactflow/client/ai.js +56 -6
- package/dist/pactflow/client/base.js +19 -1
- package/dist/pactflow/client/prompt-utils.js +89 -0
- package/dist/pactflow/client/prompts.js +133 -0
- package/dist/pactflow/client/tools.js +43 -2
- package/dist/pactflow/client/utils.js +70 -0
- package/dist/pactflow/client.js +192 -13
- package/package.json +9 -4
- package/dist/insight-hub/client/api/Project.js +0 -46
- package/dist/package.json +0 -60
- package/dist/tests/unit/common/server.test.js +0 -319
- package/dist/tests/unit/insight-hub/api-utilities.test.js +0 -31
- package/dist/tests/unit/insight-hub/client.test.js +0 -852
- package/dist/tests/unit/insight-hub/filters.test.js +0 -93
- package/dist/tests/unit/pactflow/ai.test.js +0 -21
- package/dist/tests/unit/pactflow/client.test.js +0 -67
- package/dist/tests/unit/pactflow/tools.test.js +0 -34
- 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
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { SmartBearMcpServer } from "../../../common/server.js";
|
|
3
|
-
import z from "zod";
|
|
4
|
-
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
import Bugsnag from "../../../common/bugsnag.js";
|
|
6
|
-
// Mock Bugsnag
|
|
7
|
-
vi.mock("../../../common/bugsnag.js", () => ({
|
|
8
|
-
default: {
|
|
9
|
-
notify: vi.fn(),
|
|
10
|
-
},
|
|
11
|
-
}));
|
|
12
|
-
describe("SmartBearMcpServer", () => {
|
|
13
|
-
let server;
|
|
14
|
-
let superRegisterToolMock;
|
|
15
|
-
let superRegisterResourceMock;
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
server = new SmartBearMcpServer();
|
|
18
|
-
// This approach is required to mock the super call - other techniques result in mocking the actual server
|
|
19
|
-
superRegisterToolMock = vi
|
|
20
|
-
.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(server)), "registerTool")
|
|
21
|
-
.mockImplementation(vi.fn());
|
|
22
|
-
superRegisterResourceMock = vi
|
|
23
|
-
.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(server)), "registerResource")
|
|
24
|
-
.mockImplementation(vi.fn());
|
|
25
|
-
server.server.elicitInput = vi.fn().mockResolvedValue("mocked input");
|
|
26
|
-
// Reset the Bugsnag mock
|
|
27
|
-
vi.mocked(Bugsnag.notify).mockClear();
|
|
28
|
-
});
|
|
29
|
-
describe("getDescription tests", () => {
|
|
30
|
-
it("should return the correct description", () => {
|
|
31
|
-
const zSchema = z.object({
|
|
32
|
-
examples: z.enum(["test_1", "test_2"]),
|
|
33
|
-
constraints: z.enum(["test_1", "test_2"]),
|
|
34
|
-
});
|
|
35
|
-
const toolparams = {
|
|
36
|
-
title: "Test Tool",
|
|
37
|
-
summary: "A test tool",
|
|
38
|
-
zodSchema: zSchema,
|
|
39
|
-
};
|
|
40
|
-
const description = server["getDescription"](toolparams);
|
|
41
|
-
expect(description).toBe(`A test tool
|
|
42
|
-
|
|
43
|
-
**Parameters:**
|
|
44
|
-
- examples (enum) *required* (e.g. test_1, test_2)
|
|
45
|
-
- constraints (enum) *required*
|
|
46
|
-
- test_1
|
|
47
|
-
- test_2`);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe("addClient", () => {
|
|
51
|
-
let mockClient;
|
|
52
|
-
beforeEach(() => {
|
|
53
|
-
mockClient = {
|
|
54
|
-
name: "Test Product",
|
|
55
|
-
prefix: "test_product",
|
|
56
|
-
registerTools: vi.fn(),
|
|
57
|
-
registerResources: vi.fn(),
|
|
58
|
-
};
|
|
59
|
-
});
|
|
60
|
-
it("should register tools when client provides them", async () => {
|
|
61
|
-
server.addClient(mockClient);
|
|
62
|
-
// The server should call the client's registerTools function
|
|
63
|
-
expect(mockClient.registerTools).toHaveBeenCalledWith(expect.any(Function), expect.any(Function));
|
|
64
|
-
// Get the register function passed from the server and execute it with test tool details
|
|
65
|
-
const registerFn = mockClient.registerTools.mock.calls[0][0];
|
|
66
|
-
const registerCbMock = vi.fn();
|
|
67
|
-
registerFn({
|
|
68
|
-
title: "Test Tool",
|
|
69
|
-
summary: "A test tool",
|
|
70
|
-
parameters: [
|
|
71
|
-
{
|
|
72
|
-
name: "p1",
|
|
73
|
-
type: z.string(),
|
|
74
|
-
required: true,
|
|
75
|
-
description: "The input for the tool",
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
}, registerCbMock);
|
|
79
|
-
expect(superRegisterToolMock).toHaveBeenCalledOnce();
|
|
80
|
-
// Assert some of the details
|
|
81
|
-
const registerToolParams = superRegisterToolMock.mock.calls[0];
|
|
82
|
-
expect(registerToolParams[0]).toBe("test_product_test_tool");
|
|
83
|
-
expect(registerToolParams[1]["title"]).toBe("Test Product: Test Tool");
|
|
84
|
-
expect(registerToolParams[1]["description"]).toBe("A test tool\n\n" +
|
|
85
|
-
"**Parameters:**\n" +
|
|
86
|
-
"- p1 (string) *required*: The input for the tool");
|
|
87
|
-
expect(registerToolParams[1]["inputSchema"]["p1"].toString()).toBe(z.string().describe("The input for the tool").toString());
|
|
88
|
-
expect(registerToolParams[1]["annotations"]).toEqual({
|
|
89
|
-
title: "Test Product: Test Tool",
|
|
90
|
-
readOnlyHint: true,
|
|
91
|
-
destructiveHint: false,
|
|
92
|
-
idempotentHint: true,
|
|
93
|
-
openWorldHint: false,
|
|
94
|
-
});
|
|
95
|
-
// Get the wrapper function that will execute the tool and call it
|
|
96
|
-
registerToolParams[2]();
|
|
97
|
-
expect(registerCbMock).toHaveBeenCalledOnce();
|
|
98
|
-
expect(vi.mocked(Bugsnag.notify)).not.toHaveBeenCalled();
|
|
99
|
-
});
|
|
100
|
-
it("should register tools with complex parameters", async () => {
|
|
101
|
-
server.addClient(mockClient);
|
|
102
|
-
// Get the register function passed from the server and execute it with test tool details
|
|
103
|
-
const registerFn = mockClient.registerTools.mock.calls[0][0];
|
|
104
|
-
registerFn({
|
|
105
|
-
title: "Test Tool",
|
|
106
|
-
summary: "A test tool",
|
|
107
|
-
parameters: [
|
|
108
|
-
{
|
|
109
|
-
name: "p1",
|
|
110
|
-
type: z.string(),
|
|
111
|
-
required: true,
|
|
112
|
-
description: "The input for the tool",
|
|
113
|
-
examples: ["example1", "example2"],
|
|
114
|
-
constraints: ["constraint1", "constraint2"],
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: "p2",
|
|
118
|
-
type: z.number(),
|
|
119
|
-
required: false,
|
|
120
|
-
description: "The optional numeric input for the tool",
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: "p3",
|
|
124
|
-
type: z.boolean(),
|
|
125
|
-
required: true,
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: "p4",
|
|
129
|
-
type: z.array(z.string()),
|
|
130
|
-
required: true,
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: "p5",
|
|
134
|
-
type: z.object({
|
|
135
|
-
key1: z.string(),
|
|
136
|
-
key2: z.number(),
|
|
137
|
-
}),
|
|
138
|
-
required: true,
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
name: "p6",
|
|
142
|
-
type: z.enum(["value1", "value2", "value3"]),
|
|
143
|
-
required: true,
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
name: "p7",
|
|
147
|
-
type: z.literal("value"),
|
|
148
|
-
required: true,
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: "p8",
|
|
152
|
-
type: z.union([z.literal("value1"), z.literal("value2")]),
|
|
153
|
-
required: true,
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
name: "p9",
|
|
157
|
-
type: z.any(),
|
|
158
|
-
required: true,
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
purpose: "To test the tool registration process",
|
|
162
|
-
useCases: ["Testing", "Development"],
|
|
163
|
-
examples: [
|
|
164
|
-
{
|
|
165
|
-
description: "Example 1",
|
|
166
|
-
parameters: {
|
|
167
|
-
p1: "example1",
|
|
168
|
-
p2: 42,
|
|
169
|
-
},
|
|
170
|
-
expectedOutput: "Expected output for example 1",
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
description: "Example 2",
|
|
174
|
-
parameters: {
|
|
175
|
-
p1: "example2",
|
|
176
|
-
p2: 24,
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
hints: ["First hint", "Second hint"],
|
|
181
|
-
outputFormat: "The output format",
|
|
182
|
-
readOnly: true,
|
|
183
|
-
destructive: true,
|
|
184
|
-
idempotent: true,
|
|
185
|
-
openWorld: true,
|
|
186
|
-
}, vi.fn());
|
|
187
|
-
// Assert some of the details
|
|
188
|
-
const registerToolParams = superRegisterToolMock.mock.calls[0];
|
|
189
|
-
expect(registerToolParams[0]).toBe("test_product_test_tool");
|
|
190
|
-
expect(registerToolParams[1]["title"]).toBe("Test Product: Test Tool");
|
|
191
|
-
expect(registerToolParams[1]["description"]).toBe("A test tool\n\n" +
|
|
192
|
-
"**Parameters:**\n" +
|
|
193
|
-
"- p1 (string) *required*: The input for the tool (e.g. example1, example2)\n" +
|
|
194
|
-
" - constraint1\n" +
|
|
195
|
-
" - constraint2\n" +
|
|
196
|
-
"- p2 (number): The optional numeric input for the tool\n" +
|
|
197
|
-
"- p3 (boolean) *required*\n" +
|
|
198
|
-
"- p4 (array) *required*\n" +
|
|
199
|
-
"- p5 (object) *required*\n" +
|
|
200
|
-
"- p6 (enum) *required*\n" +
|
|
201
|
-
"- p7 (literal) *required*\n" +
|
|
202
|
-
"- p8 (union) *required*\n" +
|
|
203
|
-
"- p9 (any) *required*\n\n" +
|
|
204
|
-
"**Output Format:** The output format\n\n" +
|
|
205
|
-
"**Use Cases:** 1. Testing 2. Development\n\n" +
|
|
206
|
-
"**Examples:**\n" +
|
|
207
|
-
"1. Example 1\n" +
|
|
208
|
-
"```json\n" +
|
|
209
|
-
"{\n" +
|
|
210
|
-
' "p1": "example1",\n' +
|
|
211
|
-
' "p2": 42\n' +
|
|
212
|
-
"}\n" +
|
|
213
|
-
"```\n" +
|
|
214
|
-
"Expected Output: Expected output for example 1\n\n" +
|
|
215
|
-
"2. Example 2\n" +
|
|
216
|
-
"```json\n" +
|
|
217
|
-
"{\n" +
|
|
218
|
-
' "p1": "example2",\n' +
|
|
219
|
-
' "p2": 24\n' +
|
|
220
|
-
"}\n" +
|
|
221
|
-
"```\n\n" +
|
|
222
|
-
"**Hints:** 1. First hint 2. Second hint");
|
|
223
|
-
expect(registerToolParams[1]["inputSchema"]["p1"].toString()).toBe(z.string().describe("The input for the tool").toString());
|
|
224
|
-
expect(registerToolParams[1]["annotations"]).toEqual({
|
|
225
|
-
title: "Test Product: Test Tool",
|
|
226
|
-
readOnlyHint: true,
|
|
227
|
-
destructiveHint: true,
|
|
228
|
-
idempotentHint: true,
|
|
229
|
-
openWorldHint: true,
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
it("should handle errors when registering tools", async () => {
|
|
233
|
-
server.addClient(mockClient);
|
|
234
|
-
// Get the register function passed from the server and execute it with test tool details
|
|
235
|
-
const registerFn = mockClient.registerTools.mock.calls[0][0];
|
|
236
|
-
const registerCbMock = vi.fn();
|
|
237
|
-
registerFn({
|
|
238
|
-
title: "Test Tool",
|
|
239
|
-
summary: "A test tool",
|
|
240
|
-
parameters: [],
|
|
241
|
-
}, registerCbMock);
|
|
242
|
-
// Make the callback throw an error to test error handling
|
|
243
|
-
registerCbMock.mockImplementation(() => {
|
|
244
|
-
throw new Error("Test error from registerCbMock");
|
|
245
|
-
});
|
|
246
|
-
// Get the wrapper function that will execute the tool and call it
|
|
247
|
-
const registerToolParams = superRegisterToolMock.mock.calls[0];
|
|
248
|
-
await expect(registerToolParams[2]()).rejects.toThrow("Test error from registerCbMock");
|
|
249
|
-
expect(registerCbMock).toHaveBeenCalledOnce();
|
|
250
|
-
expect(vi.mocked(Bugsnag.notify)).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
|
|
251
|
-
message: "Test error from registerCbMock",
|
|
252
|
-
}));
|
|
253
|
-
});
|
|
254
|
-
it("should elicit input when client requires it", async () => {
|
|
255
|
-
server.addClient(mockClient);
|
|
256
|
-
// The server should call the client's registerTools function
|
|
257
|
-
expect(mockClient.registerTools).toHaveBeenCalledWith(expect.any(Function), expect.any(Function));
|
|
258
|
-
// Get the register function passed from the server and execute it with test tool details
|
|
259
|
-
const getInputFn = mockClient.registerTools.mock.calls[0][1];
|
|
260
|
-
const params = vi.mockObject({});
|
|
261
|
-
const options = vi.mockObject({});
|
|
262
|
-
getInputFn(params, options);
|
|
263
|
-
expect(server.server.elicitInput).toHaveBeenCalledExactlyOnceWith(params, options);
|
|
264
|
-
});
|
|
265
|
-
it("should register resources when client provides them", async () => {
|
|
266
|
-
const mockClient = {
|
|
267
|
-
name: "Test Product",
|
|
268
|
-
prefix: "test_product",
|
|
269
|
-
registerTools: vi.fn(),
|
|
270
|
-
registerResources: vi.fn(),
|
|
271
|
-
};
|
|
272
|
-
server.addClient(mockClient);
|
|
273
|
-
// The server should call the client's registerResources function
|
|
274
|
-
expect(mockClient.registerResources).toHaveBeenCalledWith(expect.any(Function));
|
|
275
|
-
// Get the register function passed from the server and execute it with test resource details
|
|
276
|
-
const registerFn = mockClient.registerResources.mock.calls[0][0];
|
|
277
|
-
const registerCbMock = vi.fn();
|
|
278
|
-
registerFn("test_resource", "{identifier}", registerCbMock);
|
|
279
|
-
expect(superRegisterResourceMock).toHaveBeenCalledExactlyOnceWith(expect.any(String), expect.any(ResourceTemplate), expect.any(Object), expect.any(Function));
|
|
280
|
-
// Assert some of the details
|
|
281
|
-
const registerResourceParams = superRegisterResourceMock.mock.calls[0];
|
|
282
|
-
expect(registerResourceParams[0]).toBe("test_resource");
|
|
283
|
-
expect(registerResourceParams[1].uriTemplate.template).toBe("test_product://test_resource/{identifier}");
|
|
284
|
-
// Get the wrapper function that will execute the tool and call it
|
|
285
|
-
registerResourceParams[3]();
|
|
286
|
-
expect(registerCbMock).toHaveBeenCalledOnce();
|
|
287
|
-
expect(vi.mocked(Bugsnag.notify)).not.toHaveBeenCalled();
|
|
288
|
-
});
|
|
289
|
-
it("should not register resources when client does not provide them", async () => {
|
|
290
|
-
const mockClient = {
|
|
291
|
-
name: "Test Product",
|
|
292
|
-
prefix: "test_product",
|
|
293
|
-
registerTools: vi.fn(),
|
|
294
|
-
registerResources: undefined,
|
|
295
|
-
};
|
|
296
|
-
server.addClient(mockClient);
|
|
297
|
-
// It should not crash with undefined registerResources function
|
|
298
|
-
expect(vi.mocked(Bugsnag.notify)).not.toHaveBeenCalled();
|
|
299
|
-
});
|
|
300
|
-
it("should handle errors when registering resources", async () => {
|
|
301
|
-
server.addClient(mockClient);
|
|
302
|
-
// Get the register function passed from the server and execute it with test resource details
|
|
303
|
-
const registerFn = mockClient.registerResources.mock.calls[0][0];
|
|
304
|
-
const registerCbMock = vi.fn();
|
|
305
|
-
registerFn("test_resource", "{identifier}", registerCbMock);
|
|
306
|
-
// Make the callback throw an error to test error handling
|
|
307
|
-
registerCbMock.mockImplementation(() => {
|
|
308
|
-
throw new Error("Test error from registerCbMock");
|
|
309
|
-
});
|
|
310
|
-
// Get the wrapper function that will execute the resource and call it
|
|
311
|
-
const registerResourceParams = superRegisterResourceMock.mock.calls[0];
|
|
312
|
-
await expect(registerResourceParams[3]()).rejects.toThrow("Test error from registerCbMock");
|
|
313
|
-
expect(registerCbMock).toHaveBeenCalledOnce();
|
|
314
|
-
expect(vi.mocked(Bugsnag.notify)).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
|
|
315
|
-
message: "Test error from registerCbMock",
|
|
316
|
-
}));
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { pickFields, pickFieldsFromArray } from '../../../insight-hub/client/api/base.js';
|
|
3
|
-
describe('API Utilities', () => {
|
|
4
|
-
describe('pickFields', () => {
|
|
5
|
-
it('should pick only specified fields from object', () => {
|
|
6
|
-
const input = { id: '1', name: 'Test', secret: 'hidden', extra: 'data' };
|
|
7
|
-
const result = pickFields(input, ['id', 'name']);
|
|
8
|
-
expect(result).toEqual({ id: '1', name: 'Test' });
|
|
9
|
-
expect(result).not.toHaveProperty('secret');
|
|
10
|
-
expect(result).not.toHaveProperty('extra');
|
|
11
|
-
});
|
|
12
|
-
it('should handle missing fields gracefully', () => {
|
|
13
|
-
const input = { id: '1' };
|
|
14
|
-
const result = pickFields(input, ['id', 'name', 'missing']);
|
|
15
|
-
expect(result).toEqual({ id: '1' });
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe('pickFieldsFromArray', () => {
|
|
19
|
-
it('should pick fields from array of objects', () => {
|
|
20
|
-
const input = [
|
|
21
|
-
{ id: '1', name: 'Test1', secret: 'hidden1' },
|
|
22
|
-
{ id: '2', name: 'Test2', secret: 'hidden2' }
|
|
23
|
-
];
|
|
24
|
-
const result = pickFieldsFromArray(input, ['id', 'name']);
|
|
25
|
-
expect(result).toEqual([
|
|
26
|
-
{ id: '1', name: 'Test1' },
|
|
27
|
-
{ id: '2', name: 'Test2' }
|
|
28
|
-
]);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
});
|