@slbdn/mcp-tester 1.0.1 ā 1.1.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/CHANGELOG.md +70 -82
- package/README.md +231 -1438
- package/dist/assert.d.ts +75 -0
- package/dist/assert.d.ts.map +1 -0
- package/dist/assert.js +250 -0
- package/dist/assert.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +311 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/MCPClient.d.ts +316 -0
- package/dist/client/MCPClient.d.ts.map +1 -1
- package/dist/client/MCPClient.js +240 -9
- package/dist/client/MCPClient.js.map +1 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +262 -0
- package/dist/matchers.d.ts.map +1 -0
- package/dist/matchers.js +527 -0
- package/dist/matchers.js.map +1 -0
- package/dist/utils/errors.d.ts +38 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +38 -0
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/logger.d.ts +57 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +32 -3
- package/dist/utils/logger.js.map +1 -1
- package/package.json +32 -11
- package/vitest.d.ts +51 -0
package/dist/assert.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone assertion utilities for MCP tool/resource/prompt testing.
|
|
3
|
+
*
|
|
4
|
+
* Works with any test runner ā just throws descriptive errors on failure.
|
|
5
|
+
* Designed for the `runTest(name, fn)` pattern:
|
|
6
|
+
*
|
|
7
|
+
* await runTest("Get constant: c", async () => {
|
|
8
|
+
* const result = await client.callTool({ name: "get_constant", arguments: { name: "c" } });
|
|
9
|
+
* assertToolText(result, "299792458");
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* @module assert
|
|
13
|
+
*/
|
|
14
|
+
import type { CallToolResult, ReadResourceResult, GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
|
|
15
|
+
/** Thrown when an assertion fails. */
|
|
16
|
+
export declare class AssertionError extends Error {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
/** Assert that `actual === expected` (strict equality). */
|
|
20
|
+
export declare function equal<T>(actual: T, expected: T, msg?: string): void;
|
|
21
|
+
/** Assert that `actual !== expected`. */
|
|
22
|
+
export declare function notEqual<T>(actual: T, expected: T, msg?: string): void;
|
|
23
|
+
/** Assert that `actual` is deeply equal to `expected`. */
|
|
24
|
+
export declare function deepEqual(actual: unknown, expected: unknown, msg?: string): void;
|
|
25
|
+
/** Assert that `value` is truthy. */
|
|
26
|
+
export declare function ok(value: unknown, msg?: string): void;
|
|
27
|
+
/** Assert that `value` is falsy. */
|
|
28
|
+
export declare function notOk(value: unknown, msg?: string): void;
|
|
29
|
+
/** Assert that `fn` throws an error. Returns the error for further assertions. */
|
|
30
|
+
export declare function throws(fn: () => Promise<unknown>, msg?: string): Promise<Error>;
|
|
31
|
+
/** Assert that `fn` does NOT throw. */
|
|
32
|
+
export declare function doesNotThrow(fn: () => Promise<unknown>, msg?: string): Promise<void>;
|
|
33
|
+
/** Assert that `actual === expected` (numbers). */
|
|
34
|
+
export declare function equalNum(actual: number, expected: number, msg?: string): void;
|
|
35
|
+
/** Assert `actual > expected`. */
|
|
36
|
+
export declare function greaterThan(actual: number, expected: number, msg?: string): void;
|
|
37
|
+
/** Assert `actual >= expected`. */
|
|
38
|
+
export declare function atLeast(actual: number, expected: number, msg?: string): void;
|
|
39
|
+
/** Assert `actual < expected`. */
|
|
40
|
+
export declare function lessThan(actual: number, expected: number, msg?: string): void;
|
|
41
|
+
/** Assert `value` is within `epsilon` of `expected`. */
|
|
42
|
+
export declare function closeTo(actual: number, expected: number, epsilon?: number, msg?: string): void;
|
|
43
|
+
/** Assert that `str` contains `substring`. */
|
|
44
|
+
export declare function contains(str: string, substring: string, msg?: string): void;
|
|
45
|
+
/** Assert that `str` does NOT contain `substring`. */
|
|
46
|
+
export declare function notContains(str: string, substring: string, msg?: string): void;
|
|
47
|
+
/** Assert that `str` matches `regex`. */
|
|
48
|
+
export declare function matches(str: string, regex: RegExp, msg?: string): void;
|
|
49
|
+
/** Assert tool result text equals expected string exactly. If expected omitted, just checks for text content. */
|
|
50
|
+
export declare function toolTextEquals(result: CallToolResult, expected?: string, msg?: string): void;
|
|
51
|
+
/** Assert tool result text contains expected substring. */
|
|
52
|
+
export declare function toolTextContains(result: CallToolResult, substring: string, msg?: string): void;
|
|
53
|
+
/** Assert tool result text, parsed as number, equals expected. */
|
|
54
|
+
export declare function toolNumEquals(result: CallToolResult, expected: number, msg?: string): void;
|
|
55
|
+
/** Assert tool result text, parsed as number, is within epsilon of expected. */
|
|
56
|
+
export declare function toolNumCloseTo(result: CallToolResult, expected: number, epsilon?: number, msg?: string): void;
|
|
57
|
+
/** Assert tool result text, parsed as JSON, deeply equals expected. */
|
|
58
|
+
export declare function toolJsonEquals<T = unknown>(result: CallToolResult, expected: T, msg?: string): void;
|
|
59
|
+
/** Assert tool returned an error (isError flag or error in text). */
|
|
60
|
+
export declare function toolIsError(result: CallToolResult, msg?: string): void;
|
|
61
|
+
/** Assert tool did NOT return an error. */
|
|
62
|
+
export declare function toolIsOk(result: CallToolResult, msg?: string): void;
|
|
63
|
+
/** Assert tool result has at least `minItems` content items. */
|
|
64
|
+
export declare function toolHasContent(result: CallToolResult, minItems?: number, msg?: string): void;
|
|
65
|
+
/** Assert tool result contains an image content item. */
|
|
66
|
+
export declare function toolHasImage(result: CallToolResult, msg?: string): void;
|
|
67
|
+
/** Assert resource result has content. */
|
|
68
|
+
export declare function resourceHasContent(result: ReadResourceResult, minItems?: number, msg?: string): void;
|
|
69
|
+
/** Assert resource result text contains substring. */
|
|
70
|
+
export declare function resourceTextContains(result: ReadResourceResult, substring: string, msg?: string): void;
|
|
71
|
+
/** Assert prompt result has messages. */
|
|
72
|
+
export declare function promptHasMessages(result: GetPromptResult, minMessages?: number, msg?: string): void;
|
|
73
|
+
/** Assert prompt result text contains substring. */
|
|
74
|
+
export declare function promptTextContains(result: GetPromptResult, substring: string, msg?: string): void;
|
|
75
|
+
//# sourceMappingURL=assert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"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"}
|
package/dist/assert.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone assertion utilities for MCP tool/resource/prompt testing.
|
|
3
|
+
*
|
|
4
|
+
* Works with any test runner ā just throws descriptive errors on failure.
|
|
5
|
+
* Designed for the `runTest(name, fn)` pattern:
|
|
6
|
+
*
|
|
7
|
+
* await runTest("Get constant: c", async () => {
|
|
8
|
+
* const result = await client.callTool({ name: "get_constant", arguments: { name: "c" } });
|
|
9
|
+
* assertToolText(result, "299792458");
|
|
10
|
+
* });
|
|
11
|
+
*
|
|
12
|
+
* @module assert
|
|
13
|
+
*/
|
|
14
|
+
// āāā Helpers āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
15
|
+
function extractText(result) {
|
|
16
|
+
const item = result.content[0];
|
|
17
|
+
if (item && 'text' in item)
|
|
18
|
+
return item.text;
|
|
19
|
+
throw new AssertionError(`Tool result has no text content. Got: ${JSON.stringify(result.content[0])}`);
|
|
20
|
+
}
|
|
21
|
+
function fail(message) {
|
|
22
|
+
throw new AssertionError(message);
|
|
23
|
+
}
|
|
24
|
+
// āāā Error Class āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
25
|
+
/** Thrown when an assertion fails. */
|
|
26
|
+
export class AssertionError extends Error {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'AssertionError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// āāā Value Assertions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
33
|
+
/** Assert that `actual === expected` (strict equality). */
|
|
34
|
+
export function equal(actual, expected, msg) {
|
|
35
|
+
if (actual !== expected) {
|
|
36
|
+
fail(msg ?? `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Assert that `actual !== expected`. */
|
|
40
|
+
export function notEqual(actual, expected, msg) {
|
|
41
|
+
if (actual === expected) {
|
|
42
|
+
fail(msg ?? `Expected value to NOT equal ${JSON.stringify(expected)}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Assert that `actual` is deeply equal to `expected`. */
|
|
46
|
+
export function deepEqual(actual, expected, msg) {
|
|
47
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
48
|
+
fail(msg ??
|
|
49
|
+
`Expected deep equality:\n actual: ${JSON.stringify(actual)}\n expected: ${JSON.stringify(expected)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Assert that `value` is truthy. */
|
|
53
|
+
export function ok(value, msg) {
|
|
54
|
+
if (!value) {
|
|
55
|
+
fail(msg ?? `Expected truthy value, got ${JSON.stringify(value)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Assert that `value` is falsy. */
|
|
59
|
+
export function notOk(value, msg) {
|
|
60
|
+
if (value) {
|
|
61
|
+
fail(msg ?? `Expected falsy value, got ${JSON.stringify(value)}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Assert that `fn` throws an error. Returns the error for further assertions. */
|
|
65
|
+
export async function throws(fn, msg) {
|
|
66
|
+
try {
|
|
67
|
+
await fn();
|
|
68
|
+
fail(msg ?? 'Expected function to throw, but it did not');
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof AssertionError)
|
|
72
|
+
throw error;
|
|
73
|
+
return error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Assert that `fn` does NOT throw. */
|
|
77
|
+
export async function doesNotThrow(fn, msg) {
|
|
78
|
+
try {
|
|
79
|
+
await fn();
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
fail(msg ?? `Expected function NOT to throw, but it threw: ${error}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// āāā Numeric Assertions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
86
|
+
/** Assert that `actual === expected` (numbers). */
|
|
87
|
+
export function equalNum(actual, expected, msg) {
|
|
88
|
+
if (actual !== expected) {
|
|
89
|
+
fail(msg ?? `Expected ${expected}, got ${actual}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Assert `actual > expected`. */
|
|
93
|
+
export function greaterThan(actual, expected, msg) {
|
|
94
|
+
if (actual <= expected) {
|
|
95
|
+
fail(msg ?? `Expected ${actual} > ${expected}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Assert `actual >= expected`. */
|
|
99
|
+
export function atLeast(actual, expected, msg) {
|
|
100
|
+
if (actual < expected) {
|
|
101
|
+
fail(msg ?? `Expected ${actual} >= ${expected}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Assert `actual < expected`. */
|
|
105
|
+
export function lessThan(actual, expected, msg) {
|
|
106
|
+
if (actual >= expected) {
|
|
107
|
+
fail(msg ?? `Expected ${actual} < ${expected}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/** Assert `value` is within `epsilon` of `expected`. */
|
|
111
|
+
export function closeTo(actual, expected, epsilon = 0.001, msg) {
|
|
112
|
+
if (Math.abs(actual - expected) > epsilon) {
|
|
113
|
+
fail(msg ?? `Expected ${actual} to be within ${epsilon} of ${expected}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// āāā String Assertions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
117
|
+
/** Assert that `str` contains `substring`. */
|
|
118
|
+
export function contains(str, substring, msg) {
|
|
119
|
+
if (!str.includes(substring)) {
|
|
120
|
+
fail(msg ?? `Expected string to contain "${substring}". Got: "${str}"`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Assert that `str` does NOT contain `substring`. */
|
|
124
|
+
export function notContains(str, substring, msg) {
|
|
125
|
+
if (str.includes(substring)) {
|
|
126
|
+
fail(msg ?? `Expected string to NOT contain "${substring}". Got: "${str}"`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Assert that `str` matches `regex`. */
|
|
130
|
+
export function matches(str, regex, msg) {
|
|
131
|
+
if (!regex.test(str)) {
|
|
132
|
+
fail(msg ?? `Expected string to match ${regex}. Got: "${str}"`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// āāā MCP Tool Assertions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
136
|
+
/** Assert tool result text equals expected string exactly. If expected omitted, just checks for text content. */
|
|
137
|
+
export function toolTextEquals(result, expected, msg) {
|
|
138
|
+
const text = extractText(result);
|
|
139
|
+
if (text === undefined) {
|
|
140
|
+
fail(msg ?? 'Expected tool result to have text content');
|
|
141
|
+
}
|
|
142
|
+
if (expected !== undefined && text !== expected) {
|
|
143
|
+
fail(msg ?? `Expected tool text "${expected}", got "${text}"`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** Assert tool result text contains expected substring. */
|
|
147
|
+
export function toolTextContains(result, substring, msg) {
|
|
148
|
+
const text = extractText(result);
|
|
149
|
+
if (!text.includes(substring)) {
|
|
150
|
+
fail(msg ?? `Expected tool text to contain "${substring}". Got: "${text}"`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Assert tool result text, parsed as number, equals expected. */
|
|
154
|
+
export function toolNumEquals(result, expected, msg) {
|
|
155
|
+
const text = extractText(result);
|
|
156
|
+
const actual = parseFloat(text);
|
|
157
|
+
if (Number.isNaN(actual)) {
|
|
158
|
+
fail(msg ?? `Expected tool text to be a number. Got: "${text}"`);
|
|
159
|
+
}
|
|
160
|
+
if (actual !== expected) {
|
|
161
|
+
fail(msg ?? `Expected tool result ${expected}, got ${actual}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Assert tool result text, parsed as number, is within epsilon of expected. */
|
|
165
|
+
export function toolNumCloseTo(result, expected, epsilon = 0.001, msg) {
|
|
166
|
+
const text = extractText(result);
|
|
167
|
+
const actual = parseFloat(text);
|
|
168
|
+
if (Number.isNaN(actual)) {
|
|
169
|
+
fail(msg ?? `Expected tool text to be a number. Got: "${text}"`);
|
|
170
|
+
}
|
|
171
|
+
if (Math.abs(actual - expected) > epsilon) {
|
|
172
|
+
fail(msg ?? `Expected tool result within ${epsilon} of ${expected}, got ${actual}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Assert tool result text, parsed as JSON, deeply equals expected. */
|
|
176
|
+
export function toolJsonEquals(result, expected, msg) {
|
|
177
|
+
const text = extractText(result);
|
|
178
|
+
let parsed;
|
|
179
|
+
try {
|
|
180
|
+
parsed = JSON.parse(text);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
fail(msg ?? `Expected tool text to be valid JSON. Got: "${text}"`);
|
|
184
|
+
}
|
|
185
|
+
if (JSON.stringify(parsed) !== JSON.stringify(expected)) {
|
|
186
|
+
fail(msg ?? `Expected tool JSON ${JSON.stringify(expected)}, got ${JSON.stringify(parsed)}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Assert tool returned an error (isError flag or error in text). */
|
|
190
|
+
export function toolIsError(result, msg) {
|
|
191
|
+
if (!result.isError && !extractText(result).toLowerCase().includes('error')) {
|
|
192
|
+
fail(msg ?? 'Expected tool result to be an error');
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** Assert tool did NOT return an error. */
|
|
196
|
+
export function toolIsOk(result, msg) {
|
|
197
|
+
if (result.isError) {
|
|
198
|
+
fail(msg ?? `Expected tool result to be successful, but got error: ${extractText(result)}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** Assert tool result has at least `minItems` content items. */
|
|
202
|
+
export function toolHasContent(result, minItems = 1, msg) {
|
|
203
|
+
if (result.content.length < minItems) {
|
|
204
|
+
fail(msg ?? `Expected at least ${minItems} content items, got ${result.content.length}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/** Assert tool result contains an image content item. */
|
|
208
|
+
export function toolHasImage(result, msg) {
|
|
209
|
+
const hasImage = result.content.some((item) => item.type === 'image');
|
|
210
|
+
if (!hasImage) {
|
|
211
|
+
fail(msg ?? 'Expected tool result to contain an image');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// āāā MCP Resource Assertions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
215
|
+
/** Assert resource result has content. */
|
|
216
|
+
export function resourceHasContent(result, minItems = 1, msg) {
|
|
217
|
+
if (result.contents.length < minItems) {
|
|
218
|
+
fail(msg ?? `Expected at least ${minItems} resource contents, got ${result.contents.length}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Assert resource result text contains substring. */
|
|
222
|
+
export function resourceTextContains(result, substring, msg) {
|
|
223
|
+
const item = result.contents[0];
|
|
224
|
+
if (!item || !('text' in item)) {
|
|
225
|
+
fail(msg ?? 'Resource result has no text content');
|
|
226
|
+
}
|
|
227
|
+
const text = item.text;
|
|
228
|
+
if (!text.includes(substring)) {
|
|
229
|
+
fail(msg ?? `Expected resource text to contain "${substring}". Got: "${text}"`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// āāā MCP Prompt Assertions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
233
|
+
/** Assert prompt result has messages. */
|
|
234
|
+
export function promptHasMessages(result, minMessages = 1, msg) {
|
|
235
|
+
if (result.messages.length < minMessages) {
|
|
236
|
+
fail(msg ?? `Expected at least ${minMessages} prompt messages, got ${result.messages.length}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/** Assert prompt result text contains substring. */
|
|
240
|
+
export function promptTextContains(result, substring, msg) {
|
|
241
|
+
const item = result.messages[0]?.content;
|
|
242
|
+
if (!item || !('text' in item)) {
|
|
243
|
+
fail(msg ?? 'Prompt message has no text content');
|
|
244
|
+
}
|
|
245
|
+
const text = item.text;
|
|
246
|
+
if (!text.includes(substring)) {
|
|
247
|
+
fail(msg ?? `Expected prompt text to contain "${substring}". Got: "${text}"`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=assert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CLI entry point - run with: node dist/cli/index.js
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { MCPClient } from '../client/MCPClient.js';
|
|
5
|
+
import { MCPConnectionError, MCPTimeoutError, MCPServerError } from '../utils/errors.js';
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('mcp-tester')
|
|
9
|
+
.description('CLI tool for testing MCP servers')
|
|
10
|
+
.version('1.0.0', '-V, --version');
|
|
11
|
+
// Global options - use string type since commander passes strings
|
|
12
|
+
program
|
|
13
|
+
.option('-t, --timeout <ms>', 'Request timeout in milliseconds', '30000')
|
|
14
|
+
.option('-v, --verbose', 'Enable verbose output')
|
|
15
|
+
.option('-l, --log-level <level>', 'Log level (debug|info|warn|error)', 'info');
|
|
16
|
+
// Test command - connects and verifies server is working
|
|
17
|
+
program
|
|
18
|
+
.command('test')
|
|
19
|
+
.description('Test MCP server connection and list available capabilities')
|
|
20
|
+
.argument('<command>', 'Command to run the MCP server (e.g., "node", "python")')
|
|
21
|
+
.argument('[args...]', 'Arguments for the server command')
|
|
22
|
+
.action(async (command, args, opts) => {
|
|
23
|
+
const client = new MCPClient({
|
|
24
|
+
name: 'mcp-tester-cli',
|
|
25
|
+
version: '1.0.0',
|
|
26
|
+
timeout: parseInt(String(opts['timeout'] || '30000')),
|
|
27
|
+
logLevel: (opts['verbose'] ? 'debug' : String(opts['logLevel'] || 'info')),
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
console.log(`š Connecting to MCP server...`);
|
|
31
|
+
console.log(` Command: ${command} ${args.join(' ')}`);
|
|
32
|
+
await client.start({
|
|
33
|
+
command,
|
|
34
|
+
args,
|
|
35
|
+
});
|
|
36
|
+
console.log(`ā
Connected successfully\n`);
|
|
37
|
+
// Test tools
|
|
38
|
+
console.log(`š ļø Tools:`);
|
|
39
|
+
const tools = await client.listTools();
|
|
40
|
+
if (tools.length === 0) {
|
|
41
|
+
console.log(' (none)');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
tools.forEach((tool) => {
|
|
45
|
+
console.log(` ⢠${tool.name}: ${tool.description}`);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
console.log();
|
|
49
|
+
// Test resources
|
|
50
|
+
console.log(`š Resources:`);
|
|
51
|
+
const resources = await client.listResources();
|
|
52
|
+
if (resources.length === 0) {
|
|
53
|
+
console.log(' (none)');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
resources.forEach((resource) => {
|
|
57
|
+
console.log(` ⢠${resource.uri}: ${resource.description || resource.name}`);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
console.log();
|
|
61
|
+
// Test prompts
|
|
62
|
+
console.log(`š¬ Prompts:`);
|
|
63
|
+
const prompts = await client.listPrompts();
|
|
64
|
+
if (prompts.length === 0) {
|
|
65
|
+
console.log(' (none)');
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
prompts.forEach((prompt) => {
|
|
69
|
+
console.log(` ⢠${prompt.name}: ${prompt.description}`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
console.log();
|
|
73
|
+
console.log(`ā
Server test completed successfully!`);
|
|
74
|
+
console.log(` Tools: ${tools.length} | Resources: ${resources.length} | Prompts: ${prompts.length}`);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(`ā Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
78
|
+
if (error instanceof MCPConnectionError) {
|
|
79
|
+
console.error(' ā Server failed to start. Check command and server process.');
|
|
80
|
+
}
|
|
81
|
+
else if (error instanceof MCPTimeoutError) {
|
|
82
|
+
console.error(` ā Request timed out after ${opts['timeout']}ms`);
|
|
83
|
+
}
|
|
84
|
+
else if (error instanceof MCPServerError) {
|
|
85
|
+
console.error(' ā Server returned an error');
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
await client.stop();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
// List tools command
|
|
94
|
+
program
|
|
95
|
+
.command('list-tools')
|
|
96
|
+
.alias('lt')
|
|
97
|
+
.description('List all available tools from the MCP server')
|
|
98
|
+
.argument('<command>', 'Command to run the MCP server')
|
|
99
|
+
.argument('[args...]', 'Arguments for the server command')
|
|
100
|
+
.option('--json', 'Output as JSON')
|
|
101
|
+
.action(async (command, args, opts) => {
|
|
102
|
+
const client = new MCPClient({
|
|
103
|
+
name: 'mcp-tester-cli',
|
|
104
|
+
version: '1.0.0',
|
|
105
|
+
timeout: parseInt(String(opts['timeout'] || '30000')),
|
|
106
|
+
logLevel: opts['verbose'] ? 'debug' : 'info',
|
|
107
|
+
});
|
|
108
|
+
try {
|
|
109
|
+
await client.start({ command, args });
|
|
110
|
+
const tools = await client.listTools();
|
|
111
|
+
if (opts['json']) {
|
|
112
|
+
console.log(JSON.stringify({ tools }, null, 2));
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
console.log(`š ļø Available tools (${tools.length}):\n`);
|
|
116
|
+
tools.forEach((tool) => {
|
|
117
|
+
console.log(` ${tool.name}`);
|
|
118
|
+
console.log(` Description: ${tool.description}`);
|
|
119
|
+
if (tool.inputSchema?.properties) {
|
|
120
|
+
const props = Object.keys(tool.inputSchema.properties);
|
|
121
|
+
console.log(` Parameters: ${props.join(', ') || '(none)'}`);
|
|
122
|
+
}
|
|
123
|
+
console.log();
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.error(`ā Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await client.stop();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
// Call tool command
|
|
136
|
+
program
|
|
137
|
+
.command('call-tool')
|
|
138
|
+
.alias('ct')
|
|
139
|
+
.description('Call a tool on the MCP server')
|
|
140
|
+
.argument('<tool-name>', 'Name of the tool to call')
|
|
141
|
+
.argument('<command>', 'Command to run the MCP server')
|
|
142
|
+
.argument('[args...]', 'Arguments for the server command')
|
|
143
|
+
.option('--params <json>', 'Tool parameters as JSON string')
|
|
144
|
+
.option('--json', 'Output as JSON')
|
|
145
|
+
.action(async (toolName, command, serverArgs, opts) => {
|
|
146
|
+
const client = new MCPClient({
|
|
147
|
+
name: 'mcp-tester-cli',
|
|
148
|
+
version: '1.0.0',
|
|
149
|
+
timeout: parseInt(String(opts['timeout'] || '30000')),
|
|
150
|
+
logLevel: opts['verbose'] ? 'debug' : 'info',
|
|
151
|
+
});
|
|
152
|
+
let toolArgs = {};
|
|
153
|
+
if (opts['params']) {
|
|
154
|
+
try {
|
|
155
|
+
toolArgs = JSON.parse(String(opts['params']));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
console.error('ā Invalid JSON for --params');
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
await client.start({ command, args: serverArgs });
|
|
164
|
+
if (!opts['json']) {
|
|
165
|
+
console.log(`š§ Calling tool: ${toolName}`);
|
|
166
|
+
if (Object.keys(toolArgs).length > 0) {
|
|
167
|
+
console.log(` Parameters: ${JSON.stringify(toolArgs)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const result = await client.callTool({
|
|
171
|
+
name: toolName,
|
|
172
|
+
arguments: toolArgs,
|
|
173
|
+
});
|
|
174
|
+
if (opts['json']) {
|
|
175
|
+
console.log(JSON.stringify(result, null, 2));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
console.log('\nā
Result:');
|
|
179
|
+
result.content.forEach((item) => {
|
|
180
|
+
if ('text' in item) {
|
|
181
|
+
console.log(` ${item.text}`);
|
|
182
|
+
}
|
|
183
|
+
else if ('data' in item) {
|
|
184
|
+
console.log(` ${JSON.stringify(item.data)}`);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.error(`ā Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
await client.stop();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
// Read resource command
|
|
198
|
+
program
|
|
199
|
+
.command('read-resource')
|
|
200
|
+
.alias('rr')
|
|
201
|
+
.description('Read a resource from the MCP server')
|
|
202
|
+
.argument('<uri>', 'URI of the resource to read')
|
|
203
|
+
.argument('<command>', 'Command to run the MCP server')
|
|
204
|
+
.argument('[args...]', 'Arguments for the server command')
|
|
205
|
+
.option('--json', 'Output as JSON')
|
|
206
|
+
.action(async (uri, command, serverArgs, opts) => {
|
|
207
|
+
const client = new MCPClient({
|
|
208
|
+
name: 'mcp-tester-cli',
|
|
209
|
+
version: '1.0.0',
|
|
210
|
+
timeout: parseInt(String(opts['timeout'] || '30000')),
|
|
211
|
+
logLevel: opts['verbose'] ? 'debug' : 'info',
|
|
212
|
+
});
|
|
213
|
+
try {
|
|
214
|
+
await client.start({ command, args: serverArgs });
|
|
215
|
+
if (!opts['json']) {
|
|
216
|
+
console.log(`š Reading resource: ${uri}`);
|
|
217
|
+
}
|
|
218
|
+
const result = await client.readResource(uri);
|
|
219
|
+
if (opts['json']) {
|
|
220
|
+
console.log(JSON.stringify(result, null, 2));
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
result.contents.forEach((item) => {
|
|
224
|
+
if ('text' in item) {
|
|
225
|
+
console.log('\nš Content:');
|
|
226
|
+
console.log(` ${item.text}`);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
console.error(`ā Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
await client.stop();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
// Get prompt command
|
|
240
|
+
program
|
|
241
|
+
.command('get-prompt')
|
|
242
|
+
.alias('gp')
|
|
243
|
+
.description('Get a prompt from the MCP server')
|
|
244
|
+
.argument('<name>', 'Name of the prompt')
|
|
245
|
+
.argument('<command>', 'Command to run the MCP server')
|
|
246
|
+
.argument('[args...]', 'Arguments for the server command')
|
|
247
|
+
.option('--args <json>', 'Prompt arguments as JSON string')
|
|
248
|
+
.option('--json', 'Output as JSON')
|
|
249
|
+
.action(async (promptName, command, serverArgs, opts) => {
|
|
250
|
+
const client = new MCPClient({
|
|
251
|
+
name: 'mcp-tester-cli',
|
|
252
|
+
version: '1.0.0',
|
|
253
|
+
timeout: parseInt(String(opts['timeout'] || '30000')),
|
|
254
|
+
logLevel: opts['verbose'] ? 'debug' : 'info',
|
|
255
|
+
});
|
|
256
|
+
let promptArgs = {};
|
|
257
|
+
if (opts['args']) {
|
|
258
|
+
try {
|
|
259
|
+
promptArgs = JSON.parse(String(opts['args']));
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
console.error('ā Invalid JSON for --args');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
await client.start({ command, args: serverArgs });
|
|
268
|
+
if (!opts['json']) {
|
|
269
|
+
console.log(`š¬ Getting prompt: ${promptName}`);
|
|
270
|
+
}
|
|
271
|
+
const result = await client.getPrompt(promptName, promptArgs);
|
|
272
|
+
if (opts['json']) {
|
|
273
|
+
console.log(JSON.stringify(result, null, 2));
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
console.log('\nš Messages:');
|
|
277
|
+
result.messages.forEach((msg) => {
|
|
278
|
+
console.log(` [${msg.role}]`);
|
|
279
|
+
if ('text' in msg.content) {
|
|
280
|
+
console.log(` ${msg.content.text}`);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
console.error(`ā Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
await client.stop();
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
// Add custom help with examples using Commander's built-in help system
|
|
294
|
+
program.addHelpText('after', `
|
|
295
|
+
Examples:
|
|
296
|
+
mcp-tester test node ./server.js
|
|
297
|
+
mcp-tester list-tools node ./server.js --json
|
|
298
|
+
mcp-tester call-tool echo node ./server.js --params '{"message":"Hello"}'
|
|
299
|
+
mcp-tester read-resource text://example node ./server.js
|
|
300
|
+
mcp-tester get-prompt greet node ./server.js --args '{"name":"World"}'
|
|
301
|
+
mcp-tester lt node ./server.js # short alias for list-tools
|
|
302
|
+
mcp-tester ct echo node ./server.js --params '{}' # short alias for call-tool
|
|
303
|
+
`);
|
|
304
|
+
// Show help when invoked with no arguments
|
|
305
|
+
const args = process.argv.slice(2);
|
|
306
|
+
if (args.length === 0) {
|
|
307
|
+
program.outputHelp();
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
program.parse();
|
|
311
|
+
//# sourceMappingURL=index.js.map
|