@frontmcp/testing 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +1358 -0
- package/jest-preset.js +61 -0
- package/package.json +94 -0
- package/src/assertions/index.d.ts +5 -0
- package/src/assertions/index.js +18 -0
- package/src/assertions/index.js.map +1 -0
- package/src/assertions/mcp-assertions.d.ts +81 -0
- package/src/assertions/mcp-assertions.js +220 -0
- package/src/assertions/mcp-assertions.js.map +1 -0
- package/src/auth/auth-headers.d.ts +29 -0
- package/src/auth/auth-headers.js +62 -0
- package/src/auth/auth-headers.js.map +1 -0
- package/src/auth/index.d.ts +9 -0
- package/src/auth/index.js +15 -0
- package/src/auth/index.js.map +1 -0
- package/src/auth/token-factory.d.ts +94 -0
- package/src/auth/token-factory.js +181 -0
- package/src/auth/token-factory.js.map +1 -0
- package/src/auth/user-fixtures.d.ts +26 -0
- package/src/auth/user-fixtures.js +92 -0
- package/src/auth/user-fixtures.js.map +1 -0
- package/src/client/index.d.ts +7 -0
- package/src/client/index.js +12 -0
- package/src/client/index.js.map +1 -0
- package/src/client/mcp-test-client.builder.d.ts +72 -0
- package/src/client/mcp-test-client.builder.js +111 -0
- package/src/client/mcp-test-client.builder.js.map +1 -0
- package/src/client/mcp-test-client.d.ts +360 -0
- package/src/client/mcp-test-client.js +929 -0
- package/src/client/mcp-test-client.js.map +1 -0
- package/src/client/mcp-test-client.types.d.ts +216 -0
- package/src/client/mcp-test-client.types.js +7 -0
- package/src/client/mcp-test-client.types.js.map +1 -0
- package/src/errors/index.d.ts +45 -0
- package/src/errors/index.js +85 -0
- package/src/errors/index.js.map +1 -0
- package/src/expect.d.ts +67 -0
- package/src/expect.js +31 -0
- package/src/expect.js.map +1 -0
- package/src/fixtures/fixture-types.d.ts +166 -0
- package/src/fixtures/fixture-types.js +7 -0
- package/src/fixtures/fixture-types.js.map +1 -0
- package/src/fixtures/index.d.ts +7 -0
- package/src/fixtures/index.js +16 -0
- package/src/fixtures/index.js.map +1 -0
- package/src/fixtures/test-fixture.d.ts +41 -0
- package/src/fixtures/test-fixture.js +280 -0
- package/src/fixtures/test-fixture.js.map +1 -0
- package/src/http-mock/http-mock.d.ts +84 -0
- package/src/http-mock/http-mock.js +544 -0
- package/src/http-mock/http-mock.js.map +1 -0
- package/src/http-mock/http-mock.types.d.ts +124 -0
- package/src/http-mock/http-mock.types.js +10 -0
- package/src/http-mock/http-mock.types.js.map +1 -0
- package/src/http-mock/index.d.ts +6 -0
- package/src/http-mock/index.js +11 -0
- package/src/http-mock/index.js.map +1 -0
- package/src/index.d.ts +65 -0
- package/src/index.js +128 -0
- package/src/index.js.map +1 -0
- package/src/interceptor/index.d.ts +7 -0
- package/src/interceptor/index.js +15 -0
- package/src/interceptor/index.js.map +1 -0
- package/src/interceptor/interceptor-chain.d.ts +77 -0
- package/src/interceptor/interceptor-chain.js +207 -0
- package/src/interceptor/interceptor-chain.js.map +1 -0
- package/src/interceptor/interceptor.types.d.ts +131 -0
- package/src/interceptor/interceptor.types.js +7 -0
- package/src/interceptor/interceptor.types.js.map +1 -0
- package/src/interceptor/mock-registry.d.ts +82 -0
- package/src/interceptor/mock-registry.js +189 -0
- package/src/interceptor/mock-registry.js.map +1 -0
- package/src/matchers/index.d.ts +7 -0
- package/src/matchers/index.js +12 -0
- package/src/matchers/index.js.map +1 -0
- package/src/matchers/matcher-types.d.ts +266 -0
- package/src/matchers/matcher-types.js +10 -0
- package/src/matchers/matcher-types.js.map +1 -0
- package/src/matchers/mcp-matchers.d.ts +47 -0
- package/src/matchers/mcp-matchers.js +391 -0
- package/src/matchers/mcp-matchers.js.map +1 -0
- package/src/playwright/index.d.ts +37 -0
- package/src/playwright/index.js +49 -0
- package/src/playwright/index.js.map +1 -0
- package/src/server/index.d.ts +6 -0
- package/src/server/index.js +10 -0
- package/src/server/index.js.map +1 -0
- package/src/server/test-server.d.ts +99 -0
- package/src/server/test-server.js +286 -0
- package/src/server/test-server.js.map +1 -0
- package/src/setup.d.ts +22 -0
- package/src/setup.js +30 -0
- package/src/setup.js.map +1 -0
- package/src/transport/index.d.ts +6 -0
- package/src/transport/index.js +10 -0
- package/src/transport/index.js.map +1 -0
- package/src/transport/streamable-http.transport.d.ts +65 -0
- package/src/transport/streamable-http.transport.js +432 -0
- package/src/transport/streamable-http.transport.js.map +1 -0
- package/src/transport/transport.interface.d.ts +124 -0
- package/src/transport/transport.interface.js +7 -0
- package/src/transport/transport.interface.js.map +1 -0
- package/src/ui/index.d.ts +17 -0
- package/src/ui/index.js +23 -0
- package/src/ui/index.js.map +1 -0
- package/src/ui/ui-assertions.d.ts +94 -0
- package/src/ui/ui-assertions.js +215 -0
- package/src/ui/ui-assertions.js.map +1 -0
- package/src/ui/ui-matchers.d.ts +39 -0
- package/src/ui/ui-matchers.js +275 -0
- package/src/ui/ui-matchers.js.map +1 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file interceptor-chain.ts
|
|
4
|
+
* @description Interceptor chain for request/response interception
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.interceptors = exports.DefaultInterceptorChain = void 0;
|
|
8
|
+
const mock_registry_1 = require("./mock-registry");
|
|
9
|
+
/**
|
|
10
|
+
* Default implementation of InterceptorChain
|
|
11
|
+
*/
|
|
12
|
+
class DefaultInterceptorChain {
|
|
13
|
+
request = [];
|
|
14
|
+
response = [];
|
|
15
|
+
mocks;
|
|
16
|
+
constructor() {
|
|
17
|
+
this.mocks = new mock_registry_1.DefaultMockRegistry();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Add a request interceptor
|
|
21
|
+
*/
|
|
22
|
+
addRequestInterceptor(interceptor) {
|
|
23
|
+
this.request.push(interceptor);
|
|
24
|
+
return () => {
|
|
25
|
+
const index = this.request.indexOf(interceptor);
|
|
26
|
+
if (index !== -1) {
|
|
27
|
+
this.request.splice(index, 1);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add a response interceptor
|
|
33
|
+
*/
|
|
34
|
+
addResponseInterceptor(interceptor) {
|
|
35
|
+
this.response.push(interceptor);
|
|
36
|
+
return () => {
|
|
37
|
+
const index = this.response.indexOf(interceptor);
|
|
38
|
+
if (index !== -1) {
|
|
39
|
+
this.response.splice(index, 1);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Process a request through the interceptor chain
|
|
45
|
+
* Returns either:
|
|
46
|
+
* - { type: 'continue', request } - continue with (possibly modified) request
|
|
47
|
+
* - { type: 'mock', response } - return mock response immediately
|
|
48
|
+
* - { type: 'error', error } - throw error
|
|
49
|
+
*/
|
|
50
|
+
async processRequest(request, meta) {
|
|
51
|
+
let currentRequest = request;
|
|
52
|
+
// 1. Check mocks first
|
|
53
|
+
const mockDef = this.mocks.match(request);
|
|
54
|
+
if (mockDef) {
|
|
55
|
+
// Apply delay if specified
|
|
56
|
+
if (mockDef.delay && mockDef.delay > 0) {
|
|
57
|
+
await sleep(mockDef.delay);
|
|
58
|
+
}
|
|
59
|
+
// Get mock response
|
|
60
|
+
let mockResponse;
|
|
61
|
+
if (typeof mockDef.response === 'function') {
|
|
62
|
+
mockResponse = await mockDef.response(request);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
mockResponse = mockDef.response;
|
|
66
|
+
}
|
|
67
|
+
// Ensure the response ID matches the request ID
|
|
68
|
+
return {
|
|
69
|
+
type: 'mock',
|
|
70
|
+
response: { ...mockResponse, id: request.id ?? mockResponse.id },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// 2. Run request interceptors
|
|
74
|
+
for (const interceptor of this.request) {
|
|
75
|
+
const ctx = {
|
|
76
|
+
request: currentRequest,
|
|
77
|
+
meta,
|
|
78
|
+
};
|
|
79
|
+
const result = await interceptor(ctx);
|
|
80
|
+
switch (result.action) {
|
|
81
|
+
case 'passthrough':
|
|
82
|
+
// Continue with current request
|
|
83
|
+
break;
|
|
84
|
+
case 'modify':
|
|
85
|
+
// Use modified request
|
|
86
|
+
currentRequest = result.request;
|
|
87
|
+
break;
|
|
88
|
+
case 'mock':
|
|
89
|
+
// Return mock response immediately
|
|
90
|
+
return {
|
|
91
|
+
type: 'mock',
|
|
92
|
+
response: { ...result.response, id: request.id ?? result.response.id },
|
|
93
|
+
};
|
|
94
|
+
case 'error':
|
|
95
|
+
return { type: 'error', error: result.error };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { type: 'continue', request: currentRequest };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Process a response through the interceptor chain
|
|
102
|
+
*/
|
|
103
|
+
async processResponse(request, response, durationMs) {
|
|
104
|
+
let currentResponse = response;
|
|
105
|
+
for (const interceptor of this.response) {
|
|
106
|
+
const ctx = {
|
|
107
|
+
request,
|
|
108
|
+
response: currentResponse,
|
|
109
|
+
durationMs,
|
|
110
|
+
};
|
|
111
|
+
const result = await interceptor(ctx);
|
|
112
|
+
switch (result.action) {
|
|
113
|
+
case 'passthrough':
|
|
114
|
+
// Continue with current response
|
|
115
|
+
break;
|
|
116
|
+
case 'modify':
|
|
117
|
+
// Use modified response
|
|
118
|
+
currentResponse = result.response;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return currentResponse;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Clear all interceptors and mocks
|
|
126
|
+
*/
|
|
127
|
+
clear() {
|
|
128
|
+
this.request = [];
|
|
129
|
+
this.response = [];
|
|
130
|
+
this.mocks.clear();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.DefaultInterceptorChain = DefaultInterceptorChain;
|
|
134
|
+
/**
|
|
135
|
+
* Sleep helper
|
|
136
|
+
*/
|
|
137
|
+
function sleep(ms) {
|
|
138
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Convenience interceptor creators
|
|
142
|
+
*/
|
|
143
|
+
exports.interceptors = {
|
|
144
|
+
/**
|
|
145
|
+
* Create an interceptor that logs all requests
|
|
146
|
+
*/
|
|
147
|
+
logger(logFn = console.log) {
|
|
148
|
+
return (ctx) => {
|
|
149
|
+
logFn(`[MCP Request] ${ctx.request.method}`, ctx.request.params);
|
|
150
|
+
return { action: 'passthrough' };
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
/**
|
|
154
|
+
* Create an interceptor that adds latency to all requests
|
|
155
|
+
*/
|
|
156
|
+
delay(ms) {
|
|
157
|
+
return async () => {
|
|
158
|
+
await sleep(ms);
|
|
159
|
+
return { action: 'passthrough' };
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
/**
|
|
163
|
+
* Create an interceptor that fails requests matching a condition
|
|
164
|
+
*/
|
|
165
|
+
failWhen(condition, error) {
|
|
166
|
+
return (ctx) => {
|
|
167
|
+
if (condition(ctx)) {
|
|
168
|
+
const err = typeof error === 'string' ? new Error(error) : error;
|
|
169
|
+
return { action: 'error', error: err };
|
|
170
|
+
}
|
|
171
|
+
return { action: 'passthrough' };
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
/**
|
|
175
|
+
* Create an interceptor that modifies specific methods
|
|
176
|
+
*/
|
|
177
|
+
modifyMethod(method, modifier) {
|
|
178
|
+
return (ctx) => {
|
|
179
|
+
if (ctx.request.method === method) {
|
|
180
|
+
return { action: 'modify', request: modifier(ctx.request) };
|
|
181
|
+
}
|
|
182
|
+
return { action: 'passthrough' };
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
/**
|
|
186
|
+
* Create a response interceptor that logs responses
|
|
187
|
+
*/
|
|
188
|
+
responseLogger(logFn = console.log) {
|
|
189
|
+
return (ctx) => {
|
|
190
|
+
const status = ctx.response.error ? 'ERROR' : 'OK';
|
|
191
|
+
logFn(`[MCP Response] ${ctx.request.method} ${status} (${ctx.durationMs}ms)`, ctx.response);
|
|
192
|
+
return { action: 'passthrough' };
|
|
193
|
+
};
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* Create a response interceptor that modifies specific responses
|
|
197
|
+
*/
|
|
198
|
+
modifyResponse(method, modifier) {
|
|
199
|
+
return (ctx) => {
|
|
200
|
+
if (ctx.request.method === method) {
|
|
201
|
+
return { action: 'modify', response: modifier(ctx.response) };
|
|
202
|
+
}
|
|
203
|
+
return { action: 'passthrough' };
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=interceptor-chain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor-chain.js","sourceRoot":"","sources":["../../../src/interceptor/interceptor-chain.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file interceptor-chain.ts\n * @description Interceptor chain for request/response interception\n */\n\nimport type { JsonRpcRequest, JsonRpcResponse } from '../transport/transport.interface';\nimport type {\n InterceptorChain,\n RequestInterceptor,\n ResponseInterceptor,\n InterceptorContext,\n InterceptorResult,\n ResponseInterceptorContext,\n ResponseInterceptorResult,\n MockRegistry,\n} from './interceptor.types';\nimport { DefaultMockRegistry } from './mock-registry';\n\n/**\n * Default implementation of InterceptorChain\n */\nexport class DefaultInterceptorChain implements InterceptorChain {\n request: RequestInterceptor[] = [];\n response: ResponseInterceptor[] = [];\n mocks: MockRegistry;\n\n constructor() {\n this.mocks = new DefaultMockRegistry();\n }\n\n /**\n * Add a request interceptor\n */\n addRequestInterceptor(interceptor: RequestInterceptor): () => void {\n this.request.push(interceptor);\n return () => {\n const index = this.request.indexOf(interceptor);\n if (index !== -1) {\n this.request.splice(index, 1);\n }\n };\n }\n\n /**\n * Add a response interceptor\n */\n addResponseInterceptor(interceptor: ResponseInterceptor): () => void {\n this.response.push(interceptor);\n return () => {\n const index = this.response.indexOf(interceptor);\n if (index !== -1) {\n this.response.splice(index, 1);\n }\n };\n }\n\n /**\n * Process a request through the interceptor chain\n * Returns either:\n * - { type: 'continue', request } - continue with (possibly modified) request\n * - { type: 'mock', response } - return mock response immediately\n * - { type: 'error', error } - throw error\n */\n async processRequest(\n request: JsonRpcRequest,\n meta: InterceptorContext['meta'],\n ): Promise<\n | { type: 'continue'; request: JsonRpcRequest }\n | { type: 'mock'; response: JsonRpcResponse }\n | { type: 'error'; error: Error }\n > {\n let currentRequest = request;\n\n // 1. Check mocks first\n const mockDef = this.mocks.match(request);\n if (mockDef) {\n // Apply delay if specified\n if (mockDef.delay && mockDef.delay > 0) {\n await sleep(mockDef.delay);\n }\n\n // Get mock response\n let mockResponse: JsonRpcResponse;\n if (typeof mockDef.response === 'function') {\n mockResponse = await mockDef.response(request);\n } else {\n mockResponse = mockDef.response;\n }\n\n // Ensure the response ID matches the request ID\n return {\n type: 'mock',\n response: { ...mockResponse, id: request.id ?? mockResponse.id },\n };\n }\n\n // 2. Run request interceptors\n for (const interceptor of this.request) {\n const ctx: InterceptorContext = {\n request: currentRequest,\n meta,\n };\n\n const result = await interceptor(ctx);\n\n switch (result.action) {\n case 'passthrough':\n // Continue with current request\n break;\n case 'modify':\n // Use modified request\n currentRequest = result.request;\n break;\n case 'mock':\n // Return mock response immediately\n return {\n type: 'mock',\n response: { ...result.response, id: request.id ?? result.response.id },\n };\n case 'error':\n return { type: 'error', error: result.error };\n }\n }\n\n return { type: 'continue', request: currentRequest };\n }\n\n /**\n * Process a response through the interceptor chain\n */\n async processResponse(\n request: JsonRpcRequest,\n response: JsonRpcResponse,\n durationMs: number,\n ): Promise<JsonRpcResponse> {\n let currentResponse = response;\n\n for (const interceptor of this.response) {\n const ctx: ResponseInterceptorContext = {\n request,\n response: currentResponse,\n durationMs,\n };\n\n const result = await interceptor(ctx);\n\n switch (result.action) {\n case 'passthrough':\n // Continue with current response\n break;\n case 'modify':\n // Use modified response\n currentResponse = result.response;\n break;\n }\n }\n\n return currentResponse;\n }\n\n /**\n * Clear all interceptors and mocks\n */\n clear(): void {\n this.request = [];\n this.response = [];\n this.mocks.clear();\n }\n}\n\n/**\n * Sleep helper\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Convenience interceptor creators\n */\nexport const interceptors = {\n /**\n * Create an interceptor that logs all requests\n */\n logger(logFn: (message: string, data?: unknown) => void = console.log): RequestInterceptor {\n return (ctx) => {\n logFn(`[MCP Request] ${ctx.request.method}`, ctx.request.params);\n return { action: 'passthrough' };\n };\n },\n\n /**\n * Create an interceptor that adds latency to all requests\n */\n delay(ms: number): RequestInterceptor {\n return async () => {\n await sleep(ms);\n return { action: 'passthrough' };\n };\n },\n\n /**\n * Create an interceptor that fails requests matching a condition\n */\n failWhen(condition: (ctx: InterceptorContext) => boolean, error: Error | string): RequestInterceptor {\n return (ctx) => {\n if (condition(ctx)) {\n const err = typeof error === 'string' ? new Error(error) : error;\n return { action: 'error', error: err };\n }\n return { action: 'passthrough' };\n };\n },\n\n /**\n * Create an interceptor that modifies specific methods\n */\n modifyMethod(method: string, modifier: (request: JsonRpcRequest) => JsonRpcRequest): RequestInterceptor {\n return (ctx) => {\n if (ctx.request.method === method) {\n return { action: 'modify', request: modifier(ctx.request) };\n }\n return { action: 'passthrough' };\n };\n },\n\n /**\n * Create a response interceptor that logs responses\n */\n responseLogger(logFn: (message: string, data?: unknown) => void = console.log): ResponseInterceptor {\n return (ctx) => {\n const status = ctx.response.error ? 'ERROR' : 'OK';\n logFn(`[MCP Response] ${ctx.request.method} ${status} (${ctx.durationMs}ms)`, ctx.response);\n return { action: 'passthrough' };\n };\n },\n\n /**\n * Create a response interceptor that modifies specific responses\n */\n modifyResponse(method: string, modifier: (response: JsonRpcResponse) => JsonRpcResponse): ResponseInterceptor {\n return (ctx) => {\n if (ctx.request.method === method) {\n return { action: 'modify', response: modifier(ctx.response) };\n }\n return { action: 'passthrough' };\n };\n },\n};\n"]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file interceptor.types.ts
|
|
3
|
+
* @description Types for request/response interception and mocking
|
|
4
|
+
*/
|
|
5
|
+
import type { JsonRpcRequest, JsonRpcResponse } from '../transport/transport.interface';
|
|
6
|
+
/**
|
|
7
|
+
* Interceptor context passed to handler functions
|
|
8
|
+
*/
|
|
9
|
+
export interface InterceptorContext {
|
|
10
|
+
/** The original request */
|
|
11
|
+
request: JsonRpcRequest;
|
|
12
|
+
/** Request metadata */
|
|
13
|
+
meta: {
|
|
14
|
+
/** Timestamp when request was made */
|
|
15
|
+
timestamp: Date;
|
|
16
|
+
/** Transport type being used */
|
|
17
|
+
transport: string;
|
|
18
|
+
/** Session ID if available */
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Result of an interceptor - can modify, mock, or pass through
|
|
24
|
+
*/
|
|
25
|
+
export type InterceptorResult = {
|
|
26
|
+
action: 'passthrough';
|
|
27
|
+
} | {
|
|
28
|
+
action: 'modify';
|
|
29
|
+
request: JsonRpcRequest;
|
|
30
|
+
} | {
|
|
31
|
+
action: 'mock';
|
|
32
|
+
response: JsonRpcResponse;
|
|
33
|
+
} | {
|
|
34
|
+
action: 'error';
|
|
35
|
+
error: Error;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Function signature for request interceptors
|
|
39
|
+
*/
|
|
40
|
+
export type RequestInterceptor = (ctx: InterceptorContext) => InterceptorResult | Promise<InterceptorResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Response interceptor context
|
|
43
|
+
*/
|
|
44
|
+
export interface ResponseInterceptorContext {
|
|
45
|
+
/** The original request */
|
|
46
|
+
request: JsonRpcRequest;
|
|
47
|
+
/** The response from the server */
|
|
48
|
+
response: JsonRpcResponse;
|
|
49
|
+
/** Duration of the request in ms */
|
|
50
|
+
durationMs: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Response interceptor result
|
|
54
|
+
*/
|
|
55
|
+
export type ResponseInterceptorResult = {
|
|
56
|
+
action: 'passthrough';
|
|
57
|
+
} | {
|
|
58
|
+
action: 'modify';
|
|
59
|
+
response: JsonRpcResponse;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Function signature for response interceptors
|
|
63
|
+
*/
|
|
64
|
+
export type ResponseInterceptor = (ctx: ResponseInterceptorContext) => ResponseInterceptorResult | Promise<ResponseInterceptorResult>;
|
|
65
|
+
/**
|
|
66
|
+
* Mock definition for a specific method
|
|
67
|
+
*/
|
|
68
|
+
export interface MockDefinition {
|
|
69
|
+
/** Method name to mock (e.g., 'tools/call', 'resources/read') */
|
|
70
|
+
method: string;
|
|
71
|
+
/** Optional matcher for params - if provided, only matches when params match */
|
|
72
|
+
params?: Record<string, unknown> | ((params: Record<string, unknown>) => boolean);
|
|
73
|
+
/** The mock response to return */
|
|
74
|
+
response: JsonRpcResponse | ((request: JsonRpcRequest) => JsonRpcResponse | Promise<JsonRpcResponse>);
|
|
75
|
+
/** Number of times this mock should be used (default: Infinity) */
|
|
76
|
+
times?: number;
|
|
77
|
+
/** Delay in ms before returning the response (simulates latency) */
|
|
78
|
+
delay?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Mock registry for managing active mocks
|
|
82
|
+
*/
|
|
83
|
+
export interface MockRegistry {
|
|
84
|
+
/** Add a mock */
|
|
85
|
+
add(mock: MockDefinition): MockHandle;
|
|
86
|
+
/** Remove all mocks */
|
|
87
|
+
clear(): void;
|
|
88
|
+
/** Get all active mocks */
|
|
89
|
+
getAll(): MockDefinition[];
|
|
90
|
+
/** Check if a request matches any mock */
|
|
91
|
+
match(request: JsonRpcRequest): MockDefinition | undefined;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Handle returned when adding a mock - allows removal
|
|
95
|
+
*/
|
|
96
|
+
export interface MockHandle {
|
|
97
|
+
/** Remove this specific mock */
|
|
98
|
+
remove(): void;
|
|
99
|
+
/** Check how many times this mock was called */
|
|
100
|
+
callCount(): number;
|
|
101
|
+
/** Get all calls made to this mock */
|
|
102
|
+
calls(): JsonRpcRequest[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Interceptor chain configuration
|
|
106
|
+
*/
|
|
107
|
+
export interface InterceptorChain {
|
|
108
|
+
/** Request interceptors (run before request) */
|
|
109
|
+
request: RequestInterceptor[];
|
|
110
|
+
/** Response interceptors (run after response) */
|
|
111
|
+
response: ResponseInterceptor[];
|
|
112
|
+
/** Mock registry */
|
|
113
|
+
mocks: MockRegistry;
|
|
114
|
+
/**
|
|
115
|
+
* Process a request through the interceptor chain
|
|
116
|
+
*/
|
|
117
|
+
processRequest(request: JsonRpcRequest, meta: InterceptorContext['meta']): Promise<{
|
|
118
|
+
type: 'continue';
|
|
119
|
+
request: JsonRpcRequest;
|
|
120
|
+
} | {
|
|
121
|
+
type: 'mock';
|
|
122
|
+
response: JsonRpcResponse;
|
|
123
|
+
} | {
|
|
124
|
+
type: 'error';
|
|
125
|
+
error: Error;
|
|
126
|
+
}>;
|
|
127
|
+
/**
|
|
128
|
+
* Process a response through the interceptor chain
|
|
129
|
+
*/
|
|
130
|
+
processResponse(request: JsonRpcRequest, response: JsonRpcResponse, durationMs: number): Promise<JsonRpcResponse>;
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.types.js","sourceRoot":"","sources":["../../../src/interceptor/interceptor.types.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/**\n * @file interceptor.types.ts\n * @description Types for request/response interception and mocking\n */\n\nimport type { JsonRpcRequest, JsonRpcResponse } from '../transport/transport.interface';\n\n/**\n * Interceptor context passed to handler functions\n */\nexport interface InterceptorContext {\n /** The original request */\n request: JsonRpcRequest;\n /** Request metadata */\n meta: {\n /** Timestamp when request was made */\n timestamp: Date;\n /** Transport type being used */\n transport: string;\n /** Session ID if available */\n sessionId?: string;\n };\n}\n\n/**\n * Result of an interceptor - can modify, mock, or pass through\n */\nexport type InterceptorResult =\n | { action: 'passthrough' }\n | { action: 'modify'; request: JsonRpcRequest }\n | { action: 'mock'; response: JsonRpcResponse }\n | { action: 'error'; error: Error };\n\n/**\n * Function signature for request interceptors\n */\nexport type RequestInterceptor = (ctx: InterceptorContext) => InterceptorResult | Promise<InterceptorResult>;\n\n/**\n * Response interceptor context\n */\nexport interface ResponseInterceptorContext {\n /** The original request */\n request: JsonRpcRequest;\n /** The response from the server */\n response: JsonRpcResponse;\n /** Duration of the request in ms */\n durationMs: number;\n}\n\n/**\n * Response interceptor result\n */\nexport type ResponseInterceptorResult = { action: 'passthrough' } | { action: 'modify'; response: JsonRpcResponse };\n\n/**\n * Function signature for response interceptors\n */\nexport type ResponseInterceptor = (\n ctx: ResponseInterceptorContext,\n) => ResponseInterceptorResult | Promise<ResponseInterceptorResult>;\n\n/**\n * Mock definition for a specific method\n */\nexport interface MockDefinition {\n /** Method name to mock (e.g., 'tools/call', 'resources/read') */\n method: string;\n /** Optional matcher for params - if provided, only matches when params match */\n params?: Record<string, unknown> | ((params: Record<string, unknown>) => boolean);\n /** The mock response to return */\n response: JsonRpcResponse | ((request: JsonRpcRequest) => JsonRpcResponse | Promise<JsonRpcResponse>);\n /** Number of times this mock should be used (default: Infinity) */\n times?: number;\n /** Delay in ms before returning the response (simulates latency) */\n delay?: number;\n}\n\n/**\n * Mock registry for managing active mocks\n */\nexport interface MockRegistry {\n /** Add a mock */\n add(mock: MockDefinition): MockHandle;\n /** Remove all mocks */\n clear(): void;\n /** Get all active mocks */\n getAll(): MockDefinition[];\n /** Check if a request matches any mock */\n match(request: JsonRpcRequest): MockDefinition | undefined;\n}\n\n/**\n * Handle returned when adding a mock - allows removal\n */\nexport interface MockHandle {\n /** Remove this specific mock */\n remove(): void;\n /** Check how many times this mock was called */\n callCount(): number;\n /** Get all calls made to this mock */\n calls(): JsonRpcRequest[];\n}\n\n/**\n * Interceptor chain configuration\n */\nexport interface InterceptorChain {\n /** Request interceptors (run before request) */\n request: RequestInterceptor[];\n /** Response interceptors (run after response) */\n response: ResponseInterceptor[];\n /** Mock registry */\n mocks: MockRegistry;\n\n /**\n * Process a request through the interceptor chain\n */\n processRequest(\n request: JsonRpcRequest,\n meta: InterceptorContext['meta'],\n ): Promise<\n | { type: 'continue'; request: JsonRpcRequest }\n | { type: 'mock'; response: JsonRpcResponse }\n | { type: 'error'; error: Error }\n >;\n\n /**\n * Process a response through the interceptor chain\n */\n processResponse(request: JsonRpcRequest, response: JsonRpcResponse, durationMs: number): Promise<JsonRpcResponse>;\n}\n"]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file mock-registry.ts
|
|
3
|
+
* @description Registry for managing request mocks
|
|
4
|
+
*/
|
|
5
|
+
import type { JsonRpcRequest, JsonRpcResponse } from '../transport/transport.interface';
|
|
6
|
+
import type { MockDefinition, MockRegistry, MockHandle } from './interceptor.types';
|
|
7
|
+
/**
|
|
8
|
+
* Default implementation of MockRegistry
|
|
9
|
+
*/
|
|
10
|
+
export declare class DefaultMockRegistry implements MockRegistry {
|
|
11
|
+
private mocks;
|
|
12
|
+
add(mock: MockDefinition): MockHandle;
|
|
13
|
+
clear(): void;
|
|
14
|
+
getAll(): MockDefinition[];
|
|
15
|
+
match(request: JsonRpcRequest): MockDefinition | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Check if request params match the mock params definition
|
|
18
|
+
*/
|
|
19
|
+
private paramsMatch;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper to create mock responses
|
|
23
|
+
*/
|
|
24
|
+
export declare const mockResponse: {
|
|
25
|
+
/**
|
|
26
|
+
* Create a successful JSON-RPC response
|
|
27
|
+
*/
|
|
28
|
+
success<T>(result: T, id?: string | number): JsonRpcResponse;
|
|
29
|
+
/**
|
|
30
|
+
* Create an error JSON-RPC response
|
|
31
|
+
*/
|
|
32
|
+
error(code: number, message: string, data?: unknown, id?: string | number | null): JsonRpcResponse;
|
|
33
|
+
/**
|
|
34
|
+
* Create a tool result response
|
|
35
|
+
*/
|
|
36
|
+
toolResult(content: Array<{
|
|
37
|
+
type: "text";
|
|
38
|
+
text: string;
|
|
39
|
+
} | {
|
|
40
|
+
type: "image";
|
|
41
|
+
data: string;
|
|
42
|
+
mimeType: string;
|
|
43
|
+
}>, id?: string | number): JsonRpcResponse;
|
|
44
|
+
/**
|
|
45
|
+
* Create a tools/list response
|
|
46
|
+
*/
|
|
47
|
+
toolsList(tools: Array<{
|
|
48
|
+
name: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
inputSchema?: Record<string, unknown>;
|
|
51
|
+
}>, id?: string | number): JsonRpcResponse;
|
|
52
|
+
/**
|
|
53
|
+
* Create a resources/list response
|
|
54
|
+
*/
|
|
55
|
+
resourcesList(resources: Array<{
|
|
56
|
+
uri: string;
|
|
57
|
+
name?: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
mimeType?: string;
|
|
60
|
+
}>, id?: string | number): JsonRpcResponse;
|
|
61
|
+
/**
|
|
62
|
+
* Create a resources/read response
|
|
63
|
+
*/
|
|
64
|
+
resourceRead(contents: Array<{
|
|
65
|
+
uri: string;
|
|
66
|
+
text?: string;
|
|
67
|
+
blob?: string;
|
|
68
|
+
mimeType?: string;
|
|
69
|
+
}>, id?: string | number): JsonRpcResponse;
|
|
70
|
+
/**
|
|
71
|
+
* Common MCP errors
|
|
72
|
+
*/
|
|
73
|
+
errors: {
|
|
74
|
+
methodNotFound: (method: string, id?: string | number | null) => JsonRpcResponse;
|
|
75
|
+
invalidParams: (message: string, id?: string | number | null) => JsonRpcResponse;
|
|
76
|
+
internalError: (message: string, id?: string | number | null) => JsonRpcResponse;
|
|
77
|
+
resourceNotFound: (uri: string, id?: string | number | null) => JsonRpcResponse;
|
|
78
|
+
toolNotFound: (name: string, id?: string | number | null) => JsonRpcResponse;
|
|
79
|
+
unauthorized: (id?: string | number | null) => JsonRpcResponse;
|
|
80
|
+
forbidden: (id?: string | number | null) => JsonRpcResponse;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file mock-registry.ts
|
|
4
|
+
* @description Registry for managing request mocks
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.mockResponse = exports.DefaultMockRegistry = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Default implementation of MockRegistry
|
|
10
|
+
*/
|
|
11
|
+
class DefaultMockRegistry {
|
|
12
|
+
mocks = [];
|
|
13
|
+
add(mock) {
|
|
14
|
+
const entry = {
|
|
15
|
+
definition: mock,
|
|
16
|
+
callCount: 0,
|
|
17
|
+
calls: [],
|
|
18
|
+
remainingUses: mock.times ?? Infinity,
|
|
19
|
+
};
|
|
20
|
+
this.mocks.push(entry);
|
|
21
|
+
return {
|
|
22
|
+
remove: () => {
|
|
23
|
+
const index = this.mocks.indexOf(entry);
|
|
24
|
+
if (index !== -1) {
|
|
25
|
+
this.mocks.splice(index, 1);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
callCount: () => entry.callCount,
|
|
29
|
+
calls: () => [...entry.calls],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
clear() {
|
|
33
|
+
this.mocks = [];
|
|
34
|
+
}
|
|
35
|
+
getAll() {
|
|
36
|
+
return this.mocks.map((e) => e.definition);
|
|
37
|
+
}
|
|
38
|
+
match(request) {
|
|
39
|
+
for (const entry of this.mocks) {
|
|
40
|
+
if (entry.remainingUses <= 0)
|
|
41
|
+
continue;
|
|
42
|
+
const { definition } = entry;
|
|
43
|
+
// Check method match
|
|
44
|
+
if (definition.method !== request.method)
|
|
45
|
+
continue;
|
|
46
|
+
// Check params match if specified
|
|
47
|
+
if (definition.params !== undefined) {
|
|
48
|
+
const params = request.params ?? {};
|
|
49
|
+
if (typeof definition.params === 'function') {
|
|
50
|
+
// Custom matcher function
|
|
51
|
+
if (!definition.params(params))
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Object deep equality check
|
|
56
|
+
if (!this.paramsMatch(definition.params, params))
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Found a match - update tracking
|
|
61
|
+
entry.callCount++;
|
|
62
|
+
entry.calls.push(request);
|
|
63
|
+
entry.remainingUses--;
|
|
64
|
+
return definition;
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if request params match the mock params definition
|
|
70
|
+
*/
|
|
71
|
+
paramsMatch(expected, actual) {
|
|
72
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
73
|
+
if (!(key in actual))
|
|
74
|
+
return false;
|
|
75
|
+
const actualValue = actual[key];
|
|
76
|
+
// Handle arrays explicitly
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
if (!Array.isArray(actualValue))
|
|
79
|
+
return false;
|
|
80
|
+
if (value.length !== actualValue.length)
|
|
81
|
+
return false;
|
|
82
|
+
for (let i = 0; i < value.length; i++) {
|
|
83
|
+
const expectedItem = value[i];
|
|
84
|
+
const actualItem = actualValue[i];
|
|
85
|
+
if (typeof expectedItem === 'object' && expectedItem !== null) {
|
|
86
|
+
if (typeof actualItem !== 'object' || actualItem === null)
|
|
87
|
+
return false;
|
|
88
|
+
if (!this.paramsMatch(expectedItem, actualItem)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (actualItem !== expectedItem) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (typeof value === 'object' && value !== null) {
|
|
98
|
+
if (typeof actualValue !== 'object' || actualValue === null)
|
|
99
|
+
return false;
|
|
100
|
+
if (!this.paramsMatch(value, actualValue)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (actualValue !== value) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
exports.DefaultMockRegistry = DefaultMockRegistry;
|
|
112
|
+
/**
|
|
113
|
+
* Helper to create mock responses
|
|
114
|
+
*/
|
|
115
|
+
exports.mockResponse = {
|
|
116
|
+
/**
|
|
117
|
+
* Create a successful JSON-RPC response
|
|
118
|
+
*/
|
|
119
|
+
success(result, id = 1) {
|
|
120
|
+
return {
|
|
121
|
+
jsonrpc: '2.0',
|
|
122
|
+
id,
|
|
123
|
+
result,
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
/**
|
|
127
|
+
* Create an error JSON-RPC response
|
|
128
|
+
*/
|
|
129
|
+
error(code, message, data, id = 1) {
|
|
130
|
+
return {
|
|
131
|
+
jsonrpc: '2.0',
|
|
132
|
+
id,
|
|
133
|
+
error: { code, message, data },
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* Create a tool result response
|
|
138
|
+
*/
|
|
139
|
+
toolResult(content, id = 1) {
|
|
140
|
+
return {
|
|
141
|
+
jsonrpc: '2.0',
|
|
142
|
+
id,
|
|
143
|
+
result: { content },
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
/**
|
|
147
|
+
* Create a tools/list response
|
|
148
|
+
*/
|
|
149
|
+
toolsList(tools, id = 1) {
|
|
150
|
+
return {
|
|
151
|
+
jsonrpc: '2.0',
|
|
152
|
+
id,
|
|
153
|
+
result: { tools },
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* Create a resources/list response
|
|
158
|
+
*/
|
|
159
|
+
resourcesList(resources, id = 1) {
|
|
160
|
+
return {
|
|
161
|
+
jsonrpc: '2.0',
|
|
162
|
+
id,
|
|
163
|
+
result: { resources },
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* Create a resources/read response
|
|
168
|
+
*/
|
|
169
|
+
resourceRead(contents, id = 1) {
|
|
170
|
+
return {
|
|
171
|
+
jsonrpc: '2.0',
|
|
172
|
+
id,
|
|
173
|
+
result: { contents },
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* Common MCP errors
|
|
178
|
+
*/
|
|
179
|
+
errors: {
|
|
180
|
+
methodNotFound: (method, id = 1) => exports.mockResponse.error(-32601, `Method not found: ${method}`, undefined, id),
|
|
181
|
+
invalidParams: (message, id = 1) => exports.mockResponse.error(-32602, message, undefined, id),
|
|
182
|
+
internalError: (message, id = 1) => exports.mockResponse.error(-32603, message, undefined, id),
|
|
183
|
+
resourceNotFound: (uri, id = 1) => exports.mockResponse.error(-32002, `Resource not found: ${uri}`, { uri }, id),
|
|
184
|
+
toolNotFound: (name, id = 1) => exports.mockResponse.error(-32601, `Tool not found: ${name}`, { name }, id),
|
|
185
|
+
unauthorized: (id = 1) => exports.mockResponse.error(-32001, 'Unauthorized', undefined, id),
|
|
186
|
+
forbidden: (id = 1) => exports.mockResponse.error(-32003, 'Forbidden', undefined, id),
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=mock-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-registry.js","sourceRoot":"","sources":["../../../src/interceptor/mock-registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @file mock-registry.ts\n * @description Registry for managing request mocks\n */\n\nimport type { JsonRpcRequest, JsonRpcResponse } from '../transport/transport.interface';\nimport type { MockDefinition, MockRegistry, MockHandle } from './interceptor.types';\n\n/**\n * Internal mock entry with tracking\n */\ninterface MockEntry {\n definition: MockDefinition;\n callCount: number;\n calls: JsonRpcRequest[];\n remainingUses: number;\n}\n\n/**\n * Default implementation of MockRegistry\n */\nexport class DefaultMockRegistry implements MockRegistry {\n private mocks: MockEntry[] = [];\n\n add(mock: MockDefinition): MockHandle {\n const entry: MockEntry = {\n definition: mock,\n callCount: 0,\n calls: [],\n remainingUses: mock.times ?? Infinity,\n };\n\n this.mocks.push(entry);\n\n return {\n remove: () => {\n const index = this.mocks.indexOf(entry);\n if (index !== -1) {\n this.mocks.splice(index, 1);\n }\n },\n callCount: () => entry.callCount,\n calls: () => [...entry.calls],\n };\n }\n\n clear(): void {\n this.mocks = [];\n }\n\n getAll(): MockDefinition[] {\n return this.mocks.map((e) => e.definition);\n }\n\n match(request: JsonRpcRequest): MockDefinition | undefined {\n for (const entry of this.mocks) {\n if (entry.remainingUses <= 0) continue;\n\n const { definition } = entry;\n\n // Check method match\n if (definition.method !== request.method) continue;\n\n // Check params match if specified\n if (definition.params !== undefined) {\n const params = request.params ?? {};\n\n if (typeof definition.params === 'function') {\n // Custom matcher function\n if (!definition.params(params)) continue;\n } else {\n // Object deep equality check\n if (!this.paramsMatch(definition.params, params)) continue;\n }\n }\n\n // Found a match - update tracking\n entry.callCount++;\n entry.calls.push(request);\n entry.remainingUses--;\n\n return definition;\n }\n\n return undefined;\n }\n\n /**\n * Check if request params match the mock params definition\n */\n private paramsMatch(expected: Record<string, unknown>, actual: Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(expected)) {\n if (!(key in actual)) return false;\n\n const actualValue = actual[key];\n\n // Handle arrays explicitly\n if (Array.isArray(value)) {\n if (!Array.isArray(actualValue)) return false;\n if (value.length !== actualValue.length) return false;\n for (let i = 0; i < value.length; i++) {\n const expectedItem = value[i];\n const actualItem = actualValue[i];\n if (typeof expectedItem === 'object' && expectedItem !== null) {\n if (typeof actualItem !== 'object' || actualItem === null) return false;\n if (!this.paramsMatch(expectedItem as Record<string, unknown>, actualItem as Record<string, unknown>)) {\n return false;\n }\n } else if (actualItem !== expectedItem) {\n return false;\n }\n }\n } else if (typeof value === 'object' && value !== null) {\n if (typeof actualValue !== 'object' || actualValue === null) return false;\n if (!this.paramsMatch(value as Record<string, unknown>, actualValue as Record<string, unknown>)) {\n return false;\n }\n } else if (actualValue !== value) {\n return false;\n }\n }\n\n return true;\n }\n}\n\n/**\n * Helper to create mock responses\n */\nexport const mockResponse = {\n /**\n * Create a successful JSON-RPC response\n */\n success<T>(result: T, id: string | number = 1): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n result,\n };\n },\n\n /**\n * Create an error JSON-RPC response\n */\n error(code: number, message: string, data?: unknown, id: string | number | null = 1): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n error: { code, message, data },\n };\n },\n\n /**\n * Create a tool result response\n */\n toolResult(\n content: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }>,\n id: string | number = 1,\n ): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n result: { content },\n };\n },\n\n /**\n * Create a tools/list response\n */\n toolsList(\n tools: Array<{ name: string; description?: string; inputSchema?: Record<string, unknown> }>,\n id: string | number = 1,\n ): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n result: { tools },\n };\n },\n\n /**\n * Create a resources/list response\n */\n resourcesList(\n resources: Array<{ uri: string; name?: string; description?: string; mimeType?: string }>,\n id: string | number = 1,\n ): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n result: { resources },\n };\n },\n\n /**\n * Create a resources/read response\n */\n resourceRead(\n contents: Array<{ uri: string; text?: string; blob?: string; mimeType?: string }>,\n id: string | number = 1,\n ): JsonRpcResponse {\n return {\n jsonrpc: '2.0',\n id,\n result: { contents },\n };\n },\n\n /**\n * Common MCP errors\n */\n errors: {\n methodNotFound: (method: string, id: string | number | null = 1) =>\n mockResponse.error(-32601, `Method not found: ${method}`, undefined, id),\n\n invalidParams: (message: string, id: string | number | null = 1) =>\n mockResponse.error(-32602, message, undefined, id),\n\n internalError: (message: string, id: string | number | null = 1) =>\n mockResponse.error(-32603, message, undefined, id),\n\n resourceNotFound: (uri: string, id: string | number | null = 1) =>\n mockResponse.error(-32002, `Resource not found: ${uri}`, { uri }, id),\n\n toolNotFound: (name: string, id: string | number | null = 1) =>\n mockResponse.error(-32601, `Tool not found: ${name}`, { name }, id),\n\n unauthorized: (id: string | number | null = 1) => mockResponse.error(-32001, 'Unauthorized', undefined, id),\n\n forbidden: (id: string | number | null = 1) => mockResponse.error(-32003, 'Forbidden', undefined, id),\n },\n};\n"]}
|