@leanmcp/core 0.1.2 → 0.3.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/dist/index.js CHANGED
@@ -6,6 +6,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __export = (target, all) => {
10
13
  for (var name in all)
11
14
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -28,48 +31,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
31
  ));
29
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
33
 
31
- // src/index.ts
32
- var index_exports = {};
33
- __export(index_exports, {
34
- Auth: () => Auth,
35
- Deprecated: () => Deprecated,
36
- LogLevel: () => LogLevel,
37
- Logger: () => Logger,
38
- MCPServer: () => MCPServer,
39
- MCPServerRuntime: () => MCPServerRuntime,
40
- Optional: () => Optional,
41
- Prompt: () => Prompt,
42
- Render: () => Render,
43
- Resource: () => Resource,
44
- SchemaConstraint: () => SchemaConstraint,
45
- Tool: () => Tool,
46
- UI: () => UI,
47
- UserEnvs: () => UserEnvs,
48
- classToJsonSchema: () => classToJsonSchema,
49
- classToJsonSchemaWithConstraints: () => classToJsonSchemaWithConstraints,
50
- createHTTPServer: () => createHTTPServer,
51
- defaultLogger: () => defaultLogger,
52
- getDecoratedMethods: () => getDecoratedMethods,
53
- getMethodMetadata: () => getMethodMetadata,
54
- startMCPServer: () => startMCPServer,
55
- validateNonEmpty: () => validateNonEmpty,
56
- validatePath: () => validatePath,
57
- validatePort: () => validatePort,
58
- validateServiceName: () => validateServiceName,
59
- validateUrl: () => validateUrl
60
- });
61
- module.exports = __toCommonJS(index_exports);
62
- var import_reflect_metadata3 = require("reflect-metadata");
63
- var import_fs = __toESM(require("fs"));
64
- var import_path = __toESM(require("path"));
65
- var import_url = require("url");
66
- var import_server = require("@modelcontextprotocol/sdk/server/index.js");
67
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
68
- var import_types = require("@modelcontextprotocol/sdk/types.js");
69
- var import_ajv = __toESM(require("ajv"));
70
-
71
34
  // src/decorators.ts
72
- var import_reflect_metadata = require("reflect-metadata");
73
35
  function Tool(options = {}) {
74
36
  return (target, propertyKey, descriptor) => {
75
37
  const toolName = String(propertyKey);
@@ -81,7 +43,6 @@ function Tool(options = {}) {
81
43
  }
82
44
  };
83
45
  }
84
- __name(Tool, "Tool");
85
46
  function Prompt(options = {}) {
86
47
  return (target, propertyKey, descriptor) => {
87
48
  const promptName = String(propertyKey);
@@ -98,7 +59,6 @@ function Prompt(options = {}) {
98
59
  }
99
60
  };
100
61
  }
101
- __name(Prompt, "Prompt");
102
62
  function Resource(options = {}) {
103
63
  return (target, propertyKey, descriptor) => {
104
64
  const resourceName = String(propertyKey);
@@ -114,7 +74,6 @@ function Resource(options = {}) {
114
74
  }
115
75
  };
116
76
  }
117
- __name(Resource, "Resource");
118
77
  function Auth(options) {
119
78
  return (target, propertyKey, descriptor) => {
120
79
  if (propertyKey && descriptor) {
@@ -126,14 +85,12 @@ function Auth(options) {
126
85
  }
127
86
  };
128
87
  }
129
- __name(Auth, "Auth");
130
88
  function UserEnvs() {
131
89
  return (target, propertyKey) => {
132
90
  const constructor = target.constructor;
133
91
  Reflect.defineMetadata("userenvs:propertyKey", propertyKey, constructor);
134
92
  };
135
93
  }
136
- __name(UserEnvs, "UserEnvs");
137
94
  function UI(component) {
138
95
  return (target, propertyKey, descriptor) => {
139
96
  if (propertyKey && descriptor) {
@@ -143,13 +100,11 @@ function UI(component) {
143
100
  }
144
101
  };
145
102
  }
146
- __name(UI, "UI");
147
103
  function Render(format) {
148
104
  return (target, propertyKey, descriptor) => {
149
105
  Reflect.defineMetadata("render:format", format, descriptor.value);
150
106
  };
151
107
  }
152
- __name(Render, "Render");
153
108
  function Deprecated(message) {
154
109
  return (target, propertyKey, descriptor) => {
155
110
  const deprecationMessage = message || "This feature is deprecated";
@@ -168,7 +123,6 @@ function Deprecated(message) {
168
123
  }
169
124
  };
170
125
  }
171
- __name(Deprecated, "Deprecated");
172
126
  function getMethodMetadata(method) {
173
127
  return {
174
128
  // Tool metadata
@@ -192,7 +146,6 @@ function getMethodMetadata(method) {
192
146
  deprecationMessage: Reflect.getMetadata("deprecated:message", method)
193
147
  };
194
148
  }
195
- __name(getMethodMetadata, "getMethodMetadata");
196
149
  function getDecoratedMethods(target, metadataKey) {
197
150
  const methods = [];
198
151
  const prototype = target.prototype || target;
@@ -211,10 +164,25 @@ function getDecoratedMethods(target, metadataKey) {
211
164
  }
212
165
  return methods;
213
166
  }
214
- __name(getDecoratedMethods, "getDecoratedMethods");
167
+ var import_reflect_metadata;
168
+ var init_decorators = __esm({
169
+ "src/decorators.ts"() {
170
+ "use strict";
171
+ import_reflect_metadata = require("reflect-metadata");
172
+ __name(Tool, "Tool");
173
+ __name(Prompt, "Prompt");
174
+ __name(Resource, "Resource");
175
+ __name(Auth, "Auth");
176
+ __name(UserEnvs, "UserEnvs");
177
+ __name(UI, "UI");
178
+ __name(Render, "Render");
179
+ __name(Deprecated, "Deprecated");
180
+ __name(getMethodMetadata, "getMethodMetadata");
181
+ __name(getDecoratedMethods, "getDecoratedMethods");
182
+ }
183
+ });
215
184
 
216
185
  // src/schema-generator.ts
217
- var import_reflect_metadata2 = require("reflect-metadata");
218
186
  function classToJsonSchema(classConstructor) {
219
187
  const instance = new classConstructor();
220
188
  const properties = {};
@@ -261,19 +229,16 @@ function classToJsonSchema(classConstructor) {
261
229
  required: required.length > 0 ? required : void 0
262
230
  };
263
231
  }
264
- __name(classToJsonSchema, "classToJsonSchema");
265
232
  function Optional() {
266
233
  return (target, propertyKey) => {
267
234
  Reflect.defineMetadata("optional", true, target, propertyKey);
268
235
  };
269
236
  }
270
- __name(Optional, "Optional");
271
237
  function SchemaConstraint(constraints) {
272
238
  return (target, propertyKey) => {
273
239
  Reflect.defineMetadata("schema:constraints", constraints, target, propertyKey);
274
240
  };
275
241
  }
276
- __name(SchemaConstraint, "SchemaConstraint");
277
242
  function classToJsonSchemaWithConstraints(classConstructor) {
278
243
  const instance = new classConstructor();
279
244
  const properties = {};
@@ -334,70 +299,83 @@ function classToJsonSchemaWithConstraints(classConstructor) {
334
299
  required: required.length > 0 ? required : void 0
335
300
  };
336
301
  }
337
- __name(classToJsonSchemaWithConstraints, "classToJsonSchemaWithConstraints");
338
-
339
- // src/http-server.ts
340
- var import_node_crypto = require("crypto");
302
+ var import_reflect_metadata2;
303
+ var init_schema_generator = __esm({
304
+ "src/schema-generator.ts"() {
305
+ "use strict";
306
+ import_reflect_metadata2 = require("reflect-metadata");
307
+ __name(classToJsonSchema, "classToJsonSchema");
308
+ __name(Optional, "Optional");
309
+ __name(SchemaConstraint, "SchemaConstraint");
310
+ __name(classToJsonSchemaWithConstraints, "classToJsonSchemaWithConstraints");
311
+ }
312
+ });
341
313
 
342
314
  // src/logger.ts
343
- var LogLevel = /* @__PURE__ */ (function(LogLevel2) {
344
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
345
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
346
- LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
347
- LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
348
- LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
349
- return LogLevel2;
350
- })({});
351
- var Logger = class {
352
- static {
353
- __name(this, "Logger");
354
- }
355
- level;
356
- prefix;
357
- timestamps;
358
- constructor(options = {}) {
359
- this.level = options.level ?? 1;
360
- this.prefix = options.prefix ?? "";
361
- this.timestamps = options.timestamps ?? true;
362
- }
363
- format(level, message, ...args) {
364
- const timestamp = this.timestamps ? `[${(/* @__PURE__ */ new Date()).toISOString()}]` : "";
365
- const prefix = this.prefix ? `[${this.prefix}]` : "";
366
- return `${timestamp}${prefix}[${level}] ${message}`;
367
- }
368
- shouldLog(level) {
369
- return level >= this.level;
370
- }
371
- debug(message, ...args) {
372
- if (this.shouldLog(0)) {
373
- console.debug(this.format("DEBUG", message), ...args);
374
- }
375
- }
376
- info(message, ...args) {
377
- if (this.shouldLog(1)) {
378
- console.info(this.format("INFO", message), ...args);
379
- }
380
- }
381
- warn(message, ...args) {
382
- if (this.shouldLog(2)) {
383
- console.warn(this.format("WARN", message), ...args);
384
- }
385
- }
386
- error(message, ...args) {
387
- if (this.shouldLog(3)) {
388
- console.error(this.format("ERROR", message), ...args);
389
- }
390
- }
391
- setLevel(level) {
392
- this.level = level;
393
- }
394
- getLevel() {
395
- return this.level;
315
+ var LogLevel, Logger, defaultLogger;
316
+ var init_logger = __esm({
317
+ "src/logger.ts"() {
318
+ "use strict";
319
+ LogLevel = /* @__PURE__ */ (function(LogLevel2) {
320
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
321
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
322
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
323
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
324
+ LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
325
+ return LogLevel2;
326
+ })({});
327
+ Logger = class {
328
+ static {
329
+ __name(this, "Logger");
330
+ }
331
+ level;
332
+ prefix;
333
+ timestamps;
334
+ constructor(options = {}) {
335
+ this.level = options.level ?? 1;
336
+ this.prefix = options.prefix ?? "";
337
+ this.timestamps = options.timestamps ?? true;
338
+ }
339
+ format(level, message, ...args) {
340
+ const timestamp = this.timestamps ? `[${(/* @__PURE__ */ new Date()).toISOString()}]` : "";
341
+ const prefix = this.prefix ? `[${this.prefix}]` : "";
342
+ return `${timestamp}${prefix}[${level}] ${message}`;
343
+ }
344
+ shouldLog(level) {
345
+ return level >= this.level;
346
+ }
347
+ debug(message, ...args) {
348
+ if (this.shouldLog(0)) {
349
+ console.debug(this.format("DEBUG", message), ...args);
350
+ }
351
+ }
352
+ info(message, ...args) {
353
+ if (this.shouldLog(1)) {
354
+ console.info(this.format("INFO", message), ...args);
355
+ }
356
+ }
357
+ warn(message, ...args) {
358
+ if (this.shouldLog(2)) {
359
+ console.warn(this.format("WARN", message), ...args);
360
+ }
361
+ }
362
+ error(message, ...args) {
363
+ if (this.shouldLog(3)) {
364
+ console.error(this.format("ERROR", message), ...args);
365
+ }
366
+ }
367
+ setLevel(level) {
368
+ this.level = level;
369
+ }
370
+ getLevel() {
371
+ return this.level;
372
+ }
373
+ };
374
+ defaultLogger = new Logger({
375
+ level: 1,
376
+ prefix: "LeanMCP"
377
+ });
396
378
  }
397
- };
398
- var defaultLogger = new Logger({
399
- level: 1,
400
- prefix: "LeanMCP"
401
379
  });
402
380
 
403
381
  // src/validation.ts
@@ -406,26 +384,22 @@ function validatePort(port) {
406
384
  throw new Error(`Invalid port: ${port}. Must be an integer between 1-65535`);
407
385
  }
408
386
  }
409
- __name(validatePort, "validatePort");
410
387
  function validatePath(path2) {
411
388
  if (path2.includes("..") || path2.includes("~")) {
412
389
  throw new Error(`Invalid path: ${path2}. Path traversal patterns are not allowed`);
413
390
  }
414
391
  }
415
- __name(validatePath, "validatePath");
416
392
  function validateServiceName(name) {
417
393
  const validNamePattern = /^[a-zA-Z0-9_-]+$/;
418
394
  if (!validNamePattern.test(name)) {
419
395
  throw new Error(`Invalid service name: ${name}. Service names must contain only alphanumeric characters, hyphens, and underscores`);
420
396
  }
421
397
  }
422
- __name(validateServiceName, "validateServiceName");
423
398
  function validateNonEmpty(value, fieldName) {
424
399
  if (!value || value.trim().length === 0) {
425
400
  throw new Error(`${fieldName} cannot be empty`);
426
401
  }
427
402
  }
428
- __name(validateNonEmpty, "validateNonEmpty");
429
403
  function validateUrl(url, allowedProtocols = [
430
404
  "http:",
431
405
  "https:"
@@ -442,14 +416,41 @@ function validateUrl(url, allowedProtocols = [
442
416
  throw error;
443
417
  }
444
418
  }
445
- __name(validateUrl, "validateUrl");
419
+ var init_validation = __esm({
420
+ "src/validation.ts"() {
421
+ "use strict";
422
+ __name(validatePort, "validatePort");
423
+ __name(validatePath, "validatePath");
424
+ __name(validateServiceName, "validateServiceName");
425
+ __name(validateNonEmpty, "validateNonEmpty");
426
+ __name(validateUrl, "validateUrl");
427
+ }
428
+ });
446
429
 
447
430
  // src/http-server.ts
448
431
  function isInitializeRequest(body) {
449
432
  return body && body.method === "initialize";
450
433
  }
451
- __name(isInitializeRequest, "isInitializeRequest");
452
- async function createHTTPServer(serverFactory, options = {}) {
434
+ async function createHTTPServer(serverInput, options) {
435
+ let serverFactory;
436
+ let httpOptions;
437
+ if (typeof serverInput === "function") {
438
+ serverFactory = serverInput;
439
+ httpOptions = options || {};
440
+ } else {
441
+ const serverOptions = serverInput;
442
+ const { MCPServer: MCPServer2 } = await Promise.resolve().then(() => (init_index(), index_exports));
443
+ serverFactory = /* @__PURE__ */ __name(async () => {
444
+ const mcpServer2 = new MCPServer2(serverOptions);
445
+ return mcpServer2.getServer();
446
+ }, "serverFactory");
447
+ httpOptions = {
448
+ port: serverOptions.port,
449
+ cors: serverOptions.cors,
450
+ logging: serverOptions.logging,
451
+ sessionTimeout: serverOptions.sessionTimeout
452
+ };
453
+ }
453
454
  const [express, { StreamableHTTPServerTransport }, cors] = await Promise.all([
454
455
  // @ts-ignore
455
456
  import("express").catch(() => {
@@ -460,19 +461,20 @@ async function createHTTPServer(serverFactory, options = {}) {
460
461
  throw new Error("MCP SDK not found. Install with: npm install @modelcontextprotocol/sdk");
461
462
  }),
462
463
  // @ts-ignore
463
- options.cors ? import("cors").catch(() => null) : Promise.resolve(null)
464
+ httpOptions.cors ? import("cors").catch(() => null) : Promise.resolve(null)
464
465
  ]);
465
466
  const app = express.default();
466
- const port = options.port || 3001;
467
+ const port = httpOptions.port || 3001;
467
468
  validatePort(port);
468
469
  const transports = {};
469
- const logger = options.logger || new Logger({
470
- level: options.logging ? LogLevel.INFO : LogLevel.NONE,
470
+ let mcpServer = null;
471
+ const logger = httpOptions.logger || new Logger({
472
+ level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
471
473
  prefix: "HTTP"
472
474
  });
473
- if (cors && options.cors) {
474
- const corsOptions = typeof options.cors === "object" ? {
475
- origin: options.cors.origin || false,
475
+ if (cors && httpOptions.cors) {
476
+ const corsOptions = typeof httpOptions.cors === "object" ? {
477
+ origin: httpOptions.cors.origin || false,
476
478
  methods: [
477
479
  "GET",
478
480
  "POST",
@@ -488,7 +490,7 @@ async function createHTTPServer(serverFactory, options = {}) {
488
490
  exposedHeaders: [
489
491
  "mcp-session-id"
490
492
  ],
491
- credentials: options.cors.credentials ?? false,
493
+ credentials: httpOptions.cors.credentials ?? false,
492
494
  maxAge: 86400
493
495
  } : false;
494
496
  if (corsOptions) {
@@ -507,6 +509,18 @@ async function createHTTPServer(serverFactory, options = {}) {
507
509
  const handleMCPRequest = /* @__PURE__ */ __name(async (req, res) => {
508
510
  const sessionId = req.headers["mcp-session-id"];
509
511
  let transport;
512
+ const method = req.body?.method || "unknown";
513
+ const params = req.body?.params;
514
+ let logMessage = `${req.method} /mcp - ${method}`;
515
+ if (params?.name) {
516
+ logMessage += ` [${params.name}]`;
517
+ } else if (params?.uri) {
518
+ logMessage += ` [${params.uri}]`;
519
+ }
520
+ if (sessionId) {
521
+ logMessage += ` (session: ${sessionId.substring(0, 8)}...)`;
522
+ }
523
+ logger.info(logMessage);
510
524
  try {
511
525
  if (sessionId && transports[sessionId]) {
512
526
  transport = transports[sessionId];
@@ -526,8 +540,10 @@ async function createHTTPServer(serverFactory, options = {}) {
526
540
  logger.debug(`Session cleaned up: ${transport.sessionId}`);
527
541
  }
528
542
  };
529
- const server = await serverFactory();
530
- await server.connect(transport);
543
+ if (!mcpServer) {
544
+ throw new Error("MCP server not initialized");
545
+ }
546
+ await mcpServer.connect(transport);
531
547
  } else {
532
548
  res.status(400).json({
533
549
  jsonrpc: "2.0",
@@ -556,583 +572,833 @@ async function createHTTPServer(serverFactory, options = {}) {
556
572
  }, "handleMCPRequest");
557
573
  app.post("/mcp", handleMCPRequest);
558
574
  app.delete("/mcp", handleMCPRequest);
559
- process.on("SIGINT", () => {
560
- logger.info("\nShutting down server...");
561
- Object.values(transports).forEach((t) => t.close?.());
562
- process.exit(0);
563
- });
564
- return new Promise((resolve) => {
565
- app.listen(port, () => {
566
- logger.info(`Server running on http://localhost:${port}`);
567
- logger.info(`MCP endpoint: http://localhost:${port}/mcp`);
568
- logger.info(`Health check: http://localhost:${port}/health`);
569
- resolve();
570
- });
575
+ return new Promise(async (resolve, reject) => {
576
+ try {
577
+ mcpServer = await serverFactory();
578
+ if (mcpServer && typeof mcpServer.waitForInit === "function") {
579
+ await mcpServer.waitForInit();
580
+ }
581
+ const listener = app.listen(port, () => {
582
+ logger.info(`Server running on http://localhost:${port}`);
583
+ logger.info(`MCP endpoint: http://localhost:${port}/mcp`);
584
+ logger.info(`Health check: http://localhost:${port}/health`);
585
+ resolve(listener);
586
+ });
587
+ listener.on("error", (error) => {
588
+ logger.error(`Server error: ${error.message}`);
589
+ reject(error);
590
+ });
591
+ const cleanup = /* @__PURE__ */ __name(() => {
592
+ logger.info("\nShutting down server...");
593
+ Object.values(transports).forEach((t) => t.close?.());
594
+ listener.close(() => {
595
+ logger.info("Server closed");
596
+ process.exit(0);
597
+ });
598
+ setTimeout(() => {
599
+ logger.warn("Forcing shutdown...");
600
+ process.exit(1);
601
+ }, 5e3);
602
+ }, "cleanup");
603
+ process.on("SIGINT", cleanup);
604
+ process.on("SIGTERM", cleanup);
605
+ } catch (error) {
606
+ reject(error);
607
+ }
571
608
  });
572
609
  }
573
- __name(createHTTPServer, "createHTTPServer");
610
+ var import_node_crypto;
611
+ var init_http_server = __esm({
612
+ "src/http-server.ts"() {
613
+ "use strict";
614
+ import_node_crypto = require("crypto");
615
+ init_logger();
616
+ init_validation();
617
+ __name(isInitializeRequest, "isInitializeRequest");
618
+ __name(createHTTPServer, "createHTTPServer");
619
+ }
620
+ });
574
621
 
575
622
  // src/index.ts
576
- var ajv = new import_ajv.default();
577
- var MCPServer = class {
578
- static {
579
- __name(this, "MCPServer");
580
- }
581
- server;
582
- tools = /* @__PURE__ */ new Map();
583
- prompts = /* @__PURE__ */ new Map();
584
- resources = /* @__PURE__ */ new Map();
585
- logging;
586
- logger;
587
- constructor(options) {
588
- this.logging = options.logging || false;
589
- this.logger = new Logger({
590
- level: this.logging ? LogLevel.INFO : LogLevel.NONE,
591
- prefix: "MCPServer"
592
- });
593
- this.server = new import_server.Server({
594
- name: options.name,
595
- version: options.version
596
- }, {
597
- capabilities: {
598
- tools: {},
599
- resources: {},
600
- prompts: {}
623
+ var index_exports = {};
624
+ __export(index_exports, {
625
+ Auth: () => Auth,
626
+ Deprecated: () => Deprecated,
627
+ LogLevel: () => LogLevel,
628
+ Logger: () => Logger,
629
+ MCPServer: () => MCPServer,
630
+ MCPServerRuntime: () => MCPServerRuntime,
631
+ Optional: () => Optional,
632
+ Prompt: () => Prompt,
633
+ Render: () => Render,
634
+ Resource: () => Resource,
635
+ SchemaConstraint: () => SchemaConstraint,
636
+ Tool: () => Tool,
637
+ UI: () => UI,
638
+ UserEnvs: () => UserEnvs,
639
+ classToJsonSchema: () => classToJsonSchema,
640
+ classToJsonSchemaWithConstraints: () => classToJsonSchemaWithConstraints,
641
+ createHTTPServer: () => createHTTPServer,
642
+ defaultLogger: () => defaultLogger,
643
+ getDecoratedMethods: () => getDecoratedMethods,
644
+ getMethodMetadata: () => getMethodMetadata,
645
+ startMCPServer: () => startMCPServer,
646
+ validateNonEmpty: () => validateNonEmpty,
647
+ validatePath: () => validatePath,
648
+ validatePort: () => validatePort,
649
+ validateServiceName: () => validateServiceName,
650
+ validateUrl: () => validateUrl
651
+ });
652
+ module.exports = __toCommonJS(index_exports);
653
+ async function startMCPServer(options) {
654
+ const runtime = new MCPServerRuntime(options);
655
+ await runtime.start();
656
+ return runtime;
657
+ }
658
+ var import_reflect_metadata3, import_fs, import_path, import_url, import_server, import_stdio, import_types, import_ajv, ajv, MCPServer, MCPServerRuntime;
659
+ var init_index = __esm({
660
+ "src/index.ts"() {
661
+ import_reflect_metadata3 = require("reflect-metadata");
662
+ import_fs = __toESM(require("fs"));
663
+ import_path = __toESM(require("path"));
664
+ import_url = require("url");
665
+ import_server = require("@modelcontextprotocol/sdk/server/index.js");
666
+ import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
667
+ import_types = require("@modelcontextprotocol/sdk/types.js");
668
+ import_ajv = __toESM(require("ajv"));
669
+ init_decorators();
670
+ init_schema_generator();
671
+ init_http_server();
672
+ init_logger();
673
+ init_validation();
674
+ init_decorators();
675
+ init_schema_generator();
676
+ init_logger();
677
+ ajv = new import_ajv.default();
678
+ MCPServer = class {
679
+ static {
680
+ __name(this, "MCPServer");
601
681
  }
602
- });
603
- this.setupHandlers();
604
- }
605
- setupHandlers() {
606
- this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
607
- const tools = [];
608
- for (const [name, tool] of this.tools.entries()) {
609
- tools.push({
610
- name,
611
- description: tool.description,
612
- inputSchema: tool.inputSchema || {
613
- type: "object",
614
- properties: {}
682
+ server;
683
+ tools = /* @__PURE__ */ new Map();
684
+ prompts = /* @__PURE__ */ new Map();
685
+ resources = /* @__PURE__ */ new Map();
686
+ logging;
687
+ logger;
688
+ options;
689
+ initPromise;
690
+ autoDiscovered = false;
691
+ constructor(options) {
692
+ this.options = options;
693
+ this.logging = options.logging || false;
694
+ let logLevel = LogLevel.NONE;
695
+ if (options.logging) {
696
+ logLevel = options.debug ? LogLevel.DEBUG : LogLevel.INFO;
697
+ }
698
+ this.logger = new Logger({
699
+ level: logLevel,
700
+ prefix: "MCPServer"
701
+ });
702
+ this.server = new import_server.Server({
703
+ name: options.name,
704
+ version: options.version
705
+ }, {
706
+ capabilities: {
707
+ tools: {},
708
+ prompts: {},
709
+ resources: {}
615
710
  }
616
711
  });
712
+ this.setupHandlers();
713
+ this.initPromise = this.autoInit();
617
714
  }
618
- return {
619
- tools
620
- };
621
- });
622
- this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
623
- const toolName = request.params.name;
624
- const tool = this.tools.get(toolName);
625
- if (!tool) {
626
- throw new Error(`Tool ${toolName} not found`);
627
- }
628
- const methodMeta = getMethodMetadata(tool.method);
629
- if (methodMeta.inputSchema) {
630
- const validate = ajv.compile(methodMeta.inputSchema);
631
- const valid = validate(request.params.arguments || {});
632
- if (!valid) {
633
- throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
715
+ /**
716
+ * Internal initialization - runs automatically in constructor
717
+ */
718
+ async autoInit() {
719
+ const options = this.options;
720
+ if (options.autoDiscover !== false) {
721
+ await this.autoDiscoverServices(options.mcpDir, options.serviceFactories);
634
722
  }
635
723
  }
636
- try {
637
- const meta = request.params._meta;
638
- const result = await tool.method.call(tool.instance, request.params.arguments, meta);
639
- let formattedResult = result;
640
- if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
641
- formattedResult = result;
642
- } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
643
- formattedResult = JSON.stringify(result, null, 2);
644
- } else {
645
- formattedResult = String(result);
646
- }
647
- return {
648
- content: [
649
- {
650
- type: "text",
651
- text: formattedResult
652
- }
653
- ]
654
- };
655
- } catch (error) {
656
- return {
657
- content: [
658
- {
659
- type: "text",
660
- text: `Error: ${error.message}`
661
- }
662
- ],
663
- isError: true
664
- };
724
+ /**
725
+ * Wait for initialization to complete
726
+ * This is called internally by createHTTPServer
727
+ */
728
+ async waitForInit() {
729
+ await this.initPromise;
665
730
  }
666
- });
667
- this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
668
- const resources = [];
669
- for (const [uri, resource] of this.resources.entries()) {
670
- const resourceInfo = {
671
- uri: resource.uri,
672
- name: resource.name,
673
- description: resource.description,
674
- mimeType: resource.mimeType
675
- };
676
- if (resource.inputSchema) {
677
- resourceInfo.inputSchema = resource.inputSchema;
731
+ /**
732
+ * Automatically discover and register services from the mcp directory
733
+ * Called by init() unless autoDiscover is set to false
734
+ */
735
+ async autoDiscoverServices(customMcpDir, serviceFactories) {
736
+ if (this.autoDiscovered) return;
737
+ this.autoDiscovered = true;
738
+ try {
739
+ let mcpDir;
740
+ if (customMcpDir) {
741
+ mcpDir = customMcpDir;
742
+ } else {
743
+ const callerFile = this.getCallerFile();
744
+ if (callerFile) {
745
+ const callerDir = import_path.default.dirname(callerFile);
746
+ mcpDir = import_path.default.join(callerDir, "mcp");
747
+ } else {
748
+ mcpDir = import_path.default.join(process.cwd(), "mcp");
749
+ }
750
+ }
751
+ if (import_fs.default.existsSync(mcpDir)) {
752
+ this.logger.debug(`Auto-discovering services from: ${mcpDir}`);
753
+ await this.autoRegisterServices(mcpDir, serviceFactories);
754
+ } else {
755
+ this.logger.debug(`MCP directory not found at ${mcpDir}, skipping auto-discovery`);
756
+ }
757
+ } catch (error) {
758
+ this.logger.warn(`Auto-discovery failed: ${error.message}`);
678
759
  }
679
- resources.push(resourceInfo);
680
760
  }
681
- return {
682
- resources
683
- };
684
- });
685
- this.server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
686
- const uri = request.params.uri;
687
- const resource = this.resources.get(uri);
688
- if (!resource) {
689
- throw new Error(`Resource ${uri} not found`);
690
- }
691
- try {
692
- const result = await resource.method.call(resource.instance);
693
- return {
694
- contents: [
695
- {
696
- uri,
697
- mimeType: resource.mimeType,
698
- text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
761
+ /**
762
+ * Get the file path of the caller (the file that instantiated MCPServer)
763
+ */
764
+ getCallerFile() {
765
+ const originalPrepareStackTrace = Error.prepareStackTrace;
766
+ try {
767
+ const err = new Error();
768
+ Error.prepareStackTrace = (_, stack2) => stack2;
769
+ const stack = err.stack;
770
+ for (let i = 0; i < stack.length; i++) {
771
+ let fileName = stack[i].getFileName();
772
+ if (fileName && !fileName.includes("@leanmcp") && !fileName.includes("leanmcp-sdk\\packages\\core") && !fileName.includes("leanmcp-sdk/packages/core") && (fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".mjs"))) {
773
+ if (fileName.startsWith("file://")) {
774
+ fileName = fileName.replace("file:///", "").replace("file://", "");
775
+ if (process.platform === "win32" && fileName.startsWith("/")) {
776
+ fileName = fileName.substring(1);
777
+ }
778
+ }
779
+ return fileName;
699
780
  }
700
- ]
701
- };
702
- } catch (error) {
703
- throw new Error(`Failed to read resource ${uri}: ${error.message}`);
704
- }
705
- });
706
- this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
707
- const prompts = [];
708
- for (const [name, prompt] of this.prompts.entries()) {
709
- prompts.push({
710
- name,
711
- description: prompt.description,
712
- arguments: prompt.arguments
713
- });
714
- }
715
- return {
716
- prompts
717
- };
718
- });
719
- this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
720
- const promptName = request.params.name;
721
- const prompt = this.prompts.get(promptName);
722
- if (!prompt) {
723
- throw new Error(`Prompt ${promptName} not found`);
724
- }
725
- try {
726
- const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
727
- if (result && result.messages) {
728
- return result;
781
+ }
782
+ return null;
783
+ } finally {
784
+ Error.prepareStackTrace = originalPrepareStackTrace;
729
785
  }
730
- return {
731
- description: prompt.description,
732
- messages: [
733
- {
734
- role: "user",
735
- content: {
736
- type: "text",
737
- text: typeof result === "string" ? result : JSON.stringify(result)
786
+ }
787
+ setupHandlers() {
788
+ this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
789
+ const tools = [];
790
+ for (const [name, tool] of this.tools.entries()) {
791
+ tools.push({
792
+ name,
793
+ description: tool.description,
794
+ inputSchema: tool.inputSchema || {
795
+ type: "object",
796
+ properties: {}
738
797
  }
798
+ });
799
+ }
800
+ return {
801
+ tools
802
+ };
803
+ });
804
+ this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
805
+ const toolName = request.params.name;
806
+ const tool = this.tools.get(toolName);
807
+ if (!tool) {
808
+ throw new Error(`Tool ${toolName} not found`);
809
+ }
810
+ const methodMeta = getMethodMetadata(tool.method);
811
+ if (methodMeta.inputSchema) {
812
+ const validate = ajv.compile(methodMeta.inputSchema);
813
+ const valid = validate(request.params.arguments || {});
814
+ if (!valid) {
815
+ throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
739
816
  }
740
- ]
741
- };
742
- } catch (error) {
743
- throw new Error(`Failed to get prompt ${promptName}: ${error.message}`);
744
- }
745
- });
746
- }
747
- /**
748
- * Register a service instance with decorated methods
749
- */
750
- registerService(instance) {
751
- const cls = instance.constructor;
752
- const toolMethods = getDecoratedMethods(cls, "tool:name");
753
- for (const { method, propertyKey } of toolMethods) {
754
- const methodMeta = getMethodMetadata(method);
755
- const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
756
- let inputSchema = methodMeta.inputSchema;
757
- if (inputClass) {
758
- inputSchema = classToJsonSchemaWithConstraints(inputClass);
759
- }
760
- this.tools.set(methodMeta.toolName, {
761
- name: methodMeta.toolName,
762
- description: methodMeta.toolDescription || "",
763
- inputSchema,
764
- method,
765
- instance,
766
- propertyKey
767
- });
768
- if (this.logging) {
769
- this.logger.info(`Registered tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
770
- }
771
- }
772
- const promptMethods = getDecoratedMethods(cls, "prompt:name");
773
- for (const { method, propertyKey } of promptMethods) {
774
- const methodMeta = getMethodMetadata(method);
775
- const inputClass = Reflect.getMetadata?.("prompt:inputClass", method);
776
- let inputSchema = methodMeta.inputSchema;
777
- if (inputClass) {
778
- inputSchema = classToJsonSchemaWithConstraints(inputClass);
779
- }
780
- const promptArgs = inputSchema?.properties ? Object.keys(inputSchema.properties).map((key) => ({
781
- name: key,
782
- description: inputSchema?.properties?.[key]?.description || "",
783
- required: inputSchema?.required?.includes(key) || false
784
- })) : [];
785
- this.prompts.set(methodMeta.promptName, {
786
- name: methodMeta.promptName,
787
- description: methodMeta.promptDescription || "",
788
- arguments: promptArgs,
789
- method,
790
- instance,
791
- propertyKey
792
- });
793
- if (this.logging) {
794
- this.logger.info(`Registered prompt: ${methodMeta.promptName}`);
795
- }
796
- }
797
- const resourceMethods = getDecoratedMethods(cls, "resource:uri");
798
- for (const { method, propertyKey } of resourceMethods) {
799
- const methodMeta = getMethodMetadata(method);
800
- const inputClass = Reflect.getMetadata?.("resource:inputClass", method);
801
- let inputSchema = methodMeta.inputSchema;
802
- if (inputClass) {
803
- inputSchema = classToJsonSchemaWithConstraints(inputClass);
804
- }
805
- const mimeType = Reflect.getMetadata?.("resource:mimeType", method) || "application/json";
806
- this.resources.set(methodMeta.resourceUri, {
807
- uri: methodMeta.resourceUri,
808
- name: methodMeta.resourceName || methodMeta.resourceUri,
809
- description: methodMeta.resourceDescription || "",
810
- mimeType,
811
- inputSchema,
812
- method,
813
- instance,
814
- propertyKey
815
- });
816
- if (this.logging) {
817
- this.logger.info(`Registered resource: ${methodMeta.resourceUri}`);
818
- }
819
- }
820
- }
821
- /**
822
- * Get the underlying MCP SDK Server instance
823
- */
824
- getServer() {
825
- return this.server;
826
- }
827
- };
828
- var MCPServerRuntime = class {
829
- static {
830
- __name(this, "MCPServerRuntime");
831
- }
832
- server;
833
- tools = /* @__PURE__ */ new Map();
834
- prompts = /* @__PURE__ */ new Map();
835
- resources = /* @__PURE__ */ new Map();
836
- options;
837
- logger;
838
- constructor(options) {
839
- this.options = options;
840
- this.logger = new Logger({
841
- level: this.options.logging ? LogLevel.INFO : LogLevel.NONE,
842
- prefix: "MCPServerRuntime"
843
- });
844
- this.server = new import_server.Server({
845
- name: "leanmcp-server",
846
- version: "0.1.0"
847
- }, {
848
- capabilities: {
849
- tools: {},
850
- resources: {},
851
- prompts: {}
852
- }
853
- });
854
- this.setupHandlers();
855
- }
856
- setupHandlers() {
857
- this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
858
- const tools = [];
859
- for (const [name, tool] of this.tools.entries()) {
860
- tools.push({
861
- name,
862
- description: tool.description,
863
- inputSchema: tool.inputSchema || {
864
- type: "object",
865
- properties: {}
817
+ }
818
+ try {
819
+ const meta = request.params._meta;
820
+ const result = await tool.method.call(tool.instance, request.params.arguments, meta);
821
+ let formattedResult = result;
822
+ if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
823
+ formattedResult = result;
824
+ } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
825
+ formattedResult = JSON.stringify(result, null, 2);
826
+ } else {
827
+ formattedResult = String(result);
828
+ }
829
+ return {
830
+ content: [
831
+ {
832
+ type: "text",
833
+ text: formattedResult
834
+ }
835
+ ]
836
+ };
837
+ } catch (error) {
838
+ return {
839
+ content: [
840
+ {
841
+ type: "text",
842
+ text: `Error: ${error.message}`
843
+ }
844
+ ],
845
+ isError: true
846
+ };
847
+ }
848
+ });
849
+ this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
850
+ const resources = [];
851
+ for (const [uri, resource] of this.resources.entries()) {
852
+ const resourceInfo = {
853
+ uri: resource.uri,
854
+ name: resource.name,
855
+ description: resource.description,
856
+ mimeType: resource.mimeType
857
+ };
858
+ if (resource.inputSchema) {
859
+ resourceInfo.inputSchema = resource.inputSchema;
860
+ }
861
+ resources.push(resourceInfo);
862
+ }
863
+ return {
864
+ resources
865
+ };
866
+ });
867
+ this.server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
868
+ const uri = request.params.uri;
869
+ const resource = this.resources.get(uri);
870
+ if (!resource) {
871
+ throw new Error(`Resource ${uri} not found`);
872
+ }
873
+ try {
874
+ const result = await resource.method.call(resource.instance);
875
+ return {
876
+ contents: [
877
+ {
878
+ uri,
879
+ mimeType: resource.mimeType,
880
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
881
+ }
882
+ ]
883
+ };
884
+ } catch (error) {
885
+ throw new Error(`Failed to read resource ${uri}: ${error.message}`);
886
+ }
887
+ });
888
+ this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
889
+ const prompts = [];
890
+ for (const [name, prompt] of this.prompts.entries()) {
891
+ prompts.push({
892
+ name,
893
+ description: prompt.description,
894
+ arguments: prompt.arguments
895
+ });
896
+ }
897
+ return {
898
+ prompts
899
+ };
900
+ });
901
+ this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
902
+ const promptName = request.params.name;
903
+ const prompt = this.prompts.get(promptName);
904
+ if (!prompt) {
905
+ throw new Error(`Prompt ${promptName} not found`);
906
+ }
907
+ try {
908
+ const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
909
+ if (result && result.messages) {
910
+ return result;
911
+ }
912
+ return {
913
+ description: prompt.description,
914
+ messages: [
915
+ {
916
+ role: "user",
917
+ content: {
918
+ type: "text",
919
+ text: typeof result === "string" ? result : JSON.stringify(result)
920
+ }
921
+ }
922
+ ]
923
+ };
924
+ } catch (error) {
925
+ throw new Error(`Failed to get prompt ${promptName}: ${error.message}`);
866
926
  }
867
927
  });
868
928
  }
869
- return {
870
- tools
871
- };
872
- });
873
- this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
874
- const toolName = request.params.name;
875
- const tool = this.tools.get(toolName);
876
- if (!tool) {
877
- throw new Error(`Tool ${toolName} not found`);
878
- }
879
- const methodMeta = getMethodMetadata(tool.method);
880
- if (methodMeta.inputSchema) {
881
- const validate = ajv.compile(methodMeta.inputSchema);
882
- const valid = validate(request.params.arguments || {});
883
- if (!valid) {
884
- throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
929
+ /**
930
+ * Auto-register all services from the mcp directory
931
+ * Scans the directory recursively and registers all exported classes
932
+ *
933
+ * @param mcpDir - Path to the mcp directory containing service files
934
+ * @param serviceFactories - Optional map of service class names to factory functions for dependency injection
935
+ *
936
+ * @example
937
+ * // Auto-register services with no dependencies
938
+ * await server.autoRegisterServices('./mcp');
939
+ *
940
+ * @example
941
+ * // Auto-register with dependency injection
942
+ * await server.autoRegisterServices('./mcp', {
943
+ * SlackService: () => new SlackService(process.env.SLACK_TOKEN),
944
+ * AuthService: () => new AuthService(authProvider)
945
+ * });
946
+ */
947
+ async autoRegisterServices(mcpDir, serviceFactories) {
948
+ this.logger.debug(`Auto-registering services from: ${mcpDir}`);
949
+ if (!import_fs.default.existsSync(mcpDir)) {
950
+ this.logger.warn(`MCP directory not found: ${mcpDir}`);
951
+ return;
952
+ }
953
+ const serviceFiles = this.findServiceFiles(mcpDir);
954
+ this.logger.debug(`Found ${serviceFiles.length} service file(s)`);
955
+ for (const filePath of serviceFiles) {
956
+ try {
957
+ await this.loadAndRegisterService(filePath, serviceFactories);
958
+ } catch (error) {
959
+ this.logger.error(`Failed to load service from ${filePath}: ${error.message}`);
960
+ }
885
961
  }
886
962
  }
887
- if (methodMeta.authRequired) {
888
- if (this.options.logging) {
889
- this.logger.info(`Auth required for ${toolName} (provider: ${methodMeta.authProvider})`);
963
+ /**
964
+ * Recursively find all index.ts/index.js files in the mcp directory
965
+ */
966
+ findServiceFiles(dir) {
967
+ const files = [];
968
+ const entries = import_fs.default.readdirSync(dir, {
969
+ withFileTypes: true
970
+ });
971
+ for (const entry of entries) {
972
+ const fullPath = import_path.default.join(dir, entry.name);
973
+ if (entry.isDirectory()) {
974
+ files.push(...this.findServiceFiles(fullPath));
975
+ } else if (entry.isFile()) {
976
+ if (entry.name === "index.ts" || entry.name === "index.js") {
977
+ files.push(fullPath);
978
+ }
979
+ }
890
980
  }
981
+ return files;
891
982
  }
892
- try {
893
- const meta = request.params._meta;
894
- const result = await tool.method.call(tool.instance, request.params.arguments, meta);
895
- if (result && typeof result === "object" && result.type === "elicitation") {
896
- return {
897
- content: [
898
- {
899
- type: "text",
900
- text: JSON.stringify(result, null, 2)
983
+ /**
984
+ * Load a service file and register all exported classes
985
+ */
986
+ async loadAndRegisterService(filePath, serviceFactories) {
987
+ this.logger.debug(`Loading service from: ${filePath}`);
988
+ const fileUrl = (0, import_url.pathToFileURL)(filePath).href;
989
+ const module2 = await import(fileUrl);
990
+ let registeredCount = 0;
991
+ for (const [exportName, exportValue] of Object.entries(module2)) {
992
+ if (typeof exportValue === "function" && exportValue.prototype) {
993
+ try {
994
+ let instance;
995
+ if (serviceFactories && serviceFactories[exportName]) {
996
+ instance = serviceFactories[exportName]();
997
+ this.logger.info(`Using factory for service: ${exportName}`);
998
+ } else {
999
+ instance = new exportValue();
901
1000
  }
902
- ],
903
- isError: false
904
- };
1001
+ this.registerService(instance);
1002
+ registeredCount++;
1003
+ this.logger.debug(`Registered service: ${exportName} from ${import_path.default.basename(filePath)}`);
1004
+ } catch (error) {
1005
+ this.logger.warn(`Skipped ${exportName}: ${error.message}`);
1006
+ }
1007
+ }
905
1008
  }
906
- let formattedResult = result;
907
- if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
908
- formattedResult = result;
909
- } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
910
- formattedResult = JSON.stringify(result, null, 2);
911
- } else {
912
- formattedResult = String(result);
1009
+ if (registeredCount === 0) {
1010
+ this.logger.warn(`No services registered from ${filePath}`);
913
1011
  }
914
- return {
915
- content: [
916
- {
917
- type: "text",
918
- text: formattedResult
919
- }
920
- ]
921
- };
922
- } catch (error) {
923
- return {
924
- content: [
925
- {
926
- type: "text",
927
- text: `Error: ${error.message}`
928
- }
929
- ],
930
- isError: true
931
- };
932
1012
  }
933
- });
934
- this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
935
- const resources = [];
936
- for (const [uri, resource] of this.resources.entries()) {
937
- resources.push({
938
- uri: resource.uri,
939
- name: resource.name,
940
- description: resource.description,
941
- mimeType: resource.mimeType
942
- });
1013
+ /**
1014
+ * Register a service instance with decorated methods
1015
+ */
1016
+ registerService(instance) {
1017
+ const cls = instance.constructor;
1018
+ const toolMethods = getDecoratedMethods(cls, "tool:name");
1019
+ for (const { method, propertyKey } of toolMethods) {
1020
+ const methodMeta = getMethodMetadata(method);
1021
+ const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
1022
+ let inputSchema = methodMeta.inputSchema;
1023
+ if (inputClass) {
1024
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
1025
+ }
1026
+ this.tools.set(methodMeta.toolName, {
1027
+ name: methodMeta.toolName,
1028
+ description: methodMeta.toolDescription || "",
1029
+ inputSchema,
1030
+ method,
1031
+ instance,
1032
+ propertyKey
1033
+ });
1034
+ if (this.logging) {
1035
+ this.logger.debug(`Registered tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
1036
+ }
1037
+ }
1038
+ const promptMethods = getDecoratedMethods(cls, "prompt:name");
1039
+ for (const { method, propertyKey } of promptMethods) {
1040
+ const methodMeta = getMethodMetadata(method);
1041
+ const inputClass = Reflect.getMetadata?.("prompt:inputClass", method);
1042
+ let inputSchema = methodMeta.inputSchema;
1043
+ if (inputClass) {
1044
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
1045
+ }
1046
+ const promptArgs = inputSchema?.properties ? Object.keys(inputSchema.properties).map((key) => ({
1047
+ name: key,
1048
+ description: inputSchema?.properties?.[key]?.description || "",
1049
+ required: inputSchema?.required?.includes(key) || false
1050
+ })) : [];
1051
+ this.prompts.set(methodMeta.promptName, {
1052
+ name: methodMeta.promptName,
1053
+ description: methodMeta.promptDescription || "",
1054
+ arguments: promptArgs,
1055
+ method,
1056
+ instance,
1057
+ propertyKey
1058
+ });
1059
+ if (this.logging) {
1060
+ this.logger.debug(`Registered prompt: ${methodMeta.promptName}`);
1061
+ }
1062
+ }
1063
+ const resourceMethods = getDecoratedMethods(cls, "resource:uri");
1064
+ for (const { method, propertyKey } of resourceMethods) {
1065
+ const methodMeta = getMethodMetadata(method);
1066
+ const inputClass = Reflect.getMetadata?.("resource:inputClass", method);
1067
+ let inputSchema = methodMeta.inputSchema;
1068
+ if (inputClass) {
1069
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
1070
+ }
1071
+ const mimeType = Reflect.getMetadata?.("resource:mimeType", method) || "application/json";
1072
+ this.resources.set(methodMeta.resourceUri, {
1073
+ uri: methodMeta.resourceUri,
1074
+ name: methodMeta.resourceName || methodMeta.resourceUri,
1075
+ description: methodMeta.resourceDescription || "",
1076
+ mimeType,
1077
+ inputSchema,
1078
+ method,
1079
+ instance,
1080
+ propertyKey
1081
+ });
1082
+ if (this.logging) {
1083
+ this.logger.debug(`Registered resource: ${methodMeta.resourceUri}`);
1084
+ }
1085
+ }
943
1086
  }
944
- return {
945
- resources
946
- };
947
- });
948
- this.server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
949
- const uri = request.params.uri;
950
- const resource = this.resources.get(uri);
951
- if (!resource) {
952
- throw new Error(`Resource ${uri} not found`);
1087
+ /**
1088
+ * Get the underlying MCP SDK Server instance
1089
+ * Attaches waitForInit method for HTTP server initialization
1090
+ */
1091
+ getServer() {
1092
+ this.server.waitForInit = () => this.waitForInit();
1093
+ return this.server;
953
1094
  }
954
- try {
955
- const result = await resource.method.call(resource.instance);
956
- return {
957
- contents: [
958
- {
959
- uri,
960
- mimeType: resource.mimeType,
961
- text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
962
- }
963
- ]
964
- };
965
- } catch (error) {
966
- throw new Error(`Failed to read resource ${uri}: ${error.message}`);
1095
+ };
1096
+ MCPServerRuntime = class {
1097
+ static {
1098
+ __name(this, "MCPServerRuntime");
967
1099
  }
968
- });
969
- this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
970
- const prompts = [];
971
- for (const [name, prompt] of this.prompts.entries()) {
972
- prompts.push({
973
- name,
974
- description: prompt.description,
975
- arguments: prompt.arguments
1100
+ server;
1101
+ tools = /* @__PURE__ */ new Map();
1102
+ prompts = /* @__PURE__ */ new Map();
1103
+ resources = /* @__PURE__ */ new Map();
1104
+ options;
1105
+ logger;
1106
+ constructor(options) {
1107
+ this.options = options;
1108
+ this.logger = new Logger({
1109
+ level: this.options.logging ? LogLevel.INFO : LogLevel.NONE,
1110
+ prefix: "MCPServerRuntime"
976
1111
  });
1112
+ this.server = new import_server.Server({
1113
+ name: "leanmcp-server",
1114
+ version: "0.1.0"
1115
+ }, {
1116
+ capabilities: {
1117
+ tools: {},
1118
+ resources: {},
1119
+ prompts: {}
1120
+ }
1121
+ });
1122
+ this.setupHandlers();
977
1123
  }
978
- return {
979
- prompts
980
- };
981
- });
982
- this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
983
- const promptName = request.params.name;
984
- const prompt = this.prompts.get(promptName);
985
- if (!prompt) {
986
- throw new Error(`Prompt ${promptName} not found`);
987
- }
988
- try {
989
- const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
990
- if (result && result.messages) {
991
- return result;
992
- }
993
- return {
994
- description: prompt.description,
995
- messages: [
996
- {
997
- role: "user",
998
- content: {
999
- type: "text",
1000
- text: typeof result === "string" ? result : JSON.stringify(result)
1124
+ setupHandlers() {
1125
+ this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
1126
+ const tools = [];
1127
+ for (const [name, tool] of this.tools.entries()) {
1128
+ tools.push({
1129
+ name,
1130
+ description: tool.description,
1131
+ inputSchema: tool.inputSchema || {
1132
+ type: "object",
1133
+ properties: {}
1001
1134
  }
1135
+ });
1136
+ }
1137
+ return {
1138
+ tools
1139
+ };
1140
+ });
1141
+ this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
1142
+ const toolName = request.params.name;
1143
+ const tool = this.tools.get(toolName);
1144
+ if (!tool) {
1145
+ throw new Error(`Tool ${toolName} not found`);
1146
+ }
1147
+ const methodMeta = getMethodMetadata(tool.method);
1148
+ if (methodMeta.inputSchema) {
1149
+ const validate = ajv.compile(methodMeta.inputSchema);
1150
+ const valid = validate(request.params.arguments || {});
1151
+ if (!valid) {
1152
+ throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
1002
1153
  }
1003
- ]
1004
- };
1005
- } catch (error) {
1006
- throw new Error(`Failed to get prompt ${promptName}: ${error.message}`);
1007
- }
1008
- });
1009
- }
1010
- async loadServices() {
1011
- const absPath = import_path.default.resolve(this.options.servicesDir);
1012
- if (!import_fs.default.existsSync(absPath)) {
1013
- this.logger.error(`Services directory not found: ${absPath}`);
1014
- return;
1015
- }
1016
- const files = import_fs.default.readdirSync(absPath);
1017
- let toolCount = 0;
1018
- let promptCount = 0;
1019
- let resourceCount = 0;
1020
- for (const dir of files) {
1021
- const modulePath = import_path.default.join(absPath, dir, "index.ts");
1022
- const modulePathJs = import_path.default.join(absPath, dir, "index.js");
1023
- const finalPath = import_fs.default.existsSync(modulePath) ? modulePath : import_fs.default.existsSync(modulePathJs) ? modulePathJs : null;
1024
- if (finalPath) {
1025
- try {
1026
- const fileUrl = (0, import_url.pathToFileURL)(finalPath).href;
1027
- const mod = await import(fileUrl);
1028
- const exportedClasses = Object.values(mod).filter((val) => typeof val === "function" && val.prototype);
1029
- for (const cls of exportedClasses) {
1030
- const instance = new cls();
1031
- const envsPropKey = Reflect.getMetadata?.("userenvs:propertyKey", cls);
1032
- if (envsPropKey) {
1033
- instance[envsPropKey] = process.env;
1154
+ }
1155
+ if (methodMeta.authRequired) {
1156
+ if (this.options.logging) {
1157
+ this.logger.info(`Auth required for ${toolName} (provider: ${methodMeta.authProvider})`);
1034
1158
  }
1035
- const toolMethods = getDecoratedMethods(cls, "tool:name");
1036
- for (const { method, propertyKey, metadata } of toolMethods) {
1037
- const methodMeta = getMethodMetadata(method);
1038
- const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
1039
- const outputClass = Reflect.getMetadata?.("tool:outputClass", method);
1040
- let inputSchema = methodMeta.inputSchema;
1041
- if (inputClass) {
1042
- inputSchema = classToJsonSchemaWithConstraints(inputClass);
1043
- }
1044
- this.tools.set(methodMeta.toolName, {
1045
- name: methodMeta.toolName,
1046
- description: methodMeta.toolDescription || "",
1047
- inputSchema,
1048
- method,
1049
- instance,
1050
- propertyKey
1051
- });
1052
- toolCount++;
1053
- if (this.options.logging) {
1054
- this.logger.info(`Loaded tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
1055
- }
1159
+ }
1160
+ try {
1161
+ const meta = request.params._meta;
1162
+ const result = await tool.method.call(tool.instance, request.params.arguments, meta);
1163
+ if (result && typeof result === "object" && result.type === "elicitation") {
1164
+ return {
1165
+ content: [
1166
+ {
1167
+ type: "text",
1168
+ text: JSON.stringify(result, null, 2)
1169
+ }
1170
+ ],
1171
+ isError: false
1172
+ };
1056
1173
  }
1057
- const promptMethods = getDecoratedMethods(cls, "prompt:name");
1058
- for (const { method, propertyKey, metadata } of promptMethods) {
1059
- const methodMeta = getMethodMetadata(method);
1060
- const promptArgs = methodMeta.inputSchema?.properties ? Object.keys(methodMeta.inputSchema.properties).map((key) => ({
1061
- name: key,
1062
- description: methodMeta.inputSchema?.properties?.[key]?.description || "",
1063
- required: methodMeta.inputSchema?.required?.includes(key) || false
1064
- })) : [];
1065
- this.prompts.set(methodMeta.promptName, {
1066
- name: methodMeta.promptName,
1067
- description: methodMeta.promptDescription || "",
1068
- arguments: promptArgs,
1069
- method,
1070
- instance,
1071
- propertyKey
1072
- });
1073
- promptCount++;
1074
- if (this.options.logging) {
1075
- this.logger.info(`Loaded prompt: ${methodMeta.promptName}`);
1076
- }
1174
+ let formattedResult = result;
1175
+ if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
1176
+ formattedResult = result;
1177
+ } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
1178
+ formattedResult = JSON.stringify(result, null, 2);
1179
+ } else {
1180
+ formattedResult = String(result);
1181
+ }
1182
+ return {
1183
+ content: [
1184
+ {
1185
+ type: "text",
1186
+ text: formattedResult
1187
+ }
1188
+ ]
1189
+ };
1190
+ } catch (error) {
1191
+ return {
1192
+ content: [
1193
+ {
1194
+ type: "text",
1195
+ text: `Error: ${error.message}`
1196
+ }
1197
+ ],
1198
+ isError: true
1199
+ };
1200
+ }
1201
+ });
1202
+ this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
1203
+ const resources = [];
1204
+ for (const [uri, resource] of this.resources.entries()) {
1205
+ resources.push({
1206
+ uri: resource.uri,
1207
+ name: resource.name,
1208
+ description: resource.description,
1209
+ mimeType: resource.mimeType
1210
+ });
1211
+ }
1212
+ return {
1213
+ resources
1214
+ };
1215
+ });
1216
+ this.server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
1217
+ const uri = request.params.uri;
1218
+ const resource = this.resources.get(uri);
1219
+ if (!resource) {
1220
+ throw new Error(`Resource ${uri} not found`);
1221
+ }
1222
+ try {
1223
+ const result = await resource.method.call(resource.instance);
1224
+ return {
1225
+ contents: [
1226
+ {
1227
+ uri,
1228
+ mimeType: resource.mimeType,
1229
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
1230
+ }
1231
+ ]
1232
+ };
1233
+ } catch (error) {
1234
+ throw new Error(`Failed to read resource ${uri}: ${error.message}`);
1235
+ }
1236
+ });
1237
+ this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
1238
+ const prompts = [];
1239
+ for (const [name, prompt] of this.prompts.entries()) {
1240
+ prompts.push({
1241
+ name,
1242
+ description: prompt.description,
1243
+ arguments: prompt.arguments
1244
+ });
1245
+ }
1246
+ return {
1247
+ prompts
1248
+ };
1249
+ });
1250
+ this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
1251
+ const promptName = request.params.name;
1252
+ const prompt = this.prompts.get(promptName);
1253
+ if (!prompt) {
1254
+ throw new Error(`Prompt ${promptName} not found`);
1255
+ }
1256
+ try {
1257
+ const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
1258
+ if (result && result.messages) {
1259
+ return result;
1077
1260
  }
1078
- const resourceMethods = getDecoratedMethods(cls, "resource:uri");
1079
- for (const { method, propertyKey, metadata } of resourceMethods) {
1080
- const methodMeta = getMethodMetadata(method);
1081
- this.resources.set(methodMeta.resourceUri, {
1082
- uri: methodMeta.resourceUri,
1083
- name: methodMeta.resourceName || methodMeta.resourceUri,
1084
- description: methodMeta.resourceDescription || "",
1085
- mimeType: "application/json",
1086
- method,
1087
- instance,
1088
- propertyKey
1089
- });
1090
- resourceCount++;
1261
+ return {
1262
+ description: prompt.description,
1263
+ messages: [
1264
+ {
1265
+ role: "user",
1266
+ content: {
1267
+ type: "text",
1268
+ text: typeof result === "string" ? result : JSON.stringify(result)
1269
+ }
1270
+ }
1271
+ ]
1272
+ };
1273
+ } catch (error) {
1274
+ throw new Error(`Failed to get prompt ${promptName}: ${error.message}`);
1275
+ }
1276
+ });
1277
+ }
1278
+ async loadServices() {
1279
+ const absPath = import_path.default.resolve(this.options.servicesDir);
1280
+ if (!import_fs.default.existsSync(absPath)) {
1281
+ this.logger.error(`Services directory not found: ${absPath}`);
1282
+ return;
1283
+ }
1284
+ const files = import_fs.default.readdirSync(absPath);
1285
+ let toolCount = 0;
1286
+ let promptCount = 0;
1287
+ let resourceCount = 0;
1288
+ for (const dir of files) {
1289
+ const modulePath = import_path.default.join(absPath, dir, "index.ts");
1290
+ const modulePathJs = import_path.default.join(absPath, dir, "index.js");
1291
+ const finalPath = import_fs.default.existsSync(modulePath) ? modulePath : import_fs.default.existsSync(modulePathJs) ? modulePathJs : null;
1292
+ if (finalPath) {
1293
+ try {
1294
+ const fileUrl = (0, import_url.pathToFileURL)(finalPath).href;
1295
+ const mod = await import(fileUrl);
1296
+ const exportedClasses = Object.values(mod).filter((val) => typeof val === "function" && val.prototype);
1297
+ for (const cls of exportedClasses) {
1298
+ const instance = new cls();
1299
+ const envsPropKey = Reflect.getMetadata?.("userenvs:propertyKey", cls);
1300
+ if (envsPropKey) {
1301
+ instance[envsPropKey] = process.env;
1302
+ }
1303
+ const toolMethods = getDecoratedMethods(cls, "tool:name");
1304
+ for (const { method, propertyKey, metadata } of toolMethods) {
1305
+ const methodMeta = getMethodMetadata(method);
1306
+ const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
1307
+ const outputClass = Reflect.getMetadata?.("tool:outputClass", method);
1308
+ let inputSchema = methodMeta.inputSchema;
1309
+ if (inputClass) {
1310
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
1311
+ }
1312
+ this.tools.set(methodMeta.toolName, {
1313
+ name: methodMeta.toolName,
1314
+ description: methodMeta.toolDescription || "",
1315
+ inputSchema,
1316
+ method,
1317
+ instance,
1318
+ propertyKey
1319
+ });
1320
+ toolCount++;
1321
+ if (this.options.logging) {
1322
+ this.logger.info(`Loaded tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
1323
+ }
1324
+ }
1325
+ const promptMethods = getDecoratedMethods(cls, "prompt:name");
1326
+ for (const { method, propertyKey, metadata } of promptMethods) {
1327
+ const methodMeta = getMethodMetadata(method);
1328
+ const promptArgs = methodMeta.inputSchema?.properties ? Object.keys(methodMeta.inputSchema.properties).map((key) => ({
1329
+ name: key,
1330
+ description: methodMeta.inputSchema?.properties?.[key]?.description || "",
1331
+ required: methodMeta.inputSchema?.required?.includes(key) || false
1332
+ })) : [];
1333
+ this.prompts.set(methodMeta.promptName, {
1334
+ name: methodMeta.promptName,
1335
+ description: methodMeta.promptDescription || "",
1336
+ arguments: promptArgs,
1337
+ method,
1338
+ instance,
1339
+ propertyKey
1340
+ });
1341
+ promptCount++;
1342
+ if (this.options.logging) {
1343
+ this.logger.info(`Loaded prompt: ${methodMeta.promptName}`);
1344
+ }
1345
+ }
1346
+ const resourceMethods = getDecoratedMethods(cls, "resource:uri");
1347
+ for (const { method, propertyKey, metadata } of resourceMethods) {
1348
+ const methodMeta = getMethodMetadata(method);
1349
+ this.resources.set(methodMeta.resourceUri, {
1350
+ uri: methodMeta.resourceUri,
1351
+ name: methodMeta.resourceName || methodMeta.resourceUri,
1352
+ description: methodMeta.resourceDescription || "",
1353
+ mimeType: "application/json",
1354
+ method,
1355
+ instance,
1356
+ propertyKey
1357
+ });
1358
+ resourceCount++;
1359
+ if (this.options.logging) {
1360
+ this.logger.info(`Loaded resource: ${methodMeta.resourceUri}`);
1361
+ }
1362
+ }
1363
+ }
1364
+ } catch (error) {
1365
+ this.logger.error(`Failed to load from ${dir}:`, error.message || error);
1091
1366
  if (this.options.logging) {
1092
- this.logger.info(`Loaded resource: ${methodMeta.resourceUri}`);
1367
+ this.logger.error("Full error:", error);
1093
1368
  }
1094
1369
  }
1095
1370
  }
1096
- } catch (error) {
1097
- this.logger.error(`Failed to load from ${dir}:`, error.message || error);
1098
- if (this.options.logging) {
1099
- this.logger.error("Full error:", error);
1100
- }
1101
1371
  }
1102
- }
1103
- }
1104
- if (this.options.logging) {
1105
- this.logger.info(`
1372
+ if (this.options.logging) {
1373
+ this.logger.info(`
1106
1374
  Loaded ${toolCount} tools, ${promptCount} prompts, ${resourceCount} resources`);
1107
- }
1108
- }
1109
- async start() {
1110
- await this.loadServices();
1111
- const transport = new import_stdio.StdioServerTransport();
1112
- await this.server.connect(transport);
1113
- if (this.options.logging) {
1114
- this.logger.info("LeanMCP server running on stdio");
1115
- }
1116
- }
1117
- getServer() {
1118
- return this.server;
1119
- }
1120
- getTools() {
1121
- return Array.from(this.tools.values());
1122
- }
1123
- getPrompts() {
1124
- return Array.from(this.prompts.values());
1125
- }
1126
- getResources() {
1127
- return Array.from(this.resources.values());
1375
+ }
1376
+ }
1377
+ async start() {
1378
+ await this.loadServices();
1379
+ const transport = new import_stdio.StdioServerTransport();
1380
+ await this.server.connect(transport);
1381
+ if (this.options.logging) {
1382
+ this.logger.info("LeanMCP server running on stdio");
1383
+ }
1384
+ }
1385
+ getServer() {
1386
+ return this.server;
1387
+ }
1388
+ getTools() {
1389
+ return Array.from(this.tools.values());
1390
+ }
1391
+ getPrompts() {
1392
+ return Array.from(this.prompts.values());
1393
+ }
1394
+ getResources() {
1395
+ return Array.from(this.resources.values());
1396
+ }
1397
+ };
1398
+ __name(startMCPServer, "startMCPServer");
1128
1399
  }
1129
- };
1130
- async function startMCPServer(options) {
1131
- const runtime = new MCPServerRuntime(options);
1132
- await runtime.start();
1133
- return runtime;
1134
- }
1135
- __name(startMCPServer, "startMCPServer");
1400
+ });
1401
+ init_index();
1136
1402
  // Annotate the CommonJS export names for ESM import in node:
1137
1403
  0 && (module.exports = {
1138
1404
  Auth,