@leanmcp/core 0.2.0 → 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/README.md +82 -54
- package/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +875 -812
- package/dist/index.mjs +28 -9
- package/package.json +7 -7
- package/dist/chunk-O6YSETKJ.mjs +0 -6
- package/dist/dist-LQ4M5W3M.mjs +0 -587
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
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
|
|
452
|
-
|
|
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,20 +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
|
-
|
|
464
|
+
httpOptions.cors ? import("cors").catch(() => null) : Promise.resolve(null)
|
|
464
465
|
]);
|
|
465
466
|
const app = express.default();
|
|
466
|
-
const port =
|
|
467
|
+
const port = httpOptions.port || 3001;
|
|
467
468
|
validatePort(port);
|
|
468
469
|
const transports = {};
|
|
469
470
|
let mcpServer = null;
|
|
470
|
-
const logger =
|
|
471
|
-
level:
|
|
471
|
+
const logger = httpOptions.logger || new Logger({
|
|
472
|
+
level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
|
|
472
473
|
prefix: "HTTP"
|
|
473
474
|
});
|
|
474
|
-
if (cors &&
|
|
475
|
-
const corsOptions = typeof
|
|
476
|
-
origin:
|
|
475
|
+
if (cors && httpOptions.cors) {
|
|
476
|
+
const corsOptions = typeof httpOptions.cors === "object" ? {
|
|
477
|
+
origin: httpOptions.cors.origin || false,
|
|
477
478
|
methods: [
|
|
478
479
|
"GET",
|
|
479
480
|
"POST",
|
|
@@ -489,7 +490,7 @@ async function createHTTPServer(serverFactory, options = {}) {
|
|
|
489
490
|
exposedHeaders: [
|
|
490
491
|
"mcp-session-id"
|
|
491
492
|
],
|
|
492
|
-
credentials:
|
|
493
|
+
credentials: httpOptions.cors.credentials ?? false,
|
|
493
494
|
maxAge: 86400
|
|
494
495
|
} : false;
|
|
495
496
|
if (corsOptions) {
|
|
@@ -606,736 +607,798 @@ async function createHTTPServer(serverFactory, options = {}) {
|
|
|
606
607
|
}
|
|
607
608
|
});
|
|
608
609
|
}
|
|
609
|
-
|
|
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
|
+
});
|
|
610
621
|
|
|
611
622
|
// src/index.ts
|
|
612
|
-
var
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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");
|
|
645
681
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
* This is called internally by createHTTPServer
|
|
662
|
-
*/
|
|
663
|
-
async waitForInit() {
|
|
664
|
-
await this.initPromise;
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Automatically discover and register services from the mcp directory
|
|
668
|
-
* Called by init() unless autoDiscover is set to false
|
|
669
|
-
*/
|
|
670
|
-
async autoDiscoverServices(customMcpDir, serviceFactories) {
|
|
671
|
-
if (this.autoDiscovered) return;
|
|
672
|
-
this.autoDiscovered = true;
|
|
673
|
-
try {
|
|
674
|
-
let mcpDir;
|
|
675
|
-
if (customMcpDir) {
|
|
676
|
-
mcpDir = customMcpDir;
|
|
677
|
-
} else {
|
|
678
|
-
const callerFile = this.getCallerFile();
|
|
679
|
-
if (callerFile) {
|
|
680
|
-
const callerDir = import_path.default.dirname(callerFile);
|
|
681
|
-
mcpDir = import_path.default.join(callerDir, "mcp");
|
|
682
|
-
} else {
|
|
683
|
-
mcpDir = import_path.default.join(process.cwd(), "mcp");
|
|
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;
|
|
684
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: {}
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
this.setupHandlers();
|
|
713
|
+
this.initPromise = this.autoInit();
|
|
685
714
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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);
|
|
722
|
+
}
|
|
691
723
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if (
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
724
|
+
/**
|
|
725
|
+
* Wait for initialization to complete
|
|
726
|
+
* This is called internally by createHTTPServer
|
|
727
|
+
*/
|
|
728
|
+
async waitForInit() {
|
|
729
|
+
await this.initPromise;
|
|
730
|
+
}
|
|
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");
|
|
712
749
|
}
|
|
713
750
|
}
|
|
714
|
-
|
|
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}`);
|
|
715
759
|
}
|
|
716
760
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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;
|
|
780
|
+
}
|
|
732
781
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
tools
|
|
737
|
-
};
|
|
738
|
-
});
|
|
739
|
-
this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
740
|
-
const toolName = request.params.name;
|
|
741
|
-
const tool = this.tools.get(toolName);
|
|
742
|
-
if (!tool) {
|
|
743
|
-
throw new Error(`Tool ${toolName} not found`);
|
|
744
|
-
}
|
|
745
|
-
const methodMeta = getMethodMetadata(tool.method);
|
|
746
|
-
if (methodMeta.inputSchema) {
|
|
747
|
-
const validate = ajv.compile(methodMeta.inputSchema);
|
|
748
|
-
const valid = validate(request.params.arguments || {});
|
|
749
|
-
if (!valid) {
|
|
750
|
-
throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
|
|
782
|
+
return null;
|
|
783
|
+
} finally {
|
|
784
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
751
785
|
}
|
|
752
786
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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: {}
|
|
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)}`);
|
|
769
816
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
{
|
|
776
|
-
|
|
777
|
-
|
|
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);
|
|
778
828
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
return {
|
|
811
|
-
contents: [
|
|
812
|
-
{
|
|
813
|
-
uri,
|
|
814
|
-
mimeType: resource.mimeType,
|
|
815
|
-
text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
|
|
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;
|
|
816
860
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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}`);
|
|
926
|
+
}
|
|
830
927
|
});
|
|
831
928
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
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
|
+
}
|
|
961
|
+
}
|
|
841
962
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
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
|
+
}
|
|
846
980
|
}
|
|
847
|
-
return
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
981
|
+
return files;
|
|
982
|
+
}
|
|
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();
|
|
855
1000
|
}
|
|
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}`);
|
|
856
1006
|
}
|
|
857
|
-
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Auto-register all services from the mcp directory
|
|
866
|
-
* Scans the directory recursively and registers all exported classes
|
|
867
|
-
*
|
|
868
|
-
* @param mcpDir - Path to the mcp directory containing service files
|
|
869
|
-
* @param serviceFactories - Optional map of service class names to factory functions for dependency injection
|
|
870
|
-
*
|
|
871
|
-
* @example
|
|
872
|
-
* // Auto-register services with no dependencies
|
|
873
|
-
* await server.autoRegisterServices('./mcp');
|
|
874
|
-
*
|
|
875
|
-
* @example
|
|
876
|
-
* // Auto-register with dependency injection
|
|
877
|
-
* await server.autoRegisterServices('./mcp', {
|
|
878
|
-
* SlackService: () => new SlackService(process.env.SLACK_TOKEN),
|
|
879
|
-
* AuthService: () => new AuthService(authProvider)
|
|
880
|
-
* });
|
|
881
|
-
*/
|
|
882
|
-
async autoRegisterServices(mcpDir, serviceFactories) {
|
|
883
|
-
this.logger.debug(`Auto-registering services from: ${mcpDir}`);
|
|
884
|
-
if (!import_fs.default.existsSync(mcpDir)) {
|
|
885
|
-
this.logger.warn(`MCP directory not found: ${mcpDir}`);
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
const serviceFiles = this.findServiceFiles(mcpDir);
|
|
889
|
-
this.logger.debug(`Found ${serviceFiles.length} service file(s)`);
|
|
890
|
-
for (const filePath of serviceFiles) {
|
|
891
|
-
try {
|
|
892
|
-
await this.loadAndRegisterService(filePath, serviceFactories);
|
|
893
|
-
} catch (error) {
|
|
894
|
-
this.logger.error(`Failed to load service from ${filePath}: ${error.message}`);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* Recursively find all index.ts/index.js files in the mcp directory
|
|
900
|
-
*/
|
|
901
|
-
findServiceFiles(dir) {
|
|
902
|
-
const files = [];
|
|
903
|
-
const entries = import_fs.default.readdirSync(dir, {
|
|
904
|
-
withFileTypes: true
|
|
905
|
-
});
|
|
906
|
-
for (const entry of entries) {
|
|
907
|
-
const fullPath = import_path.default.join(dir, entry.name);
|
|
908
|
-
if (entry.isDirectory()) {
|
|
909
|
-
files.push(...this.findServiceFiles(fullPath));
|
|
910
|
-
} else if (entry.isFile()) {
|
|
911
|
-
if (entry.name === "index.ts" || entry.name === "index.js") {
|
|
912
|
-
files.push(fullPath);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (registeredCount === 0) {
|
|
1010
|
+
this.logger.warn(`No services registered from ${filePath}`);
|
|
913
1011
|
}
|
|
914
1012
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
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}`);
|
|
935
1084
|
}
|
|
936
|
-
this.registerService(instance);
|
|
937
|
-
registeredCount++;
|
|
938
|
-
this.logger.debug(`Registered service: ${exportName} from ${import_path.default.basename(filePath)}`);
|
|
939
|
-
} catch (error) {
|
|
940
|
-
this.logger.warn(`Skipped ${exportName}: ${error.message}`);
|
|
941
1085
|
}
|
|
942
1086
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
*/
|
|
951
|
-
registerService(instance) {
|
|
952
|
-
const cls = instance.constructor;
|
|
953
|
-
const toolMethods = getDecoratedMethods(cls, "tool:name");
|
|
954
|
-
for (const { method, propertyKey } of toolMethods) {
|
|
955
|
-
const methodMeta = getMethodMetadata(method);
|
|
956
|
-
const inputClass = Reflect.getMetadata?.("tool:inputClass", method);
|
|
957
|
-
let inputSchema = methodMeta.inputSchema;
|
|
958
|
-
if (inputClass) {
|
|
959
|
-
inputSchema = classToJsonSchemaWithConstraints(inputClass);
|
|
960
|
-
}
|
|
961
|
-
this.tools.set(methodMeta.toolName, {
|
|
962
|
-
name: methodMeta.toolName,
|
|
963
|
-
description: methodMeta.toolDescription || "",
|
|
964
|
-
inputSchema,
|
|
965
|
-
method,
|
|
966
|
-
instance,
|
|
967
|
-
propertyKey
|
|
968
|
-
});
|
|
969
|
-
if (this.logging) {
|
|
970
|
-
this.logger.debug(`Registered tool: ${methodMeta.toolName}${inputClass ? " (class-based schema)" : ""}`);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
const promptMethods = getDecoratedMethods(cls, "prompt:name");
|
|
974
|
-
for (const { method, propertyKey } of promptMethods) {
|
|
975
|
-
const methodMeta = getMethodMetadata(method);
|
|
976
|
-
const inputClass = Reflect.getMetadata?.("prompt:inputClass", method);
|
|
977
|
-
let inputSchema = methodMeta.inputSchema;
|
|
978
|
-
if (inputClass) {
|
|
979
|
-
inputSchema = classToJsonSchemaWithConstraints(inputClass);
|
|
980
|
-
}
|
|
981
|
-
const promptArgs = inputSchema?.properties ? Object.keys(inputSchema.properties).map((key) => ({
|
|
982
|
-
name: key,
|
|
983
|
-
description: inputSchema?.properties?.[key]?.description || "",
|
|
984
|
-
required: inputSchema?.required?.includes(key) || false
|
|
985
|
-
})) : [];
|
|
986
|
-
this.prompts.set(methodMeta.promptName, {
|
|
987
|
-
name: methodMeta.promptName,
|
|
988
|
-
description: methodMeta.promptDescription || "",
|
|
989
|
-
arguments: promptArgs,
|
|
990
|
-
method,
|
|
991
|
-
instance,
|
|
992
|
-
propertyKey
|
|
993
|
-
});
|
|
994
|
-
if (this.logging) {
|
|
995
|
-
this.logger.debug(`Registered prompt: ${methodMeta.promptName}`);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
const resourceMethods = getDecoratedMethods(cls, "resource:uri");
|
|
999
|
-
for (const { method, propertyKey } of resourceMethods) {
|
|
1000
|
-
const methodMeta = getMethodMetadata(method);
|
|
1001
|
-
const inputClass = Reflect.getMetadata?.("resource:inputClass", method);
|
|
1002
|
-
let inputSchema = methodMeta.inputSchema;
|
|
1003
|
-
if (inputClass) {
|
|
1004
|
-
inputSchema = classToJsonSchemaWithConstraints(inputClass);
|
|
1005
|
-
}
|
|
1006
|
-
const mimeType = Reflect.getMetadata?.("resource:mimeType", method) || "application/json";
|
|
1007
|
-
this.resources.set(methodMeta.resourceUri, {
|
|
1008
|
-
uri: methodMeta.resourceUri,
|
|
1009
|
-
name: methodMeta.resourceName || methodMeta.resourceUri,
|
|
1010
|
-
description: methodMeta.resourceDescription || "",
|
|
1011
|
-
mimeType,
|
|
1012
|
-
inputSchema,
|
|
1013
|
-
method,
|
|
1014
|
-
instance,
|
|
1015
|
-
propertyKey
|
|
1016
|
-
});
|
|
1017
|
-
if (this.logging) {
|
|
1018
|
-
this.logger.debug(`Registered resource: ${methodMeta.resourceUri}`);
|
|
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;
|
|
1019
1094
|
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
* Attaches waitForInit method for HTTP server initialization
|
|
1025
|
-
*/
|
|
1026
|
-
getServer() {
|
|
1027
|
-
this.server.waitForInit = () => this.waitForInit();
|
|
1028
|
-
return this.server;
|
|
1029
|
-
}
|
|
1030
|
-
};
|
|
1031
|
-
var MCPServerRuntime = class {
|
|
1032
|
-
static {
|
|
1033
|
-
__name(this, "MCPServerRuntime");
|
|
1034
|
-
}
|
|
1035
|
-
server;
|
|
1036
|
-
tools = /* @__PURE__ */ new Map();
|
|
1037
|
-
prompts = /* @__PURE__ */ new Map();
|
|
1038
|
-
resources = /* @__PURE__ */ new Map();
|
|
1039
|
-
options;
|
|
1040
|
-
logger;
|
|
1041
|
-
constructor(options) {
|
|
1042
|
-
this.options = options;
|
|
1043
|
-
this.logger = new Logger({
|
|
1044
|
-
level: this.options.logging ? LogLevel.INFO : LogLevel.NONE,
|
|
1045
|
-
prefix: "MCPServerRuntime"
|
|
1046
|
-
});
|
|
1047
|
-
this.server = new import_server.Server({
|
|
1048
|
-
name: "leanmcp-server",
|
|
1049
|
-
version: "0.1.0"
|
|
1050
|
-
}, {
|
|
1051
|
-
capabilities: {
|
|
1052
|
-
tools: {},
|
|
1053
|
-
resources: {},
|
|
1054
|
-
prompts: {}
|
|
1095
|
+
};
|
|
1096
|
+
MCPServerRuntime = class {
|
|
1097
|
+
static {
|
|
1098
|
+
__name(this, "MCPServerRuntime");
|
|
1055
1099
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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"
|
|
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: {}
|
|
1069
1120
|
}
|
|
1070
1121
|
});
|
|
1122
|
+
this.setupHandlers();
|
|
1071
1123
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
const methodMeta = getMethodMetadata(tool.method);
|
|
1083
|
-
if (methodMeta.inputSchema) {
|
|
1084
|
-
const validate = ajv.compile(methodMeta.inputSchema);
|
|
1085
|
-
const valid = validate(request.params.arguments || {});
|
|
1086
|
-
if (!valid) {
|
|
1087
|
-
throw new Error(`Input validation failed: ${JSON.stringify(validate.errors)}`);
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
if (methodMeta.authRequired) {
|
|
1091
|
-
if (this.options.logging) {
|
|
1092
|
-
this.logger.info(`Auth required for ${toolName} (provider: ${methodMeta.authProvider})`);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
try {
|
|
1096
|
-
const meta = request.params._meta;
|
|
1097
|
-
const result = await tool.method.call(tool.instance, request.params.arguments, meta);
|
|
1098
|
-
if (result && typeof result === "object" && result.type === "elicitation") {
|
|
1099
|
-
return {
|
|
1100
|
-
content: [
|
|
1101
|
-
{
|
|
1102
|
-
type: "text",
|
|
1103
|
-
text: JSON.stringify(result, null, 2)
|
|
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: {}
|
|
1104
1134
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
return {
|
|
1138
|
+
tools
|
|
1107
1139
|
};
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
{
|
|
1120
|
-
|
|
1121
|
-
text: formattedResult
|
|
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)}`);
|
|
1122
1153
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
content: [
|
|
1128
|
-
{
|
|
1129
|
-
type: "text",
|
|
1130
|
-
text: `Error: ${error.message}`
|
|
1154
|
+
}
|
|
1155
|
+
if (methodMeta.authRequired) {
|
|
1156
|
+
if (this.options.logging) {
|
|
1157
|
+
this.logger.info(`Auth required for ${toolName} (provider: ${methodMeta.authProvider})`);
|
|
1131
1158
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
+
};
|
|
1173
|
+
}
|
|
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
|
+
}
|
|
1145
1201
|
});
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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;
|
|
1165
1260
|
}
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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
|
+
}
|
|
1179
1276
|
});
|
|
1180
1277
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
const promptName = request.params.name;
|
|
1187
|
-
const prompt = this.prompts.get(promptName);
|
|
1188
|
-
if (!prompt) {
|
|
1189
|
-
throw new Error(`Prompt ${promptName} not found`);
|
|
1190
|
-
}
|
|
1191
|
-
try {
|
|
1192
|
-
const result = await prompt.method.call(prompt.instance, request.params.arguments || {});
|
|
1193
|
-
if (result && result.messages) {
|
|
1194
|
-
return result;
|
|
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;
|
|
1195
1283
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
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
|
+
}
|
|
1246
1363
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
description: methodMeta.toolDescription || "",
|
|
1250
|
-
inputSchema,
|
|
1251
|
-
method,
|
|
1252
|
-
instance,
|
|
1253
|
-
propertyKey
|
|
1254
|
-
});
|
|
1255
|
-
toolCount++;
|
|
1364
|
+
} catch (error) {
|
|
1365
|
+
this.logger.error(`Failed to load from ${dir}:`, error.message || error);
|
|
1256
1366
|
if (this.options.logging) {
|
|
1257
|
-
this.logger.
|
|
1367
|
+
this.logger.error("Full error:", error);
|
|
1258
1368
|
}
|
|
1259
1369
|
}
|
|
1260
|
-
const promptMethods = getDecoratedMethods(cls, "prompt:name");
|
|
1261
|
-
for (const { method, propertyKey, metadata } of promptMethods) {
|
|
1262
|
-
const methodMeta = getMethodMetadata(method);
|
|
1263
|
-
const promptArgs = methodMeta.inputSchema?.properties ? Object.keys(methodMeta.inputSchema.properties).map((key) => ({
|
|
1264
|
-
name: key,
|
|
1265
|
-
description: methodMeta.inputSchema?.properties?.[key]?.description || "",
|
|
1266
|
-
required: methodMeta.inputSchema?.required?.includes(key) || false
|
|
1267
|
-
})) : [];
|
|
1268
|
-
this.prompts.set(methodMeta.promptName, {
|
|
1269
|
-
name: methodMeta.promptName,
|
|
1270
|
-
description: methodMeta.promptDescription || "",
|
|
1271
|
-
arguments: promptArgs,
|
|
1272
|
-
method,
|
|
1273
|
-
instance,
|
|
1274
|
-
propertyKey
|
|
1275
|
-
});
|
|
1276
|
-
promptCount++;
|
|
1277
|
-
if (this.options.logging) {
|
|
1278
|
-
this.logger.info(`Loaded prompt: ${methodMeta.promptName}`);
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
const resourceMethods = getDecoratedMethods(cls, "resource:uri");
|
|
1282
|
-
for (const { method, propertyKey, metadata } of resourceMethods) {
|
|
1283
|
-
const methodMeta = getMethodMetadata(method);
|
|
1284
|
-
this.resources.set(methodMeta.resourceUri, {
|
|
1285
|
-
uri: methodMeta.resourceUri,
|
|
1286
|
-
name: methodMeta.resourceName || methodMeta.resourceUri,
|
|
1287
|
-
description: methodMeta.resourceDescription || "",
|
|
1288
|
-
mimeType: "application/json",
|
|
1289
|
-
method,
|
|
1290
|
-
instance,
|
|
1291
|
-
propertyKey
|
|
1292
|
-
});
|
|
1293
|
-
resourceCount++;
|
|
1294
|
-
if (this.options.logging) {
|
|
1295
|
-
this.logger.info(`Loaded resource: ${methodMeta.resourceUri}`);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
} catch (error) {
|
|
1300
|
-
this.logger.error(`Failed to load from ${dir}:`, error.message || error);
|
|
1301
|
-
if (this.options.logging) {
|
|
1302
|
-
this.logger.error("Full error:", error);
|
|
1303
1370
|
}
|
|
1304
1371
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
if (this.options.logging) {
|
|
1308
|
-
this.logger.info(`
|
|
1372
|
+
if (this.options.logging) {
|
|
1373
|
+
this.logger.info(`
|
|
1309
1374
|
Loaded ${toolCount} tools, ${promptCount} prompts, ${resourceCount} resources`);
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
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");
|
|
1331
1399
|
}
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
const runtime = new MCPServerRuntime(options);
|
|
1335
|
-
await runtime.start();
|
|
1336
|
-
return runtime;
|
|
1337
|
-
}
|
|
1338
|
-
__name(startMCPServer, "startMCPServer");
|
|
1400
|
+
});
|
|
1401
|
+
init_index();
|
|
1339
1402
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1340
1403
|
0 && (module.exports = {
|
|
1341
1404
|
Auth,
|