@slbdn/mcp-tester 1.4.1 → 1.5.3
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 +96 -0
- package/README.md +49 -0
- package/dist/ai/provider.d.ts +145 -0
- package/dist/ai/provider.d.ts.map +1 -0
- package/dist/ai/provider.js +428 -0
- package/dist/ai/provider.js.map +1 -0
- package/dist/cli/index.js +260 -151
- package/dist/cli/index.js.map +1 -1
- package/dist/client/MCPClient.d.ts +16 -2
- package/dist/client/MCPClient.d.ts.map +1 -1
- package/dist/client/MCPClient.js +77 -20
- package/dist/client/MCPClient.js.map +1 -1
- package/dist/generate-cases.d.ts +113 -0
- package/dist/generate-cases.d.ts.map +1 -0
- package/dist/generate-cases.js +565 -0
- package/dist/generate-cases.js.map +1 -0
- package/dist/generate-tests.d.ts +106 -0
- package/dist/generate-tests.d.ts.map +1 -0
- package/dist/generate-tests.js +441 -0
- package/dist/generate-tests.js.map +1 -0
- package/dist/generate-types.d.ts +86 -0
- package/dist/generate-types.d.ts.map +1 -0
- package/dist/generate-types.js +484 -0
- package/dist/generate-types.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +31 -21
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js.map +1 -1
- package/dist/templates/full-stack/.github/workflows/test.yml +34 -0
- package/dist/templates/full-stack/README.md +73 -0
- package/dist/templates/full-stack/examples/mock-server.js +115 -0
- package/dist/templates/full-stack/jest.config.js +39 -0
- package/dist/templates/full-stack/package.json +31 -0
- package/dist/templates/full-stack/scripts/generate-tests.ts +32 -0
- package/dist/templates/full-stack/scripts/generate-types.ts +28 -0
- package/dist/templates/full-stack/src/server.ts +78 -0
- package/dist/templates/full-stack/tests/helpers.ts +29 -0
- package/dist/templates/full-stack/tests/parallel.test.ts +44 -0
- package/dist/templates/full-stack/tests/server.test.ts +36 -0
- package/dist/templates/full-stack/tests/tools.test.ts +61 -0
- package/dist/templates/full-stack/tsconfig.json +19 -0
- package/dist/templates/minimal-jest/README.md +42 -0
- package/dist/templates/minimal-jest/examples/mock-server.js +62 -0
- package/dist/templates/minimal-jest/jest.config.js +24 -0
- package/dist/templates/minimal-jest/package.json +24 -0
- package/dist/templates/minimal-jest/tests/server.test.ts +64 -0
- package/dist/templates/minimal-jest/tsconfig.json +19 -0
- package/dist/templates/standard-jest/.github/workflows/test.yml +32 -0
- package/dist/templates/standard-jest/README.md +44 -0
- package/dist/templates/standard-jest/examples/mock-server.js +122 -0
- package/dist/templates/standard-jest/jest.config.js +39 -0
- package/dist/templates/standard-jest/package.json +27 -0
- package/dist/templates/standard-jest/tests/helpers.ts +43 -0
- package/dist/templates/standard-jest/tests/prompts.test.ts +37 -0
- package/dist/templates/standard-jest/tests/resources.test.ts +37 -0
- package/dist/templates/standard-jest/tests/server.test.ts +30 -0
- package/dist/templates/standard-jest/tests/tools.test.ts +87 -0
- package/dist/templates/standard-jest/tsconfig.json +19 -0
- package/dist/utils/masking.d.ts.map +1 -1
- package/dist/utils/masking.js +0 -3
- package/dist/utils/masking.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +9 -0
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/version.d.ts +13 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +81 -0
- package/dist/utils/version.js.map +1 -0
- package/docs/advanced.md +424 -0
- package/docs/ai-generation.md +211 -0
- package/docs/api-reference.md +819 -0
- package/docs/cicd.md +289 -0
- package/docs/cli.md +225 -0
- package/docs/examples.md +300 -0
- package/docs/nodejs-compatibility.md +81 -0
- package/docs/releases.md +413 -0
- package/docs/starter-templates.md +110 -0
- package/docs/testing.md +443 -0
- package/docs/troubleshooting.md +139 -0
- package/jest.d.ts +43 -0
- package/package.json +11 -20
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test code generator — connects to an MCP server, inspects its capabilities,
|
|
3
|
+
* and generates a ready-to-run Jest/Vitest test file.
|
|
4
|
+
*
|
|
5
|
+
* @module generate-tests
|
|
6
|
+
*/
|
|
7
|
+
import { MCPClient } from './client/MCPClient.js';
|
|
8
|
+
import type { ServerConfig } from './client/MCPClient.js';
|
|
9
|
+
import type { Tool, Resource, Prompt } from '@modelcontextprotocol/sdk/types.js';
|
|
10
|
+
import { type GeneratedCase } from './generate-cases.js';
|
|
11
|
+
import { type AIProvider, type OpenAICompatOptions } from './ai/provider.js';
|
|
12
|
+
/**
|
|
13
|
+
* How test cases are derived.
|
|
14
|
+
*
|
|
15
|
+
* - `'static'` — the classic generator: one listing test + one probe call per
|
|
16
|
+
* tool. @defaultValue
|
|
17
|
+
* - `'edge'` — additionally derives boundary/invalid cases from each tool's
|
|
18
|
+
* JSON Schema (offline, deterministic).
|
|
19
|
+
* - `'ai'` — edge cases plus AI-suggested cases. Requires a provider (see
|
|
20
|
+
* {@link GenerateTestOptions.provider} or the `MCP_TESTER_AI_*` env vars);
|
|
21
|
+
* falls back to `'edge'` with a warning unless {@link GenerateTestOptions.requireAi}
|
|
22
|
+
* is set.
|
|
23
|
+
*/
|
|
24
|
+
export type GenerationMode = 'static' | 'edge' | 'ai';
|
|
25
|
+
/** AI configuration accepted by the generate functions. */
|
|
26
|
+
export interface AIOptions extends OpenAICompatOptions {
|
|
27
|
+
/** Fail instead of falling back to deterministic cases when AI is unavailable. @defaultValue false */
|
|
28
|
+
requireAi?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Options for test generation.
|
|
32
|
+
*/
|
|
33
|
+
export interface GenerateTestOptions {
|
|
34
|
+
/** Command to run the MCP server. */
|
|
35
|
+
command: string;
|
|
36
|
+
/** Arguments for the server command. */
|
|
37
|
+
args?: string[];
|
|
38
|
+
/** Test framework to target. @defaultValue 'jest' */
|
|
39
|
+
framework?: 'jest' | 'vitest';
|
|
40
|
+
/** Include resource tests. @defaultValue true */
|
|
41
|
+
includeResources?: boolean;
|
|
42
|
+
/** Include prompt tests. @defaultValue true */
|
|
43
|
+
includePrompts?: boolean;
|
|
44
|
+
/** Include tool call tests. @defaultValue true */
|
|
45
|
+
includeTools?: boolean;
|
|
46
|
+
/** Include matchers import. @defaultValue true */
|
|
47
|
+
includeMatchers?: boolean;
|
|
48
|
+
/** Timeout in ms for connecting to the server. @defaultValue 30000 */
|
|
49
|
+
timeout?: number;
|
|
50
|
+
/** Description for the test suite. */
|
|
51
|
+
description?: string;
|
|
52
|
+
/** How test cases are derived. @defaultValue 'static' */
|
|
53
|
+
mode?: GenerationMode;
|
|
54
|
+
/** Pre-built AI provider; overrides env-based discovery. */
|
|
55
|
+
provider?: AIProvider;
|
|
56
|
+
/** AI provider configuration (used when `provider` is not supplied). */
|
|
57
|
+
ai?: AIOptions;
|
|
58
|
+
/** Dry-run every derived case against the live server and annotate the
|
|
59
|
+
* generated tests with what actually happens. @defaultValue false */
|
|
60
|
+
verify?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Collapse control characters (including line terminators) so untrusted text
|
|
64
|
+
* can be embedded in a `//` line comment without terminating it. Model-provided
|
|
65
|
+
* rationales are the main caller — a raw newline would otherwise let the text
|
|
66
|
+
* escape the comment and become executable test code.
|
|
67
|
+
*/
|
|
68
|
+
export declare function escapeLineComment(s: string): string;
|
|
69
|
+
/** Escape a string for use inside a single- or double-quoted JavaScript literal. */
|
|
70
|
+
export declare function escapeJsString(s: string, quote?: "'" | '"'): string;
|
|
71
|
+
/**
|
|
72
|
+
* Options consumed by {@link buildTestFile} (server description is passed separately).
|
|
73
|
+
*/
|
|
74
|
+
type TestBuildOptions = Pick<GenerateTestOptions, 'framework' | 'description' | 'includeResources' | 'includePrompts' | 'includeTools' | 'includeMatchers'>;
|
|
75
|
+
/**
|
|
76
|
+
* Generate a complete test file.
|
|
77
|
+
*
|
|
78
|
+
* @internal Exported for testing; consumers should use {@link generateTests}
|
|
79
|
+
* or {@link generateTestsFromClient}.
|
|
80
|
+
*/
|
|
81
|
+
export declare function buildTestFile(tools: Tool[], resources: Resource[], prompts: Prompt[], options: TestBuildOptions, server: ServerConfig, cases?: GeneratedCase[]): string;
|
|
82
|
+
/**
|
|
83
|
+
* Connect to an MCP server, inspect capabilities, and generate a test file.
|
|
84
|
+
*
|
|
85
|
+
* @returns The generated test file content
|
|
86
|
+
*/
|
|
87
|
+
export declare function generateTests(options: GenerateTestOptions): Promise<string>;
|
|
88
|
+
/**
|
|
89
|
+
* Options for {@link generateTestsFromClient}.
|
|
90
|
+
*/
|
|
91
|
+
export type GenerateTestsFromClientOptions = Pick<GenerateTestOptions, 'framework' | 'description' | 'includeResources' | 'includePrompts' | 'includeTools' | 'includeMatchers' | 'mode' | 'provider' | 'ai' | 'verify' | 'timeout'>;
|
|
92
|
+
/**
|
|
93
|
+
* Generate a test file from an already-connected client.
|
|
94
|
+
*
|
|
95
|
+
* Unlike {@link generateTests}, this supports any transport (stdio, HTTP, SSE)
|
|
96
|
+
* because the caller owns the connection. The generated test file embeds the
|
|
97
|
+
* supplied `server` config so it can reconnect on its own.
|
|
98
|
+
*
|
|
99
|
+
* @param client - A connected MCPClient instance
|
|
100
|
+
* @param server - The config the generated test should use to connect
|
|
101
|
+
* @param options - Generation options
|
|
102
|
+
* @returns The generated test file content
|
|
103
|
+
*/
|
|
104
|
+
export declare function generateTestsFromClient(client: MCPClient, server: ServerConfig, options?: GenerateTestsFromClientOptions): Promise<string>;
|
|
105
|
+
export {};
|
|
106
|
+
//# sourceMappingURL=generate-tests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-tests.d.ts","sourceRoot":"","sources":["../src/generate-tests.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAC;AAEjF,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,mBAAmB,EAEzB,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC;AAEtD,2DAA2D;AAC3D,MAAM,WAAW,SAAU,SAAQ,mBAAmB;IACpD,sGAAsG;IACtG,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kDAAkD;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,wEAAwE;IACxE,EAAE,CAAC,EAAE,SAAS,CAAC;IACf;0EACsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAsFD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,GAAG,GAAG,GAAS,GAAG,MAAM,CAYxE;AA+HD;;GAEG;AACH,KAAK,gBAAgB,GAAG,IAAI,CAC1B,mBAAmB,EACjB,WAAW,GACX,aAAa,GACb,kBAAkB,GAClB,gBAAgB,GAChB,cAAc,GACd,iBAAiB,CACpB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,EAAE,EACb,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,YAAY,EACpB,KAAK,GAAE,aAAa,EAAO,GAC1B,MAAM,CAgGR;AAmGD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BjF;AAED;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC/C,mBAAmB,EACjB,WAAW,GACX,aAAa,GACb,kBAAkB,GAClB,gBAAgB,GAChB,cAAc,GACd,iBAAiB,GACjB,MAAM,GACN,UAAU,GACV,IAAI,GACJ,QAAQ,GACR,SAAS,CACZ,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test code generator — connects to an MCP server, inspects its capabilities,
|
|
3
|
+
* and generates a ready-to-run Jest/Vitest test file.
|
|
4
|
+
*
|
|
5
|
+
* @module generate-tests
|
|
6
|
+
*/
|
|
7
|
+
import { MCPClient } from './client/MCPClient.js';
|
|
8
|
+
import { getPackageVersion } from './utils/version.js';
|
|
9
|
+
import { generateToolArgs, suggestEdgeCasesForTools, mergeAndValidateCases, } from './generate-cases.js';
|
|
10
|
+
import { createProviderFromEnv, } from './ai/provider.js';
|
|
11
|
+
/**
|
|
12
|
+
* Build the `client.start({ ... })` config literal for the generated test.
|
|
13
|
+
* Uses JSON.stringify for values so quotes/backslashes are safely escaped.
|
|
14
|
+
*/
|
|
15
|
+
function formatServerStart(server) {
|
|
16
|
+
if (server.transport === 'http' || server.transport === 'sse') {
|
|
17
|
+
const parts = [`transport: '${server.transport}'`, `url: ${JSON.stringify(server.url)}`];
|
|
18
|
+
if (server.headers && Object.keys(server.headers).length > 0) {
|
|
19
|
+
parts.push(`headers: ${JSON.stringify(server.headers)}`);
|
|
20
|
+
}
|
|
21
|
+
return parts.join(',\n ');
|
|
22
|
+
}
|
|
23
|
+
const stdio = server;
|
|
24
|
+
const parts = [`command: ${JSON.stringify(stdio.command)}`];
|
|
25
|
+
if (stdio.args && stdio.args.length > 0) {
|
|
26
|
+
parts.push(`args: ${JSON.stringify(stdio.args)}`);
|
|
27
|
+
}
|
|
28
|
+
return parts.join(',\n ');
|
|
29
|
+
}
|
|
30
|
+
/** Human-readable server target for the generated file header. */
|
|
31
|
+
function formatServerLabel(server) {
|
|
32
|
+
if (server.transport === 'http' || server.transport === 'sse') {
|
|
33
|
+
return `${server.transport} ${server.url}`;
|
|
34
|
+
}
|
|
35
|
+
const stdio = server;
|
|
36
|
+
return `${stdio.command}${stdio.args && stdio.args.length > 0 ? ' ' + stdio.args.join(' ') : ''}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Render one derived test case as an `it(...)` block.
|
|
40
|
+
*/
|
|
41
|
+
function generateCaseTest(c) {
|
|
42
|
+
const lines = [];
|
|
43
|
+
const argsStr = JSON.stringify(c.args);
|
|
44
|
+
lines.push(` // ${c.source} — ${escapeLineComment(c.rationale)}`);
|
|
45
|
+
// --verify found the server behaving differently than predicted: keep the
|
|
46
|
+
// case for visibility but skip it, and say what actually happened.
|
|
47
|
+
if (c.observed && c.expectation !== 'observe' && c.observed !== c.expectation) {
|
|
48
|
+
lines.push(` // verify: expected ${c.expectation} but server returned ${c.observed} — adjust manually`);
|
|
49
|
+
lines.push(` it.skip('${escapeJsString(c.title)}', async () => {`);
|
|
50
|
+
lines.push(' const result = await client.callTool({');
|
|
51
|
+
lines.push(` name: '${toolNameLiteral(c.tool)}',`);
|
|
52
|
+
lines.push(` arguments: ${argsStr},`);
|
|
53
|
+
lines.push(' }).catch((error: unknown) => error);');
|
|
54
|
+
lines.push(' expect(result).toBeDefined();');
|
|
55
|
+
lines.push(' });');
|
|
56
|
+
return lines;
|
|
57
|
+
}
|
|
58
|
+
if (c.expectation === 'observe') {
|
|
59
|
+
lines.push(` it('${escapeJsString(c.title)}', async () => {`);
|
|
60
|
+
lines.push(' const result = await client.callTool({');
|
|
61
|
+
lines.push(` name: '${toolNameLiteral(c.tool)}',`);
|
|
62
|
+
lines.push(` arguments: ${argsStr},`);
|
|
63
|
+
lines.push(' }).catch((error: unknown) => error);');
|
|
64
|
+
lines.push(' // Observation only — record what the server actually does.');
|
|
65
|
+
lines.push(' expect(result).toBeDefined();');
|
|
66
|
+
lines.push(` console.log('[${toolNameLiteral(c.tool)}]', JSON.stringify(result));`);
|
|
67
|
+
lines.push(' });');
|
|
68
|
+
}
|
|
69
|
+
else if (c.expectation === 'error') {
|
|
70
|
+
lines.push(` it('${escapeJsString(c.title)}', async () => {`);
|
|
71
|
+
lines.push(' const result = await client.callTool({');
|
|
72
|
+
lines.push(` name: '${toolNameLiteral(c.tool)}',`);
|
|
73
|
+
lines.push(` arguments: ${argsStr},`);
|
|
74
|
+
lines.push(' }).catch((error: unknown) => error);');
|
|
75
|
+
lines.push(' expect(isToolError(result)).toBe(true);');
|
|
76
|
+
lines.push(' });');
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
lines.push(` it('${escapeJsString(c.title)}', async () => {`);
|
|
80
|
+
lines.push(' const result = await client.callTool({');
|
|
81
|
+
lines.push(` name: '${toolNameLiteral(c.tool)}',`);
|
|
82
|
+
lines.push(` arguments: ${argsStr},`);
|
|
83
|
+
lines.push(' }).catch((error: unknown) => error);');
|
|
84
|
+
lines.push(' expect(isToolError(result)).toBe(false);');
|
|
85
|
+
lines.push(' });');
|
|
86
|
+
}
|
|
87
|
+
return lines;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Collapse control characters (including line terminators) so untrusted text
|
|
91
|
+
* can be embedded in a `//` line comment without terminating it. Model-provided
|
|
92
|
+
* rationales are the main caller — a raw newline would otherwise let the text
|
|
93
|
+
* escape the comment and become executable test code.
|
|
94
|
+
*/
|
|
95
|
+
export function escapeLineComment(s) {
|
|
96
|
+
// eslint-disable-next-line no-control-regex -- intentional: collapse control chars and line terminators
|
|
97
|
+
return s.replace(/[\u0000-\u001f\u2028\u2029]+/g, ' ');
|
|
98
|
+
}
|
|
99
|
+
/** Escape a string for use inside a single- or double-quoted JavaScript literal. */
|
|
100
|
+
export function escapeJsString(s, quote = "'") {
|
|
101
|
+
// Escape backslashes first, then the chosen quote, then newlines/control chars.
|
|
102
|
+
return (s
|
|
103
|
+
.replace(/\\/g, '\\\\')
|
|
104
|
+
.replace(new RegExp(quote, 'g'), `\\${quote}`)
|
|
105
|
+
.replace(/\n/g, '\\n')
|
|
106
|
+
.replace(/\r/g, '\\r')
|
|
107
|
+
.replace(/\t/g, '\\t')
|
|
108
|
+
// eslint-disable-next-line no-control-regex -- intentional: we want to escape ASCII control chars in user-provided strings
|
|
109
|
+
.replace(/[\u0000-\u001f]/g, (ch) => `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`));
|
|
110
|
+
}
|
|
111
|
+
/** Emit a safe single-quoted string literal for a tool name. */
|
|
112
|
+
function toolNameLiteral(name) {
|
|
113
|
+
return escapeJsString(name, "'");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Generate tool test cases.
|
|
117
|
+
*/
|
|
118
|
+
function generateToolTests(tools, cases = []) {
|
|
119
|
+
if (tools.length === 0)
|
|
120
|
+
return '';
|
|
121
|
+
const lines = [" describe('Tools', () => {"];
|
|
122
|
+
if (cases.length > 0) {
|
|
123
|
+
lines.push(' const isToolError = (res: unknown): boolean =>');
|
|
124
|
+
lines.push(' res instanceof Error ||');
|
|
125
|
+
lines.push(' (typeof res === "object" && res !== null && (res as { isError?: unknown }).isError === true);');
|
|
126
|
+
}
|
|
127
|
+
for (const tool of tools) {
|
|
128
|
+
const sampleArgs = generateToolArgs(tool);
|
|
129
|
+
const argsStr = Object.keys(sampleArgs).length > 0 ? JSON.stringify(sampleArgs) : '{}';
|
|
130
|
+
lines.push('');
|
|
131
|
+
lines.push(` it('should have tool "${escapeJsString(tool.name, "'")}"', async () => {`);
|
|
132
|
+
lines.push(' const tools = await client.listTools();');
|
|
133
|
+
lines.push(` expect(tools).toHaveTool('${toolNameLiteral(tool.name)}');`);
|
|
134
|
+
lines.push(' });');
|
|
135
|
+
lines.push('');
|
|
136
|
+
// Call test. Generated arguments are guesses, and some tools intentionally
|
|
137
|
+
// return/throw errors — so accept a successful result or a tool-level error.
|
|
138
|
+
lines.push(` it('should call "${escapeJsString(tool.name, "'")}"', async () => {`);
|
|
139
|
+
lines.push(' const result = await client.callTool({');
|
|
140
|
+
lines.push(` name: '${toolNameLiteral(tool.name)}',`);
|
|
141
|
+
lines.push(` arguments: ${argsStr},`);
|
|
142
|
+
lines.push(' }).catch((error: unknown) => error);');
|
|
143
|
+
lines.push(' expect(result).toBeDefined();');
|
|
144
|
+
lines.push(' });');
|
|
145
|
+
// Derived edge/AI cases for this tool.
|
|
146
|
+
for (const c of cases.filter((c) => c.tool === tool.name)) {
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push(...generateCaseTest(c));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
lines.push(' });');
|
|
152
|
+
return lines.join('\n');
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generate resource test cases.
|
|
156
|
+
*/
|
|
157
|
+
function generateResourceTests(resources) {
|
|
158
|
+
if (resources.length === 0)
|
|
159
|
+
return '';
|
|
160
|
+
const lines = [" describe('Resources', () => {"];
|
|
161
|
+
for (const resource of resources) {
|
|
162
|
+
lines.push('');
|
|
163
|
+
lines.push(` it('should have resource "${escapeJsString(resource.uri, "'")}"', async () => {`);
|
|
164
|
+
lines.push(' const resources = await client.listResources();');
|
|
165
|
+
lines.push(` expect(resources).toHaveResource('${escapeJsString(resource.uri, "'")}');`);
|
|
166
|
+
lines.push(' });');
|
|
167
|
+
lines.push('');
|
|
168
|
+
lines.push(` it('should read resource "${escapeJsString(resource.uri, "'")}"', async () => {`);
|
|
169
|
+
lines.push(` const result = await client.readResource('${escapeJsString(resource.uri, "'")}');`);
|
|
170
|
+
lines.push(' expect(result.contents).toBeDefined();');
|
|
171
|
+
lines.push(' expect(result.contents.length).toBeGreaterThan(0);');
|
|
172
|
+
lines.push(' });');
|
|
173
|
+
}
|
|
174
|
+
lines.push(' });');
|
|
175
|
+
return lines.join('\n');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Generate prompt test cases.
|
|
179
|
+
*/
|
|
180
|
+
function generatePromptTests(prompts) {
|
|
181
|
+
if (prompts.length === 0)
|
|
182
|
+
return '';
|
|
183
|
+
const lines = [" describe('Prompts', () => {"];
|
|
184
|
+
for (const prompt of prompts) {
|
|
185
|
+
// Generate sample args from prompt arguments
|
|
186
|
+
const promptArgs = {};
|
|
187
|
+
if (prompt.arguments && Array.isArray(prompt.arguments)) {
|
|
188
|
+
for (const arg of prompt.arguments) {
|
|
189
|
+
if (arg.required) {
|
|
190
|
+
promptArgs[arg.name] = `example-${arg.name}`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const argsStr = Object.keys(promptArgs).length > 0 ? `, ${JSON.stringify(promptArgs)}` : '';
|
|
195
|
+
lines.push('');
|
|
196
|
+
lines.push(` it('should have prompt "${escapeJsString(prompt.name, "'")}"', async () => {`);
|
|
197
|
+
lines.push(' const prompts = await client.listPrompts();');
|
|
198
|
+
lines.push(` expect(prompts).toHavePrompt('${escapeJsString(prompt.name, "'")}');`);
|
|
199
|
+
lines.push(' });');
|
|
200
|
+
lines.push('');
|
|
201
|
+
lines.push(` it('should get prompt "${escapeJsString(prompt.name, "'")}"', async () => {`);
|
|
202
|
+
lines.push(` const result = await client.getPrompt('${escapeJsString(prompt.name, "'")}'${argsStr});`);
|
|
203
|
+
lines.push(' expect(result.messages).toBeDefined();');
|
|
204
|
+
lines.push(' expect(result.messages.length).toBeGreaterThan(0);');
|
|
205
|
+
lines.push(' });');
|
|
206
|
+
}
|
|
207
|
+
lines.push(' });');
|
|
208
|
+
return lines.join('\n');
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Generate a complete test file.
|
|
212
|
+
*
|
|
213
|
+
* @internal Exported for testing; consumers should use {@link generateTests}
|
|
214
|
+
* or {@link generateTestsFromClient}.
|
|
215
|
+
*/
|
|
216
|
+
export function buildTestFile(tools, resources, prompts, options, server, cases = []) {
|
|
217
|
+
const framework = options.framework || 'jest';
|
|
218
|
+
const description = options.description || 'MCP Server';
|
|
219
|
+
const startCmd = formatServerStart(server);
|
|
220
|
+
const serverLabel = formatServerLabel(server);
|
|
221
|
+
const includeMatchers = options.includeMatchers !== false;
|
|
222
|
+
// Imports
|
|
223
|
+
const importLines = [];
|
|
224
|
+
// Triple-slash type directive must appear at the very top of the file.
|
|
225
|
+
let vitestReference = '';
|
|
226
|
+
if (framework === 'vitest') {
|
|
227
|
+
importLines.push("import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';");
|
|
228
|
+
const extraImports = includeMatchers ? ', setupVitestMatchers' : '';
|
|
229
|
+
importLines.push(`import { MCPClient${extraImports} } from '@slbdn/mcp-tester';`);
|
|
230
|
+
if (includeMatchers) {
|
|
231
|
+
vitestReference = '/// <reference types="@slbdn/mcp-tester/vitest" />\n\n';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
importLines.push("import { describe, it, expect, beforeAll, beforeEach, afterEach } from '@jest/globals';");
|
|
236
|
+
const extraImports = includeMatchers ? ', setupJestMatchers' : '';
|
|
237
|
+
importLines.push(`import { MCPClient${extraImports} } from '@slbdn/mcp-tester';`);
|
|
238
|
+
}
|
|
239
|
+
// Header comment
|
|
240
|
+
const header = [
|
|
241
|
+
'/**',
|
|
242
|
+
` * Generated test file for: ${description.replace(/\*\//g, '*\\/')}`,
|
|
243
|
+
' *',
|
|
244
|
+
` * Framework: ${framework}`,
|
|
245
|
+
` * Server: ${serverLabel.replace(/\*\//g, '*\\/')}`,
|
|
246
|
+
` * Tools: ${tools.length} | Resources: ${resources.length} | Prompts: ${prompts.length}`,
|
|
247
|
+
cases.length > 0 ? ` * Derived cases: ${cases.length} (edge/AI)` : null,
|
|
248
|
+
' *',
|
|
249
|
+
` * Generated by @slbdn/mcp-tester v${getPackageVersion()}`,
|
|
250
|
+
' * Run: npx jest --testTimeout=30000 (or npx vitest run)',
|
|
251
|
+
' */',
|
|
252
|
+
]
|
|
253
|
+
.filter((line) => line !== null)
|
|
254
|
+
.join('\n');
|
|
255
|
+
// Test sections
|
|
256
|
+
const toolTests = options.includeTools !== false ? generateToolTests(tools, cases) : '';
|
|
257
|
+
const resourceTests = options.includeResources !== false ? generateResourceTests(resources) : '';
|
|
258
|
+
const promptTests = options.includePrompts !== false ? generatePromptTests(prompts) : '';
|
|
259
|
+
// Combine
|
|
260
|
+
const sections = [toolTests, resourceTests, promptTests].filter(Boolean);
|
|
261
|
+
const beforeAllLine = includeMatchers
|
|
262
|
+
? framework === 'vitest'
|
|
263
|
+
? 'beforeAll(() => setupVitestMatchers());'
|
|
264
|
+
: 'beforeAll(() => setupJestMatchers());'
|
|
265
|
+
: '';
|
|
266
|
+
return `${vitestReference}${header}
|
|
267
|
+
|
|
268
|
+
${importLines.join('\n')}
|
|
269
|
+
|
|
270
|
+
${beforeAllLine}
|
|
271
|
+
describe('${escapeJsString(description, "'")}', () => {
|
|
272
|
+
let client: MCPClient;
|
|
273
|
+
|
|
274
|
+
beforeEach(async () => {
|
|
275
|
+
client = new MCPClient({
|
|
276
|
+
name: 'test-client',
|
|
277
|
+
version: '1.0.0',
|
|
278
|
+
});
|
|
279
|
+
await client.start({
|
|
280
|
+
${startCmd}
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
afterEach(async () => {
|
|
285
|
+
if (client.isConnected()) {
|
|
286
|
+
await client.stop();
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should connect to the server', () => {
|
|
291
|
+
expect(client.isConnected()).toBe(true);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should list tools', async () => {
|
|
295
|
+
const tools = await client.listTools();
|
|
296
|
+
expect(Array.isArray(tools)).toBe(true);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
${sections.join('\n\n')}
|
|
300
|
+
});
|
|
301
|
+
`;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Derive test cases for the requested {@link GenerationMode}.
|
|
305
|
+
*
|
|
306
|
+
* - `static` → no extra cases
|
|
307
|
+
* - `edge` → deterministic schema rules
|
|
308
|
+
* - `ai` → rules + AI-suggested cases (validated against the real schemas)
|
|
309
|
+
*
|
|
310
|
+
* @returns The cases, a diagnostics string (or null), and whether AI ran.
|
|
311
|
+
*/
|
|
312
|
+
async function buildCases(tools, options, serverLabel) {
|
|
313
|
+
const mode = options.mode ?? 'static';
|
|
314
|
+
if (mode === 'static' || options.includeTools === false || tools.length === 0) {
|
|
315
|
+
return { cases: [], note: null, usedAI: false };
|
|
316
|
+
}
|
|
317
|
+
const ruleCases = suggestEdgeCasesForTools(tools);
|
|
318
|
+
if (mode === 'edge') {
|
|
319
|
+
return { cases: ruleCases, note: null, usedAI: false };
|
|
320
|
+
}
|
|
321
|
+
// mode === 'ai'
|
|
322
|
+
const provider = options.provider ?? createProviderFromEnv();
|
|
323
|
+
if (!provider) {
|
|
324
|
+
if (options.ai?.requireAi) {
|
|
325
|
+
throw new Error('AI generation requested with requireAi, but no provider is available. ' +
|
|
326
|
+
'Set MCP_TESTER_AI_API_KEY (and optionally MCP_TESTER_AI_BASE_URL / MCP_TESTER_AI_MODEL), ' +
|
|
327
|
+
'or pass a provider.');
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
cases: ruleCases,
|
|
331
|
+
note: 'No AI provider configured (set MCP_TESTER_AI_API_KEY); ' +
|
|
332
|
+
'fell back to deterministic edge cases.',
|
|
333
|
+
usedAI: false,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const analysis = {
|
|
337
|
+
serverLabel,
|
|
338
|
+
tools: tools.map((t) => ({
|
|
339
|
+
name: t.name,
|
|
340
|
+
description: t.description,
|
|
341
|
+
inputSchema: t.inputSchema,
|
|
342
|
+
})),
|
|
343
|
+
};
|
|
344
|
+
const aiCases = await provider.suggestCases(analysis);
|
|
345
|
+
const schemas = new Map(tools.map((t) => [t.name, t]));
|
|
346
|
+
const { cases, rejected } = mergeAndValidateCases([ruleCases, aiCases], schemas);
|
|
347
|
+
// Only AI cases can be rejected (rule cases are exempt from schema
|
|
348
|
+
// validation), but count by source so the diagnostic stays correct if that
|
|
349
|
+
// ever changes.
|
|
350
|
+
const dropped = rejected.filter((r) => r.c.source.startsWith('ai:')).length;
|
|
351
|
+
const note = dropped > 0
|
|
352
|
+
? `AI suggested cases: ${aiCases.length}, kept after schema validation: ${aiCases.length - dropped} (dropped ${dropped})`
|
|
353
|
+
: null;
|
|
354
|
+
return { cases, note, usedAI: true };
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Dry-run every derived case against the live server and record what actually
|
|
358
|
+
* happens in `case.observed`. The renderer turns mismatches into `it.skip`
|
|
359
|
+
* blocks with an explanatory comment.
|
|
360
|
+
*/
|
|
361
|
+
async function verifyCases(client, cases, timeout) {
|
|
362
|
+
for (const c of cases) {
|
|
363
|
+
if (c.expectation === 'observe')
|
|
364
|
+
continue;
|
|
365
|
+
try {
|
|
366
|
+
const result = await client.callTool({
|
|
367
|
+
name: c.tool,
|
|
368
|
+
arguments: c.args,
|
|
369
|
+
timeout: Math.min(timeout, 15_000),
|
|
370
|
+
});
|
|
371
|
+
const isError = result.isError === true;
|
|
372
|
+
c.observed = isError ? 'error' : 'success';
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
c.observed = 'error';
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Connect to an MCP server, inspect capabilities, and generate a test file.
|
|
381
|
+
*
|
|
382
|
+
* @returns The generated test file content
|
|
383
|
+
*/
|
|
384
|
+
export async function generateTests(options) {
|
|
385
|
+
const client = new MCPClient({
|
|
386
|
+
name: 'mcp-tester-generate',
|
|
387
|
+
version: '1.0.0',
|
|
388
|
+
timeout: options.timeout || 30000,
|
|
389
|
+
logLevel: 'none',
|
|
390
|
+
});
|
|
391
|
+
try {
|
|
392
|
+
await client.start({
|
|
393
|
+
command: options.command,
|
|
394
|
+
args: options.args,
|
|
395
|
+
});
|
|
396
|
+
const [tools, resources, prompts] = await Promise.all([
|
|
397
|
+
options.includeTools === false ? Promise.resolve([]) : client.listTools(),
|
|
398
|
+
options.includeResources === false ? Promise.resolve([]) : client.listResources(),
|
|
399
|
+
options.includePrompts === false ? Promise.resolve([]) : client.listPrompts(),
|
|
400
|
+
]);
|
|
401
|
+
const server = { command: options.command, args: options.args };
|
|
402
|
+
const serverLabel = formatServerLabel(server);
|
|
403
|
+
const { cases, note } = await buildCases(tools, options, serverLabel);
|
|
404
|
+
if (note)
|
|
405
|
+
console.warn(`mcp-tester: ${note}`);
|
|
406
|
+
if (options.verify && cases.length > 0) {
|
|
407
|
+
await verifyCases(client, cases, options.timeout || 30000);
|
|
408
|
+
}
|
|
409
|
+
return buildTestFile(tools, resources, prompts, options, server, cases);
|
|
410
|
+
}
|
|
411
|
+
finally {
|
|
412
|
+
await client.stop();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Generate a test file from an already-connected client.
|
|
417
|
+
*
|
|
418
|
+
* Unlike {@link generateTests}, this supports any transport (stdio, HTTP, SSE)
|
|
419
|
+
* because the caller owns the connection. The generated test file embeds the
|
|
420
|
+
* supplied `server` config so it can reconnect on its own.
|
|
421
|
+
*
|
|
422
|
+
* @param client - A connected MCPClient instance
|
|
423
|
+
* @param server - The config the generated test should use to connect
|
|
424
|
+
* @param options - Generation options
|
|
425
|
+
* @returns The generated test file content
|
|
426
|
+
*/
|
|
427
|
+
export async function generateTestsFromClient(client, server, options = {}) {
|
|
428
|
+
const [tools, resources, prompts] = await Promise.all([
|
|
429
|
+
options.includeTools === false ? Promise.resolve([]) : client.listTools(),
|
|
430
|
+
options.includeResources === false ? Promise.resolve([]) : client.listResources(),
|
|
431
|
+
options.includePrompts === false ? Promise.resolve([]) : client.listPrompts(),
|
|
432
|
+
]);
|
|
433
|
+
const { cases, note } = await buildCases(tools, options, formatServerLabel(server));
|
|
434
|
+
if (note)
|
|
435
|
+
console.warn(`mcp-tester: ${note}`);
|
|
436
|
+
if (options.verify && cases.length > 0) {
|
|
437
|
+
await verifyCases(client, cases, options.timeout || 30000);
|
|
438
|
+
}
|
|
439
|
+
return buildTestFile(tools, resources, prompts, options, server, cases);
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=generate-tests.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-tests.js","sourceRoot":"","sources":["../src/generate-tests.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAEL,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIL,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAuD1B;;;GAGG;AACH,SAAS,iBAAiB,CAAC,MAAoB;IAC7C,IAAI,MAAM,CAAC,SAAS,KAAK,MAAM,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,CAAC,eAAe,MAAM,CAAC,SAAS,GAAG,EAAE,QAAQ,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzF,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7D,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,KAAK,GAAG,MAA2B,CAAC;IAC1C,MAAM,KAAK,GAAG,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACjC,CAAC;AAED,kEAAkE;AAClE,SAAS,iBAAiB,CAAC,MAAoB;IAC7C,IAAI,MAAM,CAAC,SAAS,KAAK,MAAM,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QAC9D,OAAO,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;IAC7C,CAAC;IACD,MAAM,KAAK,GAAG,MAA2B,CAAC;IAC1C,OAAO,GAAG,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACpG,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,CAAgB;IACxC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEvC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,MAAM,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACrE,0EAA0E;IAC1E,mEAAmE;IACnE,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9E,KAAK,CAAC,IAAI,CACR,2BAA2B,CAAC,CAAC,WAAW,wBAAwB,CAAC,CAAC,QAAQ,oBAAoB,CAC/F,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,gBAAgB,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtE,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,WAAW,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACjE,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,uBAAuB,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC;SAAM,IAAI,CAAC,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,WAAW,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACjE,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,WAAW,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACjE,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,KAAK,CAAC,IAAI,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAS;IACzC,wGAAwG;IACxG,OAAO,CAAC,CAAC,OAAO,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;AACzD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,CAAS,EAAE,QAAmB,GAAG;IAC9D,gFAAgF;IAChF,OAAO,CACL,CAAC;SACE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;SAC7C,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;QACtB,2HAA2H;SAC1H,OAAO,CAAC,kBAAkB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAC/F,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,KAAa,EAAE,QAAyB,EAAE;IACnE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,KAAK,GAAa,CAAC,6BAA6B,CAAC,CAAC;IAExD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;QACjE,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC5C,KAAK,CAAC,IAAI,CACR,qGAAqG,CACtG,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAEvF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,6BAA6B,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC3F,KAAK,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CAAC,mCAAmC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/E,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,2EAA2E;QAC3E,6EAA6E;QAC7E,KAAK,CAAC,IAAI,CAAC,wBAAwB,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACtF,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,kBAAkB,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,sBAAsB,OAAO,GAAG,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEtB,uCAAuC;QACvC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,SAAqB;IAClD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,MAAM,KAAK,GAAa,CAAC,iCAAiC,CAAC,CAAC;IAE5D,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CACR,iCAAiC,cAAc,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,mBAAmB,CACtF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;QACpE,KAAK,CAAC,IAAI,CAAC,2CAA2C,cAAc,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9F,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CACR,iCAAiC,cAAc,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,mBAAmB,CACtF,CAAC;QACF,KAAK,CAAC,IAAI,CACR,mDAAmD,cAAc,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAC1F,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,OAAiB;IAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAa,CAAC,+BAA+B,CAAC,CAAC;IAE1D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,6CAA6C;QAC7C,MAAM,UAAU,GAA2B,EAAE,CAAC;QAC9C,IAAI,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YACxD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACnC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;oBACjB,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,WAAW,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC/C,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAE5F,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,+BAA+B,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC/F,KAAK,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QAChE,KAAK,CAAC,IAAI,CAAC,uCAAuC,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,8BAA8B,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC9F,KAAK,CAAC,IAAI,CACR,gDAAgD,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,OAAO,IAAI,CAChG,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAeD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAa,EACb,SAAqB,EACrB,OAAiB,EACjB,OAAyB,EACzB,MAAoB,EACpB,QAAyB,EAAE;IAE3B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,YAAY,CAAC;IACxD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,KAAK,KAAK,CAAC;IAE1D,UAAU;IACV,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,uEAAuE;IACvE,IAAI,eAAe,GAAG,EAAE,CAAC;IAEzB,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,WAAW,CAAC,IAAI,CACd,kFAAkF,CACnF,CAAC;QACF,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,WAAW,CAAC,IAAI,CAAC,qBAAqB,YAAY,8BAA8B,CAAC,CAAC;QAClF,IAAI,eAAe,EAAE,CAAC;YACpB,eAAe,GAAG,wDAAwD,CAAC;QAC7E,CAAC;IACH,CAAC;SAAM,CAAC;QACN,WAAW,CAAC,IAAI,CACd,yFAAyF,CAC1F,CAAC;QACF,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,WAAW,CAAC,IAAI,CAAC,qBAAqB,YAAY,8BAA8B,CAAC,CAAC;IACpF,CAAC;IAED,iBAAiB;IACjB,MAAM,MAAM,GAAG;QACb,KAAK;QACL,+BAA+B,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE;QACrE,IAAI;QACJ,iBAAiB,SAAS,EAAE;QAC5B,cAAc,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE;QACpD,aAAa,KAAK,CAAC,MAAM,iBAAiB,SAAS,CAAC,MAAM,eAAe,OAAO,CAAC,MAAM,EAAE;QACzF,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,KAAK,CAAC,MAAM,YAAY,CAAC,CAAC,CAAC,IAAI;QACvE,IAAI;QACJ,sCAAsC,iBAAiB,EAAE,EAAE;QAC3D,0DAA0D;QAC1D,KAAK;KACN;SACE,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC;SAC/C,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,gBAAgB;IAChB,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACxF,MAAM,aAAa,GAAG,OAAO,CAAC,gBAAgB,KAAK,KAAK,CAAC,CAAC,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjG,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEzF,UAAU;IACV,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEzE,MAAM,aAAa,GAAG,eAAe;QACnC,CAAC,CAAC,SAAS,KAAK,QAAQ;YACtB,CAAC,CAAC,yCAAyC;YAC3C,CAAC,CAAC,uCAAuC;QAC3C,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO,GAAG,eAAe,GAAG,MAAM;;EAElC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;;EAEtB,aAAa;YACH,cAAc,CAAC,WAAW,EAAE,GAAG,CAAC;;;;;;;;;QASpC,QAAQ;;;;;;;;;;;;;;;;;;;EAmBd,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;;CAEtB,CAAC;AACF,CAAC;AAKD;;;;;;;;GAQG;AACH,KAAK,UAAU,UAAU,CACvB,KAAa,EACb,OAAyB,EACzB,WAAoB;IAEpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC;IACtC,IAAI,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,YAAY,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9E,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAClD,CAAC;IAED,MAAM,SAAS,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACzD,CAAC;IAED,gBAAgB;IAChB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,qBAAqB,EAAE,CAAC;IAC7D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,IAAI,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,wEAAwE;gBACtE,2FAA2F;gBAC3F,qBAAqB,CACxB,CAAC;QACJ,CAAC;QACD,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,IAAI,EACF,yDAAyD;gBACzD,wCAAwC;YAC1C,MAAM,EAAE,KAAK;SACd,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAmB;QAC/B,WAAW;QACX,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC,CAAC;KACJ,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,qBAAqB,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAEjF,mEAAmE;IACnE,2EAA2E;IAC3E,gBAAgB;IAChB,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5E,MAAM,IAAI,GACR,OAAO,GAAG,CAAC;QACT,CAAC,CAAC,uBAAuB,OAAO,CAAC,MAAM,mCAAmC,OAAO,CAAC,MAAM,GAAG,OAAO,aAAa,OAAO,GAAG;QACzH,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,WAAW,CACxB,MAAiB,EACjB,KAAsB,EACtB,OAAe;IAEf,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,WAAW,KAAK,SAAS;YAAE,SAAS;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;gBACnC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,IAAI;gBACjB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC;aACnC,CAAC,CAAC;YACH,MAAM,OAAO,GAAI,MAAgC,CAAC,OAAO,KAAK,IAAI,CAAC;YACnE,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAC;QACvB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA4B;IAC9D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK;QACjC,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC;YACjB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACpD,OAAO,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE;YACzE,OAAO,CAAC,gBAAgB,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,EAAE;YACjF,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE;SAC9E,CAAC,CAAC;QAEH,MAAM,MAAM,GAAsB,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;QACnF,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;QACtE,IAAI,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvC,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1E,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAoBD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAiB,EACjB,MAAoB,EACpB,UAA0C,EAAE;IAE5C,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACpD,OAAO,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE;QACzE,OAAO,CAAC,gBAAgB,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,EAAE;QACjF,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE;KAC9E,CAAC,CAAC;IAEH,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IACpF,IAAI,IAAI;QAAE,OAAO,CAAC,IAAI,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;IAC9C,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript type generator — connects to an MCP server, reads tool schemas,
|
|
3
|
+
* and generates a `.d.ts` TypeScript declaration file with typed interfaces.
|
|
4
|
+
*
|
|
5
|
+
* @module generate-types
|
|
6
|
+
*/
|
|
7
|
+
import { MCPClient } from './client/MCPClient.js';
|
|
8
|
+
/**
|
|
9
|
+
* Options for type generation.
|
|
10
|
+
*/
|
|
11
|
+
export interface GenerateTypesOptions {
|
|
12
|
+
/** Command to run the MCP server. */
|
|
13
|
+
command: string;
|
|
14
|
+
/** Arguments for the server command. */
|
|
15
|
+
args?: string[];
|
|
16
|
+
/** Timeout in ms for connecting to the server. @defaultValue 30000 */
|
|
17
|
+
timeout?: number;
|
|
18
|
+
/** Include resource URI types. @defaultValue true */
|
|
19
|
+
includeResources?: boolean;
|
|
20
|
+
/** Include prompt argument types. @defaultValue true */
|
|
21
|
+
includePrompts?: boolean;
|
|
22
|
+
/** Module name for the import type. @defaultValue '@slbdn/mcp-tester' */
|
|
23
|
+
moduleName?: string;
|
|
24
|
+
}
|
|
25
|
+
type JsonSchema = Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Convert a JSON Schema type to a TypeScript type string.
|
|
28
|
+
* Handles primitives, arrays, objects, enums, nullable, and compositions.
|
|
29
|
+
* Exported for unit testing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function schemaToType(schema: JsonSchema, indent?: number): string;
|
|
32
|
+
/**
|
|
33
|
+
* Emit TypeScript aliases for every `$ref`-referenced schema, resolved from the
|
|
34
|
+
* schema's `$defs` / `definitions`. Unresolved refs fall back to `unknown` so the
|
|
35
|
+
* generated file always type-checks instead of referencing undefined types.
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateRefTypes(schema: JsonSchema): string;
|
|
38
|
+
/** Escape a property name for use in TypeScript (quote if needed). Exported for testing. */
|
|
39
|
+
export declare function escapePropertyName(name: string): string;
|
|
40
|
+
/** Sanitize a tool/prompt name into a valid TypeScript identifier. Exported for testing. */
|
|
41
|
+
export declare function toTypeName(name: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Options for {@link generateTypesFromClient}.
|
|
44
|
+
*/
|
|
45
|
+
export interface GenerateTypesFromClientOptions {
|
|
46
|
+
/** Module name for the import hint. @defaultValue '@slbdn/mcp-tester' */
|
|
47
|
+
moduleName?: string;
|
|
48
|
+
/** Include resource URI types. @defaultValue true */
|
|
49
|
+
includeResources?: boolean;
|
|
50
|
+
/** Include prompt argument types. @defaultValue true */
|
|
51
|
+
includePrompts?: boolean;
|
|
52
|
+
/** Human-readable label for the server, used only in the generated header. */
|
|
53
|
+
serverLabel?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Generate TypeScript type declarations from an already-connected client.
|
|
57
|
+
*
|
|
58
|
+
* Unlike {@link generateTypes}, this supports any transport (stdio, HTTP, SSE)
|
|
59
|
+
* because the caller owns the connection.
|
|
60
|
+
*
|
|
61
|
+
* @param client - A connected MCPClient instance
|
|
62
|
+
* @param options - Generation options
|
|
63
|
+
* @returns The generated `.d.ts` file content
|
|
64
|
+
*/
|
|
65
|
+
export declare function generateTypesFromClient(client: MCPClient, options?: GenerateTypesFromClientOptions): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Connect to an MCP server, inspect tool schemas, and generate TypeScript type declarations.
|
|
68
|
+
*
|
|
69
|
+
* @returns The generated `.d.ts` file content
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* import { generateTypes } from '@slbdn/mcp-tester';
|
|
74
|
+
* import { writeFileSync } from 'fs';
|
|
75
|
+
*
|
|
76
|
+
* const types = await generateTypes({
|
|
77
|
+
* command: 'node',
|
|
78
|
+
* args: ['./my-server.js'],
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* writeFileSync('my-server-types.d.ts', types);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function generateTypes(options: GenerateTypesOptions): Promise<string>;
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=generate-types.d.ts.map
|