@eaperezc/mcpgen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1049 @@
1
+ import { z } from 'zod';
2
+ import { AsyncLocalStorage } from 'async_hooks';
3
+ import { randomUUID } from 'crypto';
4
+ import pino from 'pino';
5
+
6
+ // src/testing/TestClient.ts
7
+
8
+ // src/errors/McpError.ts
9
+ var McpError = class _McpError extends Error {
10
+ code;
11
+ details;
12
+ timestamp;
13
+ constructor(options) {
14
+ super(options.message);
15
+ this.name = "McpError";
16
+ this.code = options.code;
17
+ this.details = options.details;
18
+ this.timestamp = /* @__PURE__ */ new Date();
19
+ if (options.cause) {
20
+ this.cause = options.cause;
21
+ }
22
+ Error.captureStackTrace?.(this, this.constructor);
23
+ }
24
+ /**
25
+ * Convert error to a JSON-serializable object
26
+ */
27
+ toJSON() {
28
+ return {
29
+ name: this.name,
30
+ code: this.code,
31
+ message: this.message,
32
+ details: this.details,
33
+ timestamp: this.timestamp.toISOString(),
34
+ stack: this.stack
35
+ };
36
+ }
37
+ /**
38
+ * Create an error for tool not found
39
+ */
40
+ static toolNotFound(toolName) {
41
+ return new _McpError({
42
+ code: "TOOL_NOT_FOUND" /* TOOL_NOT_FOUND */,
43
+ message: `Tool '${toolName}' not found`,
44
+ details: { toolName }
45
+ });
46
+ }
47
+ /**
48
+ * Create an error for resource not found
49
+ */
50
+ static resourceNotFound(uri) {
51
+ return new _McpError({
52
+ code: "RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */,
53
+ message: `Resource '${uri}' not found`,
54
+ details: { uri }
55
+ });
56
+ }
57
+ /**
58
+ * Create an error for prompt not found
59
+ */
60
+ static promptNotFound(promptName) {
61
+ return new _McpError({
62
+ code: "PROMPT_NOT_FOUND" /* PROMPT_NOT_FOUND */,
63
+ message: `Prompt '${promptName}' not found`,
64
+ details: { promptName }
65
+ });
66
+ }
67
+ /**
68
+ * Create a validation error
69
+ */
70
+ static validationError(message, details) {
71
+ return new _McpError({
72
+ code: "VALIDATION_ERROR" /* VALIDATION_ERROR */,
73
+ message,
74
+ details
75
+ });
76
+ }
77
+ /**
78
+ * Create an authentication error
79
+ */
80
+ static authError(code, message) {
81
+ return new _McpError({
82
+ code,
83
+ message
84
+ });
85
+ }
86
+ /**
87
+ * Create an internal error
88
+ */
89
+ static internal(message, cause) {
90
+ return new _McpError({
91
+ code: "INTERNAL_ERROR" /* INTERNAL_ERROR */,
92
+ message,
93
+ cause
94
+ });
95
+ }
96
+ };
97
+
98
+ // src/logging/types.ts
99
+ var LOG_LEVEL_PRIORITY = {
100
+ debug: 0,
101
+ info: 1,
102
+ warn: 2,
103
+ error: 3
104
+ };
105
+
106
+ // src/logging/Logger.ts
107
+ var Logger = class _Logger {
108
+ pino;
109
+ config;
110
+ defaultContext;
111
+ constructor(config = {}) {
112
+ this.config = {
113
+ level: config.level ?? "info",
114
+ json: config.json ?? process.env["NODE_ENV"] === "production",
115
+ timestamps: config.timestamps ?? true,
116
+ pretty: config.pretty ?? process.env["NODE_ENV"] !== "production",
117
+ defaultContext: config.defaultContext ?? {}
118
+ };
119
+ this.defaultContext = this.config.defaultContext ?? {};
120
+ this.pino = pino({
121
+ level: this.config.level,
122
+ transport: this.config.pretty ? {
123
+ target: "pino-pretty",
124
+ options: {
125
+ colorize: true,
126
+ translateTime: "SYS:standard",
127
+ ignore: "pid,hostname"
128
+ }
129
+ } : void 0,
130
+ formatters: {
131
+ level: (label) => ({ level: label })
132
+ },
133
+ timestamp: this.config.timestamps ? pino.stdTimeFunctions.isoTime : false
134
+ });
135
+ }
136
+ /**
137
+ * Check if a log level should be output
138
+ */
139
+ shouldLog(level) {
140
+ return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[this.config.level];
141
+ }
142
+ /**
143
+ * Create a child logger with additional context
144
+ */
145
+ child(context) {
146
+ const child = new _Logger(this.config);
147
+ child.defaultContext = { ...this.defaultContext, ...context };
148
+ child.pino = this.pino.child(context);
149
+ return child;
150
+ }
151
+ /**
152
+ * Log a debug message
153
+ */
154
+ debug(message, context) {
155
+ if (this.shouldLog("debug")) {
156
+ this.pino.debug({ ...this.defaultContext, ...context }, message);
157
+ }
158
+ }
159
+ /**
160
+ * Log an info message
161
+ */
162
+ info(message, context) {
163
+ if (this.shouldLog("info")) {
164
+ this.pino.info({ ...this.defaultContext, ...context }, message);
165
+ }
166
+ }
167
+ /**
168
+ * Log a warning message
169
+ */
170
+ warn(message, context) {
171
+ if (this.shouldLog("warn")) {
172
+ this.pino.warn({ ...this.defaultContext, ...context }, message);
173
+ }
174
+ }
175
+ /**
176
+ * Log an error message
177
+ */
178
+ error(message, error) {
179
+ if (this.shouldLog("error")) {
180
+ if (error instanceof Error) {
181
+ this.pino.error(
182
+ {
183
+ ...this.defaultContext,
184
+ err: {
185
+ message: error.message,
186
+ name: error.name,
187
+ stack: error.stack
188
+ }
189
+ },
190
+ message
191
+ );
192
+ } else {
193
+ this.pino.error({ ...this.defaultContext, ...error }, message);
194
+ }
195
+ }
196
+ }
197
+ /**
198
+ * Log with correlation ID for request tracing
199
+ */
200
+ withCorrelationId(correlationId) {
201
+ return this.child({ correlationId });
202
+ }
203
+ /**
204
+ * Set the log level
205
+ */
206
+ setLevel(level) {
207
+ this.config.level = level;
208
+ this.pino.level = level;
209
+ }
210
+ /**
211
+ * Get the current log level
212
+ */
213
+ getLevel() {
214
+ return this.config.level;
215
+ }
216
+ };
217
+ var logger = new Logger();
218
+
219
+ // src/context/RequestContext.ts
220
+ var asyncLocalStorage = new AsyncLocalStorage();
221
+ function createRequestContext(correlationId, logger2) {
222
+ const id = randomUUID();
223
+ const contextLogger = (logger2 ?? logger).withCorrelationId(id);
224
+ return {
225
+ correlationId: id,
226
+ startTime: /* @__PURE__ */ new Date(),
227
+ logger: contextLogger,
228
+ custom: /* @__PURE__ */ new Map()
229
+ };
230
+ }
231
+ async function runInRequestContextAsync(fn, context) {
232
+ const ctx = context ?? createRequestContext();
233
+ return asyncLocalStorage.run(ctx, fn);
234
+ }
235
+ function getRequestContext() {
236
+ return asyncLocalStorage.getStore();
237
+ }
238
+ function requireRequestContext() {
239
+ const context = getRequestContext();
240
+ if (!context) {
241
+ throw new Error("No request context available. Ensure code is running within runInRequestContext.");
242
+ }
243
+ return context;
244
+ }
245
+ function getContextLogger() {
246
+ return getRequestContext()?.logger ?? logger;
247
+ }
248
+ function setAuthContext(auth) {
249
+ const context = requireRequestContext();
250
+ context.auth = auth;
251
+ }
252
+
253
+ // src/tools/BaseTool.ts
254
+ var BaseTool = class {
255
+ /** Required scopes for this tool (optional) */
256
+ scopes = [];
257
+ /**
258
+ * Validate and execute the tool
259
+ */
260
+ async run(input) {
261
+ const logger2 = getContextLogger();
262
+ const parseResult = this.schema.safeParse(input);
263
+ if (!parseResult.success) {
264
+ const errors = parseResult.error.errors.map((e) => ({
265
+ path: e.path.join("."),
266
+ message: e.message
267
+ }));
268
+ logger2.warn("Tool input validation failed", {
269
+ tool: this.name,
270
+ errors
271
+ });
272
+ throw new McpError({
273
+ code: "TOOL_VALIDATION_ERROR" /* TOOL_VALIDATION_ERROR */,
274
+ message: `Invalid input for tool '${this.name}'`,
275
+ details: { errors }
276
+ });
277
+ }
278
+ try {
279
+ logger2.debug("Executing tool", { tool: this.name });
280
+ const result = await this.execute(parseResult.data);
281
+ logger2.debug("Tool executed successfully", { tool: this.name });
282
+ return result;
283
+ } catch (error) {
284
+ if (error instanceof McpError) {
285
+ throw error;
286
+ }
287
+ logger2.error("Tool execution failed", error instanceof Error ? error : void 0);
288
+ throw new McpError({
289
+ code: "TOOL_EXECUTION_ERROR" /* TOOL_EXECUTION_ERROR */,
290
+ message: `Tool '${this.name}' execution failed: ${error instanceof Error ? error.message : "Unknown error"}`,
291
+ cause: error instanceof Error ? error : void 0
292
+ });
293
+ }
294
+ }
295
+ /**
296
+ * Get tool metadata for registration
297
+ */
298
+ getMetadata() {
299
+ return {
300
+ name: this.name,
301
+ description: this.description,
302
+ inputSchema: this.schema,
303
+ scopes: this.scopes
304
+ };
305
+ }
306
+ /**
307
+ * Convert schema to JSON Schema format for MCP protocol
308
+ */
309
+ getJsonSchema() {
310
+ return zodToJsonSchema(this.schema);
311
+ }
312
+ };
313
+ function zodToJsonSchema(schema) {
314
+ const def = schema._def;
315
+ const typeName = def.typeName;
316
+ if (typeName === "ZodObject") {
317
+ const shape = schema.shape;
318
+ const properties = {};
319
+ const required = [];
320
+ for (const [key, value] of Object.entries(shape)) {
321
+ const zodValue = value;
322
+ properties[key] = zodToJsonSchema(zodValue);
323
+ if (!zodValue.isOptional()) {
324
+ required.push(key);
325
+ }
326
+ }
327
+ return {
328
+ type: "object",
329
+ properties,
330
+ required: required.length > 0 ? required : void 0
331
+ };
332
+ }
333
+ if (typeName === "ZodString") {
334
+ const result = { type: "string" };
335
+ if (def.description) result["description"] = def.description;
336
+ return result;
337
+ }
338
+ if (typeName === "ZodNumber") {
339
+ const result = { type: "number" };
340
+ if (def.description) result["description"] = def.description;
341
+ return result;
342
+ }
343
+ if (typeName === "ZodBoolean") {
344
+ const result = { type: "boolean" };
345
+ if (def.description) result["description"] = def.description;
346
+ return result;
347
+ }
348
+ if (typeName === "ZodArray") {
349
+ return {
350
+ type: "array",
351
+ items: zodToJsonSchema(def.type)
352
+ };
353
+ }
354
+ if (typeName === "ZodOptional") {
355
+ return zodToJsonSchema(def.innerType);
356
+ }
357
+ if (typeName === "ZodDefault") {
358
+ const inner = zodToJsonSchema(def.innerType);
359
+ return { ...inner, default: def.defaultValue() };
360
+ }
361
+ return {};
362
+ }
363
+
364
+ // src/tools/registry.ts
365
+ var InlineTool = class extends BaseTool {
366
+ name;
367
+ description;
368
+ schema;
369
+ scopes;
370
+ handler;
371
+ constructor(name, definition) {
372
+ super();
373
+ this.name = name;
374
+ this.description = definition.description;
375
+ this.schema = definition.schema;
376
+ this.scopes = definition.scopes ?? [];
377
+ this.handler = definition.handler;
378
+ }
379
+ async execute(input) {
380
+ return this.handler(input);
381
+ }
382
+ };
383
+ var ToolRegistry = class {
384
+ tools = /* @__PURE__ */ new Map();
385
+ /**
386
+ * Register a tool instance
387
+ */
388
+ register(tool) {
389
+ if (this.tools.has(tool.name)) {
390
+ throw new McpError({
391
+ code: "TOOL_ALREADY_REGISTERED" /* TOOL_ALREADY_REGISTERED */,
392
+ message: `Tool '${tool.name}' is already registered`,
393
+ details: { toolName: tool.name }
394
+ });
395
+ }
396
+ this.tools.set(tool.name, tool);
397
+ }
398
+ /**
399
+ * Register an inline tool definition
400
+ */
401
+ registerInline(name, definition) {
402
+ const tool = new InlineTool(name, definition);
403
+ this.register(tool);
404
+ }
405
+ /**
406
+ * Get a tool by name
407
+ */
408
+ get(name) {
409
+ return this.tools.get(name);
410
+ }
411
+ /**
412
+ * Get a tool by name or throw if not found
413
+ */
414
+ getOrThrow(name) {
415
+ const tool = this.tools.get(name);
416
+ if (!tool) {
417
+ throw McpError.toolNotFound(name);
418
+ }
419
+ return tool;
420
+ }
421
+ /**
422
+ * Check if a tool exists
423
+ */
424
+ has(name) {
425
+ return this.tools.has(name);
426
+ }
427
+ /**
428
+ * Remove a tool
429
+ */
430
+ remove(name) {
431
+ return this.tools.delete(name);
432
+ }
433
+ /**
434
+ * Get all registered tools
435
+ */
436
+ getAll() {
437
+ return Array.from(this.tools.values());
438
+ }
439
+ /**
440
+ * Get all tool names
441
+ */
442
+ getNames() {
443
+ return Array.from(this.tools.keys());
444
+ }
445
+ /**
446
+ * Get the number of registered tools
447
+ */
448
+ get size() {
449
+ return this.tools.size;
450
+ }
451
+ /**
452
+ * Clear all tools
453
+ */
454
+ clear() {
455
+ this.tools.clear();
456
+ }
457
+ };
458
+
459
+ // src/resources/BaseResource.ts
460
+ var BaseResource = class {
461
+ /** Description of the resource */
462
+ description;
463
+ /** MIME type of the resource content */
464
+ mimeType = "text/plain";
465
+ /** Required scopes for this resource (optional) */
466
+ scopes = [];
467
+ /**
468
+ * Safely read the resource with error handling
469
+ */
470
+ async safeRead() {
471
+ const logger2 = getContextLogger();
472
+ try {
473
+ logger2.debug("Reading resource", { uri: this.uri });
474
+ const result = await this.read();
475
+ logger2.debug("Resource read successfully", { uri: this.uri });
476
+ return result;
477
+ } catch (error) {
478
+ if (error instanceof McpError) {
479
+ throw error;
480
+ }
481
+ logger2.error("Resource read failed", error instanceof Error ? error : void 0);
482
+ throw new McpError({
483
+ code: "RESOURCE_READ_ERROR" /* RESOURCE_READ_ERROR */,
484
+ message: `Failed to read resource '${this.uri}': ${error instanceof Error ? error.message : "Unknown error"}`,
485
+ cause: error instanceof Error ? error : void 0,
486
+ details: { uri: this.uri }
487
+ });
488
+ }
489
+ }
490
+ /**
491
+ * Get resource metadata for registration
492
+ */
493
+ getMetadata() {
494
+ return {
495
+ uri: this.uri,
496
+ name: this.name,
497
+ description: this.description,
498
+ mimeType: this.mimeType,
499
+ scopes: this.scopes
500
+ };
501
+ }
502
+ /**
503
+ * Helper to create text content
504
+ */
505
+ text(content) {
506
+ return { type: "text", text: content };
507
+ }
508
+ /**
509
+ * Helper to create JSON content
510
+ */
511
+ json(data) {
512
+ return { type: "text", text: JSON.stringify(data, null, 2) };
513
+ }
514
+ /**
515
+ * Helper to create blob content
516
+ */
517
+ blob(data, mimeType) {
518
+ return { type: "blob", data, mimeType };
519
+ }
520
+ };
521
+
522
+ // src/resources/registry.ts
523
+ var InlineResource = class extends BaseResource {
524
+ uri;
525
+ name;
526
+ description;
527
+ mimeType;
528
+ scopes;
529
+ handler;
530
+ constructor(uri, definition) {
531
+ super();
532
+ this.uri = uri;
533
+ this.name = definition.name;
534
+ this.description = definition.description;
535
+ this.mimeType = definition.mimeType;
536
+ this.scopes = definition.scopes ?? [];
537
+ this.handler = definition.read;
538
+ }
539
+ async read() {
540
+ return this.handler();
541
+ }
542
+ };
543
+ var ResourceRegistry = class {
544
+ resources = /* @__PURE__ */ new Map();
545
+ /**
546
+ * Register a resource instance
547
+ */
548
+ register(resource) {
549
+ if (this.resources.has(resource.uri)) {
550
+ throw new McpError({
551
+ code: "RESOURCE_ALREADY_REGISTERED" /* RESOURCE_ALREADY_REGISTERED */,
552
+ message: `Resource '${resource.uri}' is already registered`,
553
+ details: { uri: resource.uri }
554
+ });
555
+ }
556
+ this.resources.set(resource.uri, resource);
557
+ }
558
+ /**
559
+ * Register an inline resource definition
560
+ */
561
+ registerInline(uri, definition) {
562
+ const resource = new InlineResource(uri, definition);
563
+ this.register(resource);
564
+ }
565
+ /**
566
+ * Get a resource by URI
567
+ */
568
+ get(uri) {
569
+ return this.resources.get(uri);
570
+ }
571
+ /**
572
+ * Get a resource by URI or throw if not found
573
+ */
574
+ getOrThrow(uri) {
575
+ const resource = this.resources.get(uri);
576
+ if (!resource) {
577
+ throw McpError.resourceNotFound(uri);
578
+ }
579
+ return resource;
580
+ }
581
+ /**
582
+ * Check if a resource exists
583
+ */
584
+ has(uri) {
585
+ return this.resources.has(uri);
586
+ }
587
+ /**
588
+ * Remove a resource
589
+ */
590
+ remove(uri) {
591
+ return this.resources.delete(uri);
592
+ }
593
+ /**
594
+ * Get all registered resources
595
+ */
596
+ getAll() {
597
+ return Array.from(this.resources.values());
598
+ }
599
+ /**
600
+ * Get all resource URIs
601
+ */
602
+ getUris() {
603
+ return Array.from(this.resources.keys());
604
+ }
605
+ /**
606
+ * Get the number of registered resources
607
+ */
608
+ get size() {
609
+ return this.resources.size;
610
+ }
611
+ /**
612
+ * Clear all resources
613
+ */
614
+ clear() {
615
+ this.resources.clear();
616
+ }
617
+ };
618
+
619
+ // src/prompts/BasePrompt.ts
620
+ var BasePrompt = class {
621
+ /** Human-readable description */
622
+ description;
623
+ /** Zod schema for arguments validation */
624
+ arguments;
625
+ /** Required scopes for this prompt (optional) */
626
+ scopes = [];
627
+ /**
628
+ * Validate arguments and render the prompt
629
+ */
630
+ async safeRender(args) {
631
+ const logger2 = getContextLogger();
632
+ if (this.arguments) {
633
+ const parseResult = this.arguments.safeParse(args);
634
+ if (!parseResult.success) {
635
+ const errors = parseResult.error.errors.map((e) => ({
636
+ path: e.path.join("."),
637
+ message: e.message
638
+ }));
639
+ logger2.warn("Prompt argument validation failed", {
640
+ prompt: this.name,
641
+ errors
642
+ });
643
+ throw new McpError({
644
+ code: "VALIDATION_ERROR" /* VALIDATION_ERROR */,
645
+ message: `Invalid arguments for prompt '${this.name}'`,
646
+ details: { errors }
647
+ });
648
+ }
649
+ args = parseResult.data;
650
+ }
651
+ try {
652
+ logger2.debug("Rendering prompt", { prompt: this.name });
653
+ const result = await this.render(args);
654
+ logger2.debug("Prompt rendered successfully", { prompt: this.name });
655
+ return result;
656
+ } catch (error) {
657
+ if (error instanceof McpError) {
658
+ throw error;
659
+ }
660
+ logger2.error("Prompt render failed", error instanceof Error ? error : void 0);
661
+ throw new McpError({
662
+ code: "PROMPT_RENDER_ERROR" /* PROMPT_RENDER_ERROR */,
663
+ message: `Failed to render prompt '${this.name}': ${error instanceof Error ? error.message : "Unknown error"}`,
664
+ cause: error instanceof Error ? error : void 0,
665
+ details: { promptName: this.name }
666
+ });
667
+ }
668
+ }
669
+ /**
670
+ * Get prompt metadata for registration
671
+ */
672
+ getMetadata() {
673
+ return {
674
+ name: this.name,
675
+ description: this.description,
676
+ arguments: this.getArgumentsMetadata(),
677
+ scopes: this.scopes
678
+ };
679
+ }
680
+ /**
681
+ * Get arguments metadata from schema
682
+ */
683
+ getArgumentsMetadata() {
684
+ if (!this.arguments) return [];
685
+ const def = this.arguments._def;
686
+ if (def.typeName !== "ZodObject") return [];
687
+ const shape = this.arguments.shape;
688
+ const result = [];
689
+ for (const [name, value] of Object.entries(shape)) {
690
+ const zodValue = value;
691
+ const zodDef = zodValue._def;
692
+ result.push({
693
+ name,
694
+ description: zodDef.description,
695
+ required: !zodValue.isOptional()
696
+ });
697
+ }
698
+ return result;
699
+ }
700
+ /**
701
+ * Helper to create a user message
702
+ */
703
+ user(content) {
704
+ return { role: "user", content };
705
+ }
706
+ /**
707
+ * Helper to create an assistant message
708
+ */
709
+ assistant(content) {
710
+ return { role: "assistant", content };
711
+ }
712
+ };
713
+
714
+ // src/prompts/registry.ts
715
+ var InlinePrompt = class extends BasePrompt {
716
+ name;
717
+ description;
718
+ arguments;
719
+ scopes;
720
+ handler;
721
+ constructor(name, definition) {
722
+ super();
723
+ this.name = name;
724
+ this.description = definition.description;
725
+ this.arguments = definition.arguments;
726
+ this.scopes = definition.scopes ?? [];
727
+ this.handler = definition.render;
728
+ }
729
+ async render(args) {
730
+ return this.handler(args);
731
+ }
732
+ };
733
+ var PromptRegistry = class {
734
+ prompts = /* @__PURE__ */ new Map();
735
+ /**
736
+ * Register a prompt instance
737
+ */
738
+ register(prompt) {
739
+ if (this.prompts.has(prompt.name)) {
740
+ throw new McpError({
741
+ code: "PROMPT_ALREADY_REGISTERED" /* PROMPT_ALREADY_REGISTERED */,
742
+ message: `Prompt '${prompt.name}' is already registered`,
743
+ details: { promptName: prompt.name }
744
+ });
745
+ }
746
+ this.prompts.set(prompt.name, prompt);
747
+ }
748
+ /**
749
+ * Register an inline prompt definition
750
+ */
751
+ registerInline(name, definition) {
752
+ const prompt = new InlinePrompt(name, definition);
753
+ this.register(prompt);
754
+ }
755
+ /**
756
+ * Get a prompt by name
757
+ */
758
+ get(name) {
759
+ return this.prompts.get(name);
760
+ }
761
+ /**
762
+ * Get a prompt by name or throw if not found
763
+ */
764
+ getOrThrow(name) {
765
+ const prompt = this.prompts.get(name);
766
+ if (!prompt) {
767
+ throw McpError.promptNotFound(name);
768
+ }
769
+ return prompt;
770
+ }
771
+ /**
772
+ * Check if a prompt exists
773
+ */
774
+ has(name) {
775
+ return this.prompts.has(name);
776
+ }
777
+ /**
778
+ * Remove a prompt
779
+ */
780
+ remove(name) {
781
+ return this.prompts.delete(name);
782
+ }
783
+ /**
784
+ * Get all registered prompts
785
+ */
786
+ getAll() {
787
+ return Array.from(this.prompts.values());
788
+ }
789
+ /**
790
+ * Get all prompt names
791
+ */
792
+ getNames() {
793
+ return Array.from(this.prompts.keys());
794
+ }
795
+ /**
796
+ * Get the number of registered prompts
797
+ */
798
+ get size() {
799
+ return this.prompts.size;
800
+ }
801
+ /**
802
+ * Clear all prompts
803
+ */
804
+ clear() {
805
+ this.prompts.clear();
806
+ }
807
+ };
808
+
809
+ // src/testing/TestClient.ts
810
+ var MockTool = class extends BaseTool {
811
+ name;
812
+ description;
813
+ schema = z.object({}).passthrough();
814
+ response;
815
+ error;
816
+ customHandler;
817
+ constructor(config) {
818
+ super();
819
+ this.name = config.name;
820
+ this.description = config.description ?? `Mock tool: ${config.name}`;
821
+ this.response = config.response;
822
+ this.error = config.error;
823
+ this.customHandler = config.handler;
824
+ }
825
+ async execute(input) {
826
+ if (this.error) {
827
+ throw this.error;
828
+ }
829
+ if (this.customHandler) {
830
+ const result = await this.customHandler(input);
831
+ return { content: result };
832
+ }
833
+ return { content: this.response ?? { success: true } };
834
+ }
835
+ };
836
+ var MockResource = class extends BaseResource {
837
+ uri;
838
+ name;
839
+ content;
840
+ error;
841
+ customHandler;
842
+ constructor(config) {
843
+ super();
844
+ this.uri = config.uri;
845
+ this.name = config.name ?? `Mock resource: ${config.uri}`;
846
+ this.content = config.content;
847
+ this.error = config.error;
848
+ this.customHandler = config.handler;
849
+ }
850
+ async read() {
851
+ if (this.error) {
852
+ throw this.error;
853
+ }
854
+ if (this.customHandler) {
855
+ const result = await this.customHandler();
856
+ const text2 = typeof result === "string" ? result : JSON.stringify(result);
857
+ return { contents: [{ type: "text", text: text2 }] };
858
+ }
859
+ const text = typeof this.content === "string" ? this.content : JSON.stringify(this.content ?? {});
860
+ return { contents: [{ type: "text", text }] };
861
+ }
862
+ };
863
+ var MockPrompt = class extends BasePrompt {
864
+ name;
865
+ description;
866
+ arguments = z.object({}).passthrough();
867
+ messages;
868
+ error;
869
+ customHandler;
870
+ constructor(config) {
871
+ super();
872
+ this.name = config.name;
873
+ this.description = config.description;
874
+ this.messages = config.messages;
875
+ this.error = config.error;
876
+ this.customHandler = config.handler;
877
+ }
878
+ async render(args) {
879
+ if (this.error) {
880
+ throw this.error;
881
+ }
882
+ if (this.customHandler) {
883
+ return this.customHandler(args);
884
+ }
885
+ return {
886
+ messages: this.messages ?? [{ role: "user", content: "Mock prompt" }]
887
+ };
888
+ }
889
+ };
890
+ var TestClient = class _TestClient {
891
+ toolRegistry = new ToolRegistry();
892
+ resourceRegistry = new ResourceRegistry();
893
+ promptRegistry = new PromptRegistry();
894
+ config;
895
+ logger;
896
+ constructor(config = {}) {
897
+ this.config = config;
898
+ this.logger = new Logger({
899
+ level: config.debug ? "debug" : "error",
900
+ pretty: true
901
+ });
902
+ }
903
+ /**
904
+ * Create a test client from an existing McpServer
905
+ */
906
+ static fromServer(server, config) {
907
+ const client = new _TestClient(config);
908
+ for (const tool of server.getToolRegistry().getAll()) {
909
+ client.toolRegistry.register(tool);
910
+ }
911
+ for (const resource of server.getResourceRegistry().getAll()) {
912
+ client.resourceRegistry.register(resource);
913
+ }
914
+ for (const prompt of server.getPromptRegistry().getAll()) {
915
+ client.promptRegistry.register(prompt);
916
+ }
917
+ return client;
918
+ }
919
+ /**
920
+ * Register a tool for testing
921
+ */
922
+ registerTool(tool) {
923
+ this.toolRegistry.register(tool);
924
+ return this;
925
+ }
926
+ /**
927
+ * Register a resource for testing
928
+ */
929
+ registerResource(resource) {
930
+ this.resourceRegistry.register(resource);
931
+ return this;
932
+ }
933
+ /**
934
+ * Register a prompt for testing
935
+ */
936
+ registerPrompt(prompt) {
937
+ this.promptRegistry.register(prompt);
938
+ return this;
939
+ }
940
+ /**
941
+ * Register a mock tool
942
+ */
943
+ mockTool(config) {
944
+ this.toolRegistry.register(new MockTool(config));
945
+ return this;
946
+ }
947
+ /**
948
+ * Register a mock resource
949
+ */
950
+ mockResource(config) {
951
+ this.resourceRegistry.register(new MockResource(config));
952
+ return this;
953
+ }
954
+ /**
955
+ * Register a mock prompt
956
+ */
957
+ mockPrompt(config) {
958
+ this.promptRegistry.register(new MockPrompt(config));
959
+ return this;
960
+ }
961
+ /**
962
+ * Call a tool
963
+ */
964
+ async callTool(name, input) {
965
+ return runInRequestContextAsync(async () => {
966
+ if (this.config.auth) {
967
+ setAuthContext(this.config.auth);
968
+ }
969
+ const tool = this.toolRegistry.getOrThrow(name);
970
+ return tool.run(input ?? {});
971
+ }, createRequestContext(void 0, this.logger));
972
+ }
973
+ /**
974
+ * Read a resource
975
+ */
976
+ async readResource(uri) {
977
+ return runInRequestContextAsync(async () => {
978
+ if (this.config.auth) {
979
+ setAuthContext(this.config.auth);
980
+ }
981
+ const resource = this.resourceRegistry.getOrThrow(uri);
982
+ return resource.safeRead();
983
+ }, createRequestContext(void 0, this.logger));
984
+ }
985
+ /**
986
+ * Render a prompt
987
+ */
988
+ async renderPrompt(name, args) {
989
+ return runInRequestContextAsync(async () => {
990
+ if (this.config.auth) {
991
+ setAuthContext(this.config.auth);
992
+ }
993
+ const prompt = this.promptRegistry.getOrThrow(name);
994
+ return prompt.safeRender(args ?? {});
995
+ }, createRequestContext(void 0, this.logger));
996
+ }
997
+ /**
998
+ * List all registered tools
999
+ */
1000
+ listTools() {
1001
+ return this.toolRegistry.getNames();
1002
+ }
1003
+ /**
1004
+ * List all registered resources
1005
+ */
1006
+ listResources() {
1007
+ return this.resourceRegistry.getUris();
1008
+ }
1009
+ /**
1010
+ * List all registered prompts
1011
+ */
1012
+ listPrompts() {
1013
+ return this.promptRegistry.getNames();
1014
+ }
1015
+ /**
1016
+ * Check if a tool exists
1017
+ */
1018
+ hasTool(name) {
1019
+ return this.toolRegistry.has(name);
1020
+ }
1021
+ /**
1022
+ * Check if a resource exists
1023
+ */
1024
+ hasResource(uri) {
1025
+ return this.resourceRegistry.has(uri);
1026
+ }
1027
+ /**
1028
+ * Check if a prompt exists
1029
+ */
1030
+ hasPrompt(name) {
1031
+ return this.promptRegistry.has(name);
1032
+ }
1033
+ /**
1034
+ * Clear all registered items
1035
+ */
1036
+ clear() {
1037
+ this.toolRegistry.clear();
1038
+ this.resourceRegistry.clear();
1039
+ this.promptRegistry.clear();
1040
+ return this;
1041
+ }
1042
+ };
1043
+ function createTestClient(config) {
1044
+ return new TestClient(config);
1045
+ }
1046
+
1047
+ export { TestClient, createTestClient };
1048
+ //# sourceMappingURL=index.js.map
1049
+ //# sourceMappingURL=index.js.map