@leanmcp/core 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.
package/dist/index.js ADDED
@@ -0,0 +1,1162 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
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
+ // src/decorators.ts
72
+ var import_reflect_metadata = require("reflect-metadata");
73
+ function Tool(options = {}) {
74
+ return (target, propertyKey, descriptor) => {
75
+ const toolName = String(propertyKey);
76
+ Reflect.defineMetadata("tool:name", toolName, descriptor.value);
77
+ Reflect.defineMetadata("tool:description", options.description || "", descriptor.value);
78
+ Reflect.defineMetadata("tool:propertyKey", propertyKey, descriptor.value);
79
+ if (options.inputClass) {
80
+ Reflect.defineMetadata("tool:inputClass", options.inputClass, descriptor.value);
81
+ }
82
+ };
83
+ }
84
+ __name(Tool, "Tool");
85
+ function Prompt(options = {}) {
86
+ return (target, propertyKey, descriptor) => {
87
+ const promptName = String(propertyKey);
88
+ Reflect.defineMetadata("prompt:name", promptName, descriptor.value);
89
+ Reflect.defineMetadata("prompt:description", options.description || "", descriptor.value);
90
+ Reflect.defineMetadata("prompt:propertyKey", propertyKey, descriptor.value);
91
+ if (options.inputClass) {
92
+ Reflect.defineMetadata("prompt:inputClass", options.inputClass, descriptor.value);
93
+ } else {
94
+ const paramTypes = Reflect.getMetadata("design:paramtypes", target, propertyKey);
95
+ if (paramTypes && paramTypes.length > 0 && paramTypes[0] !== Object) {
96
+ Reflect.defineMetadata("prompt:inputClass", paramTypes[0], descriptor.value);
97
+ }
98
+ }
99
+ };
100
+ }
101
+ __name(Prompt, "Prompt");
102
+ function Resource(options = {}) {
103
+ return (target, propertyKey, descriptor) => {
104
+ const resourceName = String(propertyKey);
105
+ const className = target.constructor.name.toLowerCase().replace("service", "");
106
+ const resourceUri = `${className}://${resourceName}`;
107
+ Reflect.defineMetadata("resource:uri", resourceUri, descriptor.value);
108
+ Reflect.defineMetadata("resource:name", resourceName, descriptor.value);
109
+ Reflect.defineMetadata("resource:description", options.description || "", descriptor.value);
110
+ Reflect.defineMetadata("resource:mimeType", options.mimeType || "application/json", descriptor.value);
111
+ Reflect.defineMetadata("resource:propertyKey", propertyKey, descriptor.value);
112
+ if (options.inputClass) {
113
+ Reflect.defineMetadata("resource:inputClass", options.inputClass, descriptor.value);
114
+ }
115
+ };
116
+ }
117
+ __name(Resource, "Resource");
118
+ function Auth(options) {
119
+ return (target, propertyKey, descriptor) => {
120
+ if (propertyKey && descriptor) {
121
+ Reflect.defineMetadata("auth:provider", options.provider, descriptor.value);
122
+ Reflect.defineMetadata("auth:required", true, descriptor.value);
123
+ } else {
124
+ Reflect.defineMetadata("auth:provider", options.provider, target);
125
+ Reflect.defineMetadata("auth:required", true, target);
126
+ }
127
+ };
128
+ }
129
+ __name(Auth, "Auth");
130
+ function UserEnvs() {
131
+ return (target, propertyKey) => {
132
+ const constructor = target.constructor;
133
+ Reflect.defineMetadata("userenvs:propertyKey", propertyKey, constructor);
134
+ };
135
+ }
136
+ __name(UserEnvs, "UserEnvs");
137
+ function UI(component) {
138
+ return (target, propertyKey, descriptor) => {
139
+ if (propertyKey && descriptor) {
140
+ Reflect.defineMetadata("ui:component", component, descriptor.value);
141
+ } else {
142
+ Reflect.defineMetadata("ui:component", component, target);
143
+ }
144
+ };
145
+ }
146
+ __name(UI, "UI");
147
+ function Render(format) {
148
+ return (target, propertyKey, descriptor) => {
149
+ Reflect.defineMetadata("render:format", format, descriptor.value);
150
+ };
151
+ }
152
+ __name(Render, "Render");
153
+ function Deprecated(message) {
154
+ return (target, propertyKey, descriptor) => {
155
+ const deprecationMessage = message || "This feature is deprecated";
156
+ if (propertyKey && descriptor) {
157
+ Reflect.defineMetadata("deprecated:message", deprecationMessage, descriptor.value);
158
+ Reflect.defineMetadata("deprecated:true", true, descriptor.value);
159
+ const originalMethod = descriptor.value;
160
+ descriptor.value = function(...args) {
161
+ console.warn(`DEPRECATED: ${String(propertyKey)} - ${deprecationMessage}`);
162
+ return originalMethod.apply(this, args);
163
+ };
164
+ } else {
165
+ Reflect.defineMetadata("deprecated:message", deprecationMessage, target);
166
+ Reflect.defineMetadata("deprecated:true", true, target);
167
+ console.warn(`DEPRECATED: ${target.name} - ${deprecationMessage}`);
168
+ }
169
+ };
170
+ }
171
+ __name(Deprecated, "Deprecated");
172
+ function getMethodMetadata(method) {
173
+ return {
174
+ // Tool metadata
175
+ toolName: Reflect.getMetadata("tool:name", method),
176
+ toolDescription: Reflect.getMetadata("tool:description", method),
177
+ // Prompt metadata
178
+ promptName: Reflect.getMetadata("prompt:name", method),
179
+ promptDescription: Reflect.getMetadata("prompt:description", method),
180
+ // Resource metadata
181
+ resourceUri: Reflect.getMetadata("resource:uri", method),
182
+ resourceName: Reflect.getMetadata("resource:name", method),
183
+ resourceDescription: Reflect.getMetadata("resource:description", method),
184
+ // Common metadata
185
+ inputSchema: Reflect.getMetadata("schema:input", method),
186
+ outputSchema: Reflect.getMetadata("schema:output", method),
187
+ authProvider: Reflect.getMetadata("auth:provider", method),
188
+ authRequired: Reflect.getMetadata("auth:required", method),
189
+ uiComponent: Reflect.getMetadata("ui:component", method),
190
+ renderFormat: Reflect.getMetadata("render:format", method),
191
+ deprecated: Reflect.getMetadata("deprecated:true", method),
192
+ deprecationMessage: Reflect.getMetadata("deprecated:message", method)
193
+ };
194
+ }
195
+ __name(getMethodMetadata, "getMethodMetadata");
196
+ function getDecoratedMethods(target, metadataKey) {
197
+ const methods = [];
198
+ const prototype = target.prototype || target;
199
+ for (const propertyKey of Object.getOwnPropertyNames(prototype)) {
200
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, propertyKey);
201
+ if (descriptor && typeof descriptor.value === "function") {
202
+ const metadata = Reflect.getMetadata(metadataKey, descriptor.value);
203
+ if (metadata !== void 0) {
204
+ methods.push({
205
+ method: descriptor.value,
206
+ propertyKey,
207
+ metadata
208
+ });
209
+ }
210
+ }
211
+ }
212
+ return methods;
213
+ }
214
+ __name(getDecoratedMethods, "getDecoratedMethods");
215
+
216
+ // src/schema-generator.ts
217
+ var import_reflect_metadata2 = require("reflect-metadata");
218
+ function classToJsonSchema(classConstructor) {
219
+ const instance = new classConstructor();
220
+ const properties = {};
221
+ const required = [];
222
+ const propertyNames = Object.keys(instance);
223
+ for (const propertyName of propertyNames) {
224
+ const propertyType = Reflect.getMetadata("design:type", instance, propertyName);
225
+ let jsonSchemaType = "any";
226
+ if (propertyType) {
227
+ switch (propertyType.name) {
228
+ case "String":
229
+ jsonSchemaType = "string";
230
+ break;
231
+ case "Number":
232
+ jsonSchemaType = "number";
233
+ break;
234
+ case "Boolean":
235
+ jsonSchemaType = "boolean";
236
+ break;
237
+ case "Array":
238
+ jsonSchemaType = "array";
239
+ break;
240
+ case "Object":
241
+ jsonSchemaType = "object";
242
+ break;
243
+ default:
244
+ jsonSchemaType = "object";
245
+ }
246
+ }
247
+ properties[propertyName] = {
248
+ type: jsonSchemaType
249
+ };
250
+ const descriptor = Object.getOwnPropertyDescriptor(instance, propertyName);
251
+ if (descriptor && descriptor.value === void 0) {
252
+ const isOptional = propertyName.endsWith("?") || Reflect.getMetadata("optional", instance, propertyName);
253
+ if (!isOptional) {
254
+ required.push(propertyName);
255
+ }
256
+ }
257
+ }
258
+ return {
259
+ type: "object",
260
+ properties,
261
+ required: required.length > 0 ? required : void 0
262
+ };
263
+ }
264
+ __name(classToJsonSchema, "classToJsonSchema");
265
+ function Optional() {
266
+ return (target, propertyKey) => {
267
+ Reflect.defineMetadata("optional", true, target, propertyKey);
268
+ };
269
+ }
270
+ __name(Optional, "Optional");
271
+ function SchemaConstraint(constraints) {
272
+ return (target, propertyKey) => {
273
+ Reflect.defineMetadata("schema:constraints", constraints, target, propertyKey);
274
+ };
275
+ }
276
+ __name(SchemaConstraint, "SchemaConstraint");
277
+ function classToJsonSchemaWithConstraints(classConstructor) {
278
+ const instance = new classConstructor();
279
+ const properties = {};
280
+ const required = [];
281
+ const propertyNames = Object.keys(instance);
282
+ for (const propertyName of propertyNames) {
283
+ const propertyType = Reflect.getMetadata("design:type", instance, propertyName);
284
+ const constraints = Reflect.getMetadata("schema:constraints", instance, propertyName);
285
+ const isOptional = Reflect.getMetadata("optional", instance, propertyName);
286
+ let jsonSchemaType = "string";
287
+ if (propertyType) {
288
+ switch (propertyType.name) {
289
+ case "String":
290
+ jsonSchemaType = "string";
291
+ break;
292
+ case "Number":
293
+ jsonSchemaType = "number";
294
+ break;
295
+ case "Boolean":
296
+ jsonSchemaType = "boolean";
297
+ break;
298
+ case "Array":
299
+ jsonSchemaType = "array";
300
+ break;
301
+ case "Object":
302
+ jsonSchemaType = "object";
303
+ break;
304
+ default:
305
+ jsonSchemaType = "object";
306
+ }
307
+ } else if (constraints) {
308
+ if (constraints.minLength !== void 0 || constraints.maxLength !== void 0 || constraints.pattern) {
309
+ jsonSchemaType = "string";
310
+ } else if (constraints.minimum !== void 0 || constraints.maximum !== void 0) {
311
+ jsonSchemaType = "number";
312
+ } else if (constraints.enum && constraints.enum.length > 0) {
313
+ const firstValue = constraints.enum[0];
314
+ if (typeof firstValue === "number") {
315
+ jsonSchemaType = "number";
316
+ } else if (typeof firstValue === "boolean") {
317
+ jsonSchemaType = "boolean";
318
+ } else {
319
+ jsonSchemaType = "string";
320
+ }
321
+ }
322
+ }
323
+ properties[propertyName] = {
324
+ type: jsonSchemaType,
325
+ ...constraints || {}
326
+ };
327
+ if (!isOptional) {
328
+ required.push(propertyName);
329
+ }
330
+ }
331
+ return {
332
+ type: "object",
333
+ properties,
334
+ required: required.length > 0 ? required : void 0
335
+ };
336
+ }
337
+ __name(classToJsonSchemaWithConstraints, "classToJsonSchemaWithConstraints");
338
+
339
+ // src/http-server.ts
340
+ var import_node_crypto = require("crypto");
341
+
342
+ // 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;
396
+ }
397
+ };
398
+ var defaultLogger = new Logger({
399
+ level: 1,
400
+ prefix: "LeanMCP"
401
+ });
402
+
403
+ // src/validation.ts
404
+ function validatePort(port) {
405
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
406
+ throw new Error(`Invalid port: ${port}. Must be an integer between 1-65535`);
407
+ }
408
+ }
409
+ __name(validatePort, "validatePort");
410
+ function validatePath(path2) {
411
+ if (path2.includes("..") || path2.includes("~")) {
412
+ throw new Error(`Invalid path: ${path2}. Path traversal patterns are not allowed`);
413
+ }
414
+ }
415
+ __name(validatePath, "validatePath");
416
+ function validateServiceName(name) {
417
+ const validNamePattern = /^[a-zA-Z0-9_-]+$/;
418
+ if (!validNamePattern.test(name)) {
419
+ throw new Error(`Invalid service name: ${name}. Service names must contain only alphanumeric characters, hyphens, and underscores`);
420
+ }
421
+ }
422
+ __name(validateServiceName, "validateServiceName");
423
+ function validateNonEmpty(value, fieldName) {
424
+ if (!value || value.trim().length === 0) {
425
+ throw new Error(`${fieldName} cannot be empty`);
426
+ }
427
+ }
428
+ __name(validateNonEmpty, "validateNonEmpty");
429
+ function validateUrl(url, allowedProtocols = [
430
+ "http:",
431
+ "https:"
432
+ ]) {
433
+ try {
434
+ const parsed = new URL(url);
435
+ if (!allowedProtocols.includes(parsed.protocol)) {
436
+ throw new Error(`Invalid URL protocol: ${parsed.protocol}. Allowed protocols: ${allowedProtocols.join(", ")}`);
437
+ }
438
+ } catch (error) {
439
+ if (error instanceof TypeError) {
440
+ throw new Error(`Invalid URL: ${url}`);
441
+ }
442
+ throw error;
443
+ }
444
+ }
445
+ __name(validateUrl, "validateUrl");
446
+
447
+ // src/http-server.ts
448
+ function isInitializeRequest(body) {
449
+ return body && body.method === "initialize";
450
+ }
451
+ __name(isInitializeRequest, "isInitializeRequest");
452
+ async function createHTTPServer(serverFactory, options = {}) {
453
+ const [express, { StreamableHTTPServerTransport }, cors] = await Promise.all([
454
+ // @ts-ignore
455
+ import("express").catch(() => {
456
+ throw new Error("Express not found. Install with: npm install express @types/express");
457
+ }),
458
+ // @ts-ignore
459
+ import("@modelcontextprotocol/sdk/server/streamableHttp.js").catch(() => {
460
+ throw new Error("MCP SDK not found. Install with: npm install @modelcontextprotocol/sdk");
461
+ }),
462
+ // @ts-ignore
463
+ options.cors ? import("cors").catch(() => null) : Promise.resolve(null)
464
+ ]);
465
+ const app = express.default();
466
+ const port = options.port || 3001;
467
+ validatePort(port);
468
+ const transports = {};
469
+ const logger = options.logger || new Logger({
470
+ level: options.logging ? LogLevel.INFO : LogLevel.NONE,
471
+ prefix: "HTTP"
472
+ });
473
+ if (cors && options.cors) {
474
+ const corsOptions = typeof options.cors === "object" ? {
475
+ origin: options.cors.origin || false,
476
+ methods: [
477
+ "GET",
478
+ "POST",
479
+ "DELETE",
480
+ "OPTIONS"
481
+ ],
482
+ allowedHeaders: [
483
+ "Content-Type",
484
+ "mcp-session-id",
485
+ "mcp-protocol-version",
486
+ "Authorization"
487
+ ],
488
+ exposedHeaders: [
489
+ "mcp-session-id"
490
+ ],
491
+ credentials: options.cors.credentials ?? false,
492
+ maxAge: 86400
493
+ } : false;
494
+ if (corsOptions) {
495
+ app.use(cors.default(corsOptions));
496
+ }
497
+ }
498
+ app.use(express.json());
499
+ logger.info("Starting LeanMCP HTTP Server...");
500
+ app.get("/health", (req, res) => {
501
+ res.json({
502
+ status: "ok",
503
+ activeSessions: Object.keys(transports).length,
504
+ uptime: process.uptime()
505
+ });
506
+ });
507
+ const handleMCPRequest = /* @__PURE__ */ __name(async (req, res) => {
508
+ const sessionId = req.headers["mcp-session-id"];
509
+ let transport;
510
+ try {
511
+ if (sessionId && transports[sessionId]) {
512
+ transport = transports[sessionId];
513
+ logger.debug(`Reusing session: ${sessionId}`);
514
+ } else if (!sessionId && isInitializeRequest(req.body)) {
515
+ logger.info("Creating new MCP session...");
516
+ transport = new StreamableHTTPServerTransport({
517
+ sessionIdGenerator: /* @__PURE__ */ __name(() => (0, import_node_crypto.randomUUID)(), "sessionIdGenerator"),
518
+ onsessioninitialized: /* @__PURE__ */ __name((newSessionId) => {
519
+ transports[newSessionId] = transport;
520
+ logger.info(`Session initialized: ${newSessionId}`);
521
+ }, "onsessioninitialized")
522
+ });
523
+ transport.onclose = () => {
524
+ if (transport.sessionId) {
525
+ delete transports[transport.sessionId];
526
+ logger.debug(`Session cleaned up: ${transport.sessionId}`);
527
+ }
528
+ };
529
+ const server = await serverFactory();
530
+ await server.connect(transport);
531
+ } else {
532
+ res.status(400).json({
533
+ jsonrpc: "2.0",
534
+ error: {
535
+ code: -32e3,
536
+ message: "Bad Request: Invalid session or not an init request"
537
+ },
538
+ id: null
539
+ });
540
+ return;
541
+ }
542
+ await transport.handleRequest(req, res, req.body);
543
+ } catch (error) {
544
+ logger.error("Error handling MCP request:", error);
545
+ if (!res.headersSent) {
546
+ res.status(500).json({
547
+ jsonrpc: "2.0",
548
+ error: {
549
+ code: -32603,
550
+ message: "Internal server error"
551
+ },
552
+ id: null
553
+ });
554
+ }
555
+ }
556
+ }, "handleMCPRequest");
557
+ app.post("/mcp", handleMCPRequest);
558
+ 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
+ });
571
+ });
572
+ }
573
+ __name(createHTTPServer, "createHTTPServer");
574
+
575
+ // 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: {}
601
+ }
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: {}
615
+ }
616
+ });
617
+ }
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)}`);
634
+ }
635
+ }
636
+ try {
637
+ const result = await tool.method.call(tool.instance, request.params.arguments);
638
+ let formattedResult = result;
639
+ if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
640
+ formattedResult = result;
641
+ } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
642
+ formattedResult = JSON.stringify(result, null, 2);
643
+ } else {
644
+ formattedResult = String(result);
645
+ }
646
+ return {
647
+ content: [
648
+ {
649
+ type: "text",
650
+ text: formattedResult
651
+ }
652
+ ]
653
+ };
654
+ } catch (error) {
655
+ return {
656
+ content: [
657
+ {
658
+ type: "text",
659
+ text: `Error: ${error.message}`
660
+ }
661
+ ],
662
+ isError: true
663
+ };
664
+ }
665
+ });
666
+ this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
667
+ const resources = [];
668
+ for (const [uri, resource] of this.resources.entries()) {
669
+ const resourceInfo = {
670
+ uri: resource.uri,
671
+ name: resource.name,
672
+ description: resource.description,
673
+ mimeType: resource.mimeType
674
+ };
675
+ if (resource.inputSchema) {
676
+ resourceInfo.inputSchema = resource.inputSchema;
677
+ }
678
+ resources.push(resourceInfo);
679
+ }
680
+ return {
681
+ resources
682
+ };
683
+ });
684
+ this.server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
685
+ const uri = request.params.uri;
686
+ const resource = this.resources.get(uri);
687
+ if (!resource) {
688
+ throw new Error(`Resource ${uri} not found`);
689
+ }
690
+ try {
691
+ const result = await resource.method.call(resource.instance);
692
+ return {
693
+ contents: [
694
+ {
695
+ uri,
696
+ mimeType: resource.mimeType,
697
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
698
+ }
699
+ ]
700
+ };
701
+ } catch (error) {
702
+ throw new Error(`Failed to read resource ${uri}: ${error.message}`);
703
+ }
704
+ });
705
+ this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
706
+ const prompts = [];
707
+ for (const [name, prompt] of this.prompts.entries()) {
708
+ prompts.push({
709
+ name,
710
+ description: prompt.description,
711
+ arguments: prompt.arguments
712
+ });
713
+ }
714
+ return {
715
+ prompts
716
+ };
717
+ });
718
+ this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
719
+ const promptName = request.params.name;
720
+ const prompt = this.prompts.get(promptName);
721
+ if (!prompt) {
722
+ throw new Error(`Prompt ${promptName} not found`);
723
+ }
724
+ try {
725
+ const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
726
+ if (result && result.messages) {
727
+ return result;
728
+ }
729
+ return {
730
+ description: prompt.description,
731
+ messages: [
732
+ {
733
+ role: "user",
734
+ content: {
735
+ type: "text",
736
+ text: typeof result === "string" ? result : JSON.stringify(result)
737
+ }
738
+ }
739
+ ]
740
+ };
741
+ } catch (error) {
742
+ throw new Error(`Failed to get prompt ${promptName}: ${error.message}`);
743
+ }
744
+ });
745
+ }
746
+ /**
747
+ * Register a service instance with decorated methods
748
+ */
749
+ registerService(instance) {
750
+ const cls = instance.constructor;
751
+ const toolMethods = getDecoratedMethods(cls, "tool:name");
752
+ for (const { method, propertyKey } of toolMethods) {
753
+ const methodMeta = getMethodMetadata(method);
754
+ const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
755
+ let inputSchema = methodMeta.inputSchema;
756
+ if (inputClass) {
757
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
758
+ }
759
+ this.tools.set(methodMeta.toolName, {
760
+ name: methodMeta.toolName,
761
+ description: methodMeta.toolDescription || "",
762
+ inputSchema,
763
+ method,
764
+ instance,
765
+ propertyKey
766
+ });
767
+ if (this.logging) {
768
+ this.logger.info(`Registered tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
769
+ }
770
+ }
771
+ const promptMethods = getDecoratedMethods(cls, "prompt:name");
772
+ for (const { method, propertyKey } of promptMethods) {
773
+ const methodMeta = getMethodMetadata(method);
774
+ const inputClass = Reflect.getMetadata?.("prompt:inputClass", method);
775
+ let inputSchema = methodMeta.inputSchema;
776
+ if (inputClass) {
777
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
778
+ }
779
+ const promptArgs = inputSchema?.properties ? Object.keys(inputSchema.properties).map((key) => ({
780
+ name: key,
781
+ description: inputSchema?.properties?.[key]?.description || "",
782
+ required: inputSchema?.required?.includes(key) || false
783
+ })) : [];
784
+ this.prompts.set(methodMeta.promptName, {
785
+ name: methodMeta.promptName,
786
+ description: methodMeta.promptDescription || "",
787
+ arguments: promptArgs,
788
+ method,
789
+ instance,
790
+ propertyKey
791
+ });
792
+ if (this.logging) {
793
+ this.logger.info(`Registered prompt: ${methodMeta.promptName}`);
794
+ }
795
+ }
796
+ const resourceMethods = getDecoratedMethods(cls, "resource:uri");
797
+ for (const { method, propertyKey } of resourceMethods) {
798
+ const methodMeta = getMethodMetadata(method);
799
+ const inputClass = Reflect.getMetadata?.("resource:inputClass", method);
800
+ let inputSchema = methodMeta.inputSchema;
801
+ if (inputClass) {
802
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
803
+ }
804
+ const mimeType = Reflect.getMetadata?.("resource:mimeType", method) || "application/json";
805
+ this.resources.set(methodMeta.resourceUri, {
806
+ uri: methodMeta.resourceUri,
807
+ name: methodMeta.resourceName || methodMeta.resourceUri,
808
+ description: methodMeta.resourceDescription || "",
809
+ mimeType,
810
+ inputSchema,
811
+ method,
812
+ instance,
813
+ propertyKey
814
+ });
815
+ if (this.logging) {
816
+ this.logger.info(`Registered resource: ${methodMeta.resourceUri}`);
817
+ }
818
+ }
819
+ }
820
+ /**
821
+ * Get the underlying MCP SDK Server instance
822
+ */
823
+ getServer() {
824
+ return this.server;
825
+ }
826
+ };
827
+ var MCPServerRuntime = class {
828
+ static {
829
+ __name(this, "MCPServerRuntime");
830
+ }
831
+ server;
832
+ tools = /* @__PURE__ */ new Map();
833
+ prompts = /* @__PURE__ */ new Map();
834
+ resources = /* @__PURE__ */ new Map();
835
+ options;
836
+ logger;
837
+ constructor(options) {
838
+ this.options = options;
839
+ this.logger = new Logger({
840
+ level: this.options.logging ? LogLevel.INFO : LogLevel.NONE,
841
+ prefix: "MCPServerRuntime"
842
+ });
843
+ this.server = new import_server.Server({
844
+ name: "leanmcp-server",
845
+ version: "0.1.0"
846
+ }, {
847
+ capabilities: {
848
+ tools: {},
849
+ resources: {},
850
+ prompts: {}
851
+ }
852
+ });
853
+ this.setupHandlers();
854
+ }
855
+ setupHandlers() {
856
+ this.server.setRequestHandler(import_types.ListToolsRequestSchema, async () => {
857
+ const tools = [];
858
+ for (const [name, tool] of this.tools.entries()) {
859
+ tools.push({
860
+ name,
861
+ description: tool.description,
862
+ inputSchema: tool.inputSchema || {
863
+ type: "object",
864
+ properties: {}
865
+ }
866
+ });
867
+ }
868
+ return {
869
+ tools
870
+ };
871
+ });
872
+ this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
873
+ const toolName = request.params.name;
874
+ const tool = this.tools.get(toolName);
875
+ if (!tool) {
876
+ throw new Error(`Tool ${toolName} not found`);
877
+ }
878
+ const methodMeta = getMethodMetadata(tool.method);
879
+ if (methodMeta.inputSchema) {
880
+ const validate = ajv.compile(methodMeta.inputSchema);
881
+ const valid = validate(request.params.arguments || {});
882
+ if (!valid) {
883
+ throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
884
+ }
885
+ }
886
+ if (methodMeta.authRequired) {
887
+ if (this.options.logging) {
888
+ this.logger.info(`Auth required for ${toolName} (provider: ${methodMeta.authProvider})`);
889
+ }
890
+ }
891
+ try {
892
+ const result = await tool.method.call(tool.instance, request.params.arguments);
893
+ if (result && typeof result === "object" && result.type === "elicitation") {
894
+ return {
895
+ content: [
896
+ {
897
+ type: "text",
898
+ text: JSON.stringify(result, null, 2)
899
+ }
900
+ ],
901
+ isError: false
902
+ };
903
+ }
904
+ let formattedResult = result;
905
+ if (methodMeta.renderFormat === "markdown" && typeof result === "string") {
906
+ formattedResult = result;
907
+ } else if (methodMeta.renderFormat === "json" || typeof result === "object") {
908
+ formattedResult = JSON.stringify(result, null, 2);
909
+ } else {
910
+ formattedResult = String(result);
911
+ }
912
+ return {
913
+ content: [
914
+ {
915
+ type: "text",
916
+ text: formattedResult
917
+ }
918
+ ]
919
+ };
920
+ } catch (error) {
921
+ return {
922
+ content: [
923
+ {
924
+ type: "text",
925
+ text: `Error: ${error.message}`
926
+ }
927
+ ],
928
+ isError: true
929
+ };
930
+ }
931
+ });
932
+ this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
933
+ const resources = [];
934
+ for (const [uri, resource] of this.resources.entries()) {
935
+ resources.push({
936
+ uri: resource.uri,
937
+ name: resource.name,
938
+ description: resource.description,
939
+ mimeType: resource.mimeType
940
+ });
941
+ }
942
+ return {
943
+ resources
944
+ };
945
+ });
946
+ this.server.setRequestHandler(import_types.ReadResourceRequestSchema, async (request) => {
947
+ const uri = request.params.uri;
948
+ const resource = this.resources.get(uri);
949
+ if (!resource) {
950
+ throw new Error(`Resource ${uri} not found`);
951
+ }
952
+ try {
953
+ const result = await resource.method.call(resource.instance);
954
+ return {
955
+ contents: [
956
+ {
957
+ uri,
958
+ mimeType: resource.mimeType,
959
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
960
+ }
961
+ ]
962
+ };
963
+ } catch (error) {
964
+ throw new Error(`Failed to read resource ${uri}: ${error.message}`);
965
+ }
966
+ });
967
+ this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
968
+ const prompts = [];
969
+ for (const [name, prompt] of this.prompts.entries()) {
970
+ prompts.push({
971
+ name,
972
+ description: prompt.description,
973
+ arguments: prompt.arguments
974
+ });
975
+ }
976
+ return {
977
+ prompts
978
+ };
979
+ });
980
+ this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
981
+ const promptName = request.params.name;
982
+ const prompt = this.prompts.get(promptName);
983
+ if (!prompt) {
984
+ throw new Error(`Prompt ${promptName} not found`);
985
+ }
986
+ try {
987
+ const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
988
+ if (result && result.messages) {
989
+ return result;
990
+ }
991
+ return {
992
+ description: prompt.description,
993
+ messages: [
994
+ {
995
+ role: "user",
996
+ content: {
997
+ type: "text",
998
+ text: typeof result === "string" ? result : JSON.stringify(result)
999
+ }
1000
+ }
1001
+ ]
1002
+ };
1003
+ } catch (error) {
1004
+ throw new Error(`Failed to get prompt ${promptName}: ${error.message}`);
1005
+ }
1006
+ });
1007
+ }
1008
+ async loadServices() {
1009
+ const absPath = import_path.default.resolve(this.options.servicesDir);
1010
+ if (!import_fs.default.existsSync(absPath)) {
1011
+ this.logger.error(`Services directory not found: ${absPath}`);
1012
+ return;
1013
+ }
1014
+ const files = import_fs.default.readdirSync(absPath);
1015
+ let toolCount = 0;
1016
+ let promptCount = 0;
1017
+ let resourceCount = 0;
1018
+ for (const dir of files) {
1019
+ const modulePath = import_path.default.join(absPath, dir, "index.ts");
1020
+ const modulePathJs = import_path.default.join(absPath, dir, "index.js");
1021
+ const finalPath = import_fs.default.existsSync(modulePath) ? modulePath : import_fs.default.existsSync(modulePathJs) ? modulePathJs : null;
1022
+ if (finalPath) {
1023
+ try {
1024
+ const fileUrl = (0, import_url.pathToFileURL)(finalPath).href;
1025
+ const mod = await import(fileUrl);
1026
+ const exportedClasses = Object.values(mod).filter((val) => typeof val === "function" && val.prototype);
1027
+ for (const cls of exportedClasses) {
1028
+ const instance = new cls();
1029
+ const envsPropKey = Reflect.getMetadata?.("userenvs:propertyKey", cls);
1030
+ if (envsPropKey) {
1031
+ instance[envsPropKey] = process.env;
1032
+ }
1033
+ const toolMethods = getDecoratedMethods(cls, "tool:name");
1034
+ for (const { method, propertyKey, metadata } of toolMethods) {
1035
+ const methodMeta = getMethodMetadata(method);
1036
+ const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
1037
+ const outputClass = Reflect.getMetadata?.("tool:outputClass", method);
1038
+ let inputSchema = methodMeta.inputSchema;
1039
+ if (inputClass) {
1040
+ inputSchema = classToJsonSchemaWithConstraints(inputClass);
1041
+ }
1042
+ this.tools.set(methodMeta.toolName, {
1043
+ name: methodMeta.toolName,
1044
+ description: methodMeta.toolDescription || "",
1045
+ inputSchema,
1046
+ method,
1047
+ instance,
1048
+ propertyKey
1049
+ });
1050
+ toolCount++;
1051
+ if (this.options.logging) {
1052
+ this.logger.info(`Loaded tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
1053
+ }
1054
+ }
1055
+ const promptMethods = getDecoratedMethods(cls, "prompt:name");
1056
+ for (const { method, propertyKey, metadata } of promptMethods) {
1057
+ const methodMeta = getMethodMetadata(method);
1058
+ const promptArgs = methodMeta.inputSchema?.properties ? Object.keys(methodMeta.inputSchema.properties).map((key) => ({
1059
+ name: key,
1060
+ description: methodMeta.inputSchema?.properties?.[key]?.description || "",
1061
+ required: methodMeta.inputSchema?.required?.includes(key) || false
1062
+ })) : [];
1063
+ this.prompts.set(methodMeta.promptName, {
1064
+ name: methodMeta.promptName,
1065
+ description: methodMeta.promptDescription || "",
1066
+ arguments: promptArgs,
1067
+ method,
1068
+ instance,
1069
+ propertyKey
1070
+ });
1071
+ promptCount++;
1072
+ if (this.options.logging) {
1073
+ this.logger.info(`Loaded prompt: ${methodMeta.promptName}`);
1074
+ }
1075
+ }
1076
+ const resourceMethods = getDecoratedMethods(cls, "resource:uri");
1077
+ for (const { method, propertyKey, metadata } of resourceMethods) {
1078
+ const methodMeta = getMethodMetadata(method);
1079
+ this.resources.set(methodMeta.resourceUri, {
1080
+ uri: methodMeta.resourceUri,
1081
+ name: methodMeta.resourceName || methodMeta.resourceUri,
1082
+ description: methodMeta.resourceDescription || "",
1083
+ mimeType: "application/json",
1084
+ method,
1085
+ instance,
1086
+ propertyKey
1087
+ });
1088
+ resourceCount++;
1089
+ if (this.options.logging) {
1090
+ this.logger.info(`Loaded resource: ${methodMeta.resourceUri}`);
1091
+ }
1092
+ }
1093
+ }
1094
+ } catch (error) {
1095
+ this.logger.error(`Failed to load from ${dir}:`, error.message || error);
1096
+ if (this.options.logging) {
1097
+ this.logger.error("Full error:", error);
1098
+ }
1099
+ }
1100
+ }
1101
+ }
1102
+ if (this.options.logging) {
1103
+ this.logger.info(`
1104
+ Loaded ${toolCount} tools, ${promptCount} prompts, ${resourceCount} resources`);
1105
+ }
1106
+ }
1107
+ async start() {
1108
+ await this.loadServices();
1109
+ const transport = new import_stdio.StdioServerTransport();
1110
+ await this.server.connect(transport);
1111
+ if (this.options.logging) {
1112
+ this.logger.info("LeanMCP server running on stdio");
1113
+ }
1114
+ }
1115
+ getServer() {
1116
+ return this.server;
1117
+ }
1118
+ getTools() {
1119
+ return Array.from(this.tools.values());
1120
+ }
1121
+ getPrompts() {
1122
+ return Array.from(this.prompts.values());
1123
+ }
1124
+ getResources() {
1125
+ return Array.from(this.resources.values());
1126
+ }
1127
+ };
1128
+ async function startMCPServer(options) {
1129
+ const runtime = new MCPServerRuntime(options);
1130
+ await runtime.start();
1131
+ return runtime;
1132
+ }
1133
+ __name(startMCPServer, "startMCPServer");
1134
+ // Annotate the CommonJS export names for ESM import in node:
1135
+ 0 && (module.exports = {
1136
+ Auth,
1137
+ Deprecated,
1138
+ LogLevel,
1139
+ Logger,
1140
+ MCPServer,
1141
+ MCPServerRuntime,
1142
+ Optional,
1143
+ Prompt,
1144
+ Render,
1145
+ Resource,
1146
+ SchemaConstraint,
1147
+ Tool,
1148
+ UI,
1149
+ UserEnvs,
1150
+ classToJsonSchema,
1151
+ classToJsonSchemaWithConstraints,
1152
+ createHTTPServer,
1153
+ defaultLogger,
1154
+ getDecoratedMethods,
1155
+ getMethodMetadata,
1156
+ startMCPServer,
1157
+ validateNonEmpty,
1158
+ validatePath,
1159
+ validatePort,
1160
+ validateServiceName,
1161
+ validateUrl
1162
+ });