@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/dist/index.mjs CHANGED
@@ -392,7 +392,26 @@ function isInitializeRequest(body) {
392
392
  return body && body.method === "initialize";
393
393
  }
394
394
  __name(isInitializeRequest, "isInitializeRequest");
395
- async function createHTTPServer(serverFactory, options = {}) {
395
+ async function createHTTPServer(serverInput, options) {
396
+ let serverFactory;
397
+ let httpOptions;
398
+ if (typeof serverInput === "function") {
399
+ serverFactory = serverInput;
400
+ httpOptions = options || {};
401
+ } else {
402
+ const serverOptions = serverInput;
403
+ const { MCPServer: MCPServer2 } = await import("./index.mjs");
404
+ serverFactory = /* @__PURE__ */ __name(async () => {
405
+ const mcpServer2 = new MCPServer2(serverOptions);
406
+ return mcpServer2.getServer();
407
+ }, "serverFactory");
408
+ httpOptions = {
409
+ port: serverOptions.port,
410
+ cors: serverOptions.cors,
411
+ logging: serverOptions.logging,
412
+ sessionTimeout: serverOptions.sessionTimeout
413
+ };
414
+ }
396
415
  const [express, { StreamableHTTPServerTransport }, cors] = await Promise.all([
397
416
  // @ts-ignore
398
417
  import("express").catch(() => {
@@ -403,20 +422,20 @@ async function createHTTPServer(serverFactory, options = {}) {
403
422
  throw new Error("MCP SDK not found. Install with: npm install @modelcontextprotocol/sdk");
404
423
  }),
405
424
  // @ts-ignore
406
- options.cors ? import("cors").catch(() => null) : Promise.resolve(null)
425
+ httpOptions.cors ? import("cors").catch(() => null) : Promise.resolve(null)
407
426
  ]);
408
427
  const app = express.default();
409
- const port = options.port || 3001;
428
+ const port = httpOptions.port || 3001;
410
429
  validatePort(port);
411
430
  const transports = {};
412
431
  let mcpServer = null;
413
- const logger = options.logger || new Logger({
414
- level: options.logging ? LogLevel.INFO : LogLevel.NONE,
432
+ const logger = httpOptions.logger || new Logger({
433
+ level: httpOptions.logging ? LogLevel.INFO : LogLevel.NONE,
415
434
  prefix: "HTTP"
416
435
  });
417
- if (cors && options.cors) {
418
- const corsOptions = typeof options.cors === "object" ? {
419
- origin: options.cors.origin || false,
436
+ if (cors && httpOptions.cors) {
437
+ const corsOptions = typeof httpOptions.cors === "object" ? {
438
+ origin: httpOptions.cors.origin || false,
420
439
  methods: [
421
440
  "GET",
422
441
  "POST",
@@ -432,7 +451,7 @@ async function createHTTPServer(serverFactory, options = {}) {
432
451
  exposedHeaders: [
433
452
  "mcp-session-id"
434
453
  ],
435
- credentials: options.cors.credentials ?? false,
454
+ credentials: httpOptions.cors.credentials ?? false,
436
455
  maxAge: 86400
437
456
  } : false;
438
457
  if (corsOptions) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanmcp/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Core library implementing decorators, reflection, and MCP runtime server",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -24,14 +24,14 @@
24
24
  "test:watch": "jest --watch"
25
25
  },
26
26
  "dependencies": {
27
- "reflect-metadata": "^0.2.1",
28
27
  "@modelcontextprotocol/sdk": "^1.0.0",
28
+ "ajv": "^8.12.0",
29
29
  "dotenv": "^16.3.1",
30
- "ajv": "^8.12.0"
30
+ "reflect-metadata": "^0.2.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "express": "^5.0.0",
34
- "cors": "^2.8.5"
33
+ "cors": "^2.8.5",
34
+ "express": "^5.0.0"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "express": {
@@ -42,9 +42,9 @@
42
42
  }
43
43
  },
44
44
  "devDependencies": {
45
- "@types/node": "^20.0.0",
45
+ "@types/cors": "^2.8.0",
46
46
  "@types/express": "^5.0.0",
47
- "@types/cors": "^2.8.0"
47
+ "@types/node": "^20.0.0"
48
48
  },
49
49
  "repository": {
50
50
  "type": "git",
@@ -1,6 +0,0 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
-
4
- export {
5
- __name
6
- };
@@ -1,587 +0,0 @@
1
- import {
2
- __name
3
- } from "./chunk-O6YSETKJ.mjs";
4
-
5
- // ../elicitation/dist/index.mjs
6
- import "reflect-metadata";
7
- import "reflect-metadata";
8
- import "reflect-metadata";
9
- var __defProp = Object.defineProperty;
10
- var __name2 = /* @__PURE__ */ __name((target, value) => __defProp(target, "name", { value, configurable: true }), "__name");
11
- var ElicitationError = class extends Error {
12
- static {
13
- __name(this, "ElicitationError");
14
- }
15
- static {
16
- __name2(this, "ElicitationError");
17
- }
18
- code;
19
- details;
20
- constructor(message, code, details) {
21
- super(message), this.code = code, this.details = details;
22
- this.name = "ElicitationError";
23
- }
24
- };
25
- var ElicitationStrategyBase = class {
26
- static {
27
- __name(this, "ElicitationStrategyBase");
28
- }
29
- static {
30
- __name2(this, "ElicitationStrategyBase");
31
- }
32
- /**
33
- * Validate user response against field definitions
34
- */
35
- validateResponse(response, fields) {
36
- const errors = [];
37
- for (const field of fields) {
38
- const value = response.values[field.name];
39
- if (field.required && (value === void 0 || value === null || value === "")) {
40
- errors.push({
41
- field: field.name,
42
- message: field.validation?.errorMessage || `${field.label} is required`
43
- });
44
- continue;
45
- }
46
- if (value === void 0 || value === null || value === "") {
47
- continue;
48
- }
49
- const typeError = this.validateFieldType(value, field);
50
- if (typeError) {
51
- errors.push(typeError);
52
- continue;
53
- }
54
- if (field.validation) {
55
- const validationError = this.validateField(value, field);
56
- if (validationError) {
57
- errors.push(validationError);
58
- }
59
- }
60
- }
61
- return errors;
62
- }
63
- /**
64
- * Validate field type
65
- */
66
- validateFieldType(value, field) {
67
- switch (field.type) {
68
- case "number":
69
- if (typeof value !== "number" && isNaN(Number(value))) {
70
- return {
71
- field: field.name,
72
- message: `${field.label} must be a number`
73
- };
74
- }
75
- break;
76
- case "boolean":
77
- if (typeof value !== "boolean") {
78
- return {
79
- field: field.name,
80
- message: `${field.label} must be true or false`
81
- };
82
- }
83
- break;
84
- case "email":
85
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
86
- if (!emailRegex.test(String(value))) {
87
- return {
88
- field: field.name,
89
- message: `${field.label} must be a valid email address`
90
- };
91
- }
92
- break;
93
- case "url":
94
- try {
95
- new URL(String(value));
96
- } catch {
97
- return {
98
- field: field.name,
99
- message: `${field.label} must be a valid URL`
100
- };
101
- }
102
- break;
103
- }
104
- return null;
105
- }
106
- /**
107
- * Validate field against validation rules
108
- */
109
- validateField(value, field) {
110
- const validation2 = field.validation;
111
- if (!validation2) return null;
112
- if (field.type === "number") {
113
- const numValue = Number(value);
114
- if (validation2.min !== void 0 && numValue < validation2.min) {
115
- return {
116
- field: field.name,
117
- message: validation2.errorMessage || `${field.label} must be at least ${validation2.min}`
118
- };
119
- }
120
- if (validation2.max !== void 0 && numValue > validation2.max) {
121
- return {
122
- field: field.name,
123
- message: validation2.errorMessage || `${field.label} must be at most ${validation2.max}`
124
- };
125
- }
126
- }
127
- if (field.type === "text" || field.type === "textarea" || field.type === "email" || field.type === "url") {
128
- const strValue = String(value);
129
- if (validation2.minLength !== void 0 && strValue.length < validation2.minLength) {
130
- return {
131
- field: field.name,
132
- message: validation2.errorMessage || `${field.label} must be at least ${validation2.minLength} characters`
133
- };
134
- }
135
- if (validation2.maxLength !== void 0 && strValue.length > validation2.maxLength) {
136
- return {
137
- field: field.name,
138
- message: validation2.errorMessage || `${field.label} must be at most ${validation2.maxLength} characters`
139
- };
140
- }
141
- }
142
- if (validation2.pattern) {
143
- const regex = new RegExp(validation2.pattern);
144
- if (!regex.test(String(value))) {
145
- return {
146
- field: field.name,
147
- message: validation2.errorMessage || `${field.label} format is invalid`
148
- };
149
- }
150
- }
151
- if (validation2.customValidator) {
152
- const result = validation2.customValidator(value);
153
- if (result !== true) {
154
- return {
155
- field: field.name,
156
- message: typeof result === "string" ? result : validation2.errorMessage || `${field.label} is invalid`
157
- };
158
- }
159
- }
160
- return null;
161
- }
162
- /**
163
- * Merge elicited values with original arguments
164
- */
165
- mergeWithArgs(originalArgs, elicitedValues) {
166
- return {
167
- ...originalArgs,
168
- ...elicitedValues
169
- };
170
- }
171
- };
172
- var FormElicitationStrategy = class extends ElicitationStrategyBase {
173
- static {
174
- __name(this, "FormElicitationStrategy");
175
- }
176
- static {
177
- __name2(this, "FormElicitationStrategy");
178
- }
179
- buildRequest(config, context) {
180
- return {
181
- type: "elicitation",
182
- title: config.title || "Additional Information Required",
183
- description: config.description,
184
- fields: config.fields || [],
185
- metadata: {
186
- strategy: "form",
187
- previousValues: context.args
188
- }
189
- };
190
- }
191
- };
192
- var MultiStepElicitationStrategy = class extends ElicitationStrategyBase {
193
- static {
194
- __name(this, "MultiStepElicitationStrategy");
195
- }
196
- static {
197
- __name2(this, "MultiStepElicitationStrategy");
198
- }
199
- steps;
200
- currentStep = 0;
201
- accumulatedValues = {};
202
- constructor(steps) {
203
- super();
204
- this.steps = steps;
205
- }
206
- buildRequest(config, context) {
207
- while (this.currentStep < this.steps.length) {
208
- const step = this.steps[this.currentStep];
209
- if (!step.condition || step.condition(this.accumulatedValues)) {
210
- return {
211
- type: "elicitation",
212
- title: step.title,
213
- description: step.description,
214
- fields: step.fields,
215
- metadata: {
216
- strategy: "multi-step",
217
- stepNumber: this.currentStep + 1,
218
- totalSteps: this.steps.length,
219
- previousValues: {
220
- ...context.args,
221
- ...this.accumulatedValues
222
- }
223
- }
224
- };
225
- }
226
- this.currentStep++;
227
- }
228
- return {
229
- type: "elicitation",
230
- title: "Complete",
231
- description: "All steps completed",
232
- fields: [],
233
- metadata: {
234
- strategy: "multi-step",
235
- stepNumber: this.steps.length,
236
- totalSteps: this.steps.length,
237
- previousValues: this.accumulatedValues
238
- }
239
- };
240
- }
241
- /**
242
- * Check if there are more steps
243
- */
244
- hasNextStep() {
245
- return this.currentStep < this.steps.length - 1;
246
- }
247
- /**
248
- * Move to next step and accumulate values
249
- */
250
- nextStep(values) {
251
- this.accumulatedValues = {
252
- ...this.accumulatedValues,
253
- ...values
254
- };
255
- this.currentStep++;
256
- }
257
- /**
258
- * Get all accumulated values
259
- */
260
- getAccumulatedValues() {
261
- return {
262
- ...this.accumulatedValues
263
- };
264
- }
265
- /**
266
- * Reset to first step
267
- */
268
- reset() {
269
- this.currentStep = 0;
270
- this.accumulatedValues = {};
271
- }
272
- /**
273
- * Override merge to include all accumulated values
274
- */
275
- mergeWithArgs(originalArgs, elicitedValues) {
276
- return {
277
- ...originalArgs,
278
- ...this.accumulatedValues,
279
- ...elicitedValues
280
- };
281
- }
282
- };
283
- function Elicitation(config) {
284
- return (target, propertyKey, descriptor) => {
285
- if (!descriptor || typeof descriptor.value !== "function") {
286
- throw new Error("@Elicitation can only be applied to methods");
287
- }
288
- Reflect.defineMetadata("elicitation:config", config, descriptor.value);
289
- Reflect.defineMetadata("elicitation:enabled", true, descriptor.value);
290
- const strategy = config.strategy || "form";
291
- Reflect.defineMetadata("elicitation:strategy", strategy, descriptor.value);
292
- };
293
- }
294
- __name(Elicitation, "Elicitation");
295
- __name2(Elicitation, "Elicitation");
296
- function isElicitationEnabled(method) {
297
- return Reflect.getMetadata("elicitation:enabled", method) === true;
298
- }
299
- __name(isElicitationEnabled, "isElicitationEnabled");
300
- __name2(isElicitationEnabled, "isElicitationEnabled");
301
- function getElicitationConfig(method) {
302
- return Reflect.getMetadata("elicitation:config", method);
303
- }
304
- __name(getElicitationConfig, "getElicitationConfig");
305
- __name2(getElicitationConfig, "getElicitationConfig");
306
- function getElicitationStrategy(method) {
307
- return Reflect.getMetadata("elicitation:strategy", method);
308
- }
309
- __name(getElicitationStrategy, "getElicitationStrategy");
310
- __name2(getElicitationStrategy, "getElicitationStrategy");
311
- function buildElicitationRequest(method, args, meta) {
312
- const config = getElicitationConfig(method);
313
- if (!config) return null;
314
- const context = {
315
- args,
316
- meta,
317
- previousAttempts: 0
318
- };
319
- if (config.condition && !config.condition(args)) {
320
- return null;
321
- }
322
- if (config.builder) {
323
- const result = config.builder(context);
324
- if (Array.isArray(result)) {
325
- const strategy = new MultiStepElicitationStrategy(result);
326
- return strategy.buildRequest(config, context);
327
- }
328
- return result;
329
- }
330
- const strategyType = config.strategy || "form";
331
- switch (strategyType) {
332
- case "form": {
333
- const strategy = new FormElicitationStrategy();
334
- return strategy.buildRequest(config, context);
335
- }
336
- case "multi-step": {
337
- if (!config.fields) {
338
- throw new Error("Multi-step elicitation requires either a builder or fields");
339
- }
340
- const steps = [
341
- {
342
- title: config.title || "Step 1",
343
- description: config.description,
344
- fields: config.fields
345
- }
346
- ];
347
- const strategy = new MultiStepElicitationStrategy(steps);
348
- return strategy.buildRequest(config, context);
349
- }
350
- default:
351
- throw new Error(`Unsupported elicitation strategy: ${strategyType}`);
352
- }
353
- }
354
- __name(buildElicitationRequest, "buildElicitationRequest");
355
- __name2(buildElicitationRequest, "buildElicitationRequest");
356
- function checkMissingFields(args, config) {
357
- if (!config.fields) return false;
358
- for (const field of config.fields) {
359
- if (field.required) {
360
- const value = args[field.name];
361
- if (value === void 0 || value === null || value === "") {
362
- return true;
363
- }
364
- }
365
- }
366
- return false;
367
- }
368
- __name(checkMissingFields, "checkMissingFields");
369
- __name2(checkMissingFields, "checkMissingFields");
370
- var ElicitationFormBuilder = class {
371
- static {
372
- __name(this, "ElicitationFormBuilder");
373
- }
374
- static {
375
- __name2(this, "ElicitationFormBuilder");
376
- }
377
- fields = [];
378
- config = {};
379
- /**
380
- * Set the form title
381
- */
382
- title(title) {
383
- this.config.title = title;
384
- return this;
385
- }
386
- /**
387
- * Set the form description
388
- */
389
- description(description) {
390
- this.config.description = description;
391
- return this;
392
- }
393
- /**
394
- * Set a condition for when elicitation should occur
395
- */
396
- condition(condition) {
397
- this.config.condition = condition;
398
- return this;
399
- }
400
- /**
401
- * Add a text field
402
- */
403
- addTextField(name, label, options) {
404
- this.fields.push({
405
- name,
406
- label,
407
- type: "text",
408
- ...options
409
- });
410
- return this;
411
- }
412
- /**
413
- * Add a textarea field
414
- */
415
- addTextAreaField(name, label, options) {
416
- this.fields.push({
417
- name,
418
- label,
419
- type: "textarea",
420
- ...options
421
- });
422
- return this;
423
- }
424
- /**
425
- * Add a number field
426
- */
427
- addNumberField(name, label, options) {
428
- this.fields.push({
429
- name,
430
- label,
431
- type: "number",
432
- ...options
433
- });
434
- return this;
435
- }
436
- /**
437
- * Add a boolean field (checkbox)
438
- */
439
- addBooleanField(name, label, options) {
440
- this.fields.push({
441
- name,
442
- label,
443
- type: "boolean",
444
- ...options
445
- });
446
- return this;
447
- }
448
- /**
449
- * Add a select field (dropdown)
450
- */
451
- addSelectField(name, label, options, fieldOptions) {
452
- this.fields.push({
453
- name,
454
- label,
455
- type: "select",
456
- options,
457
- ...fieldOptions
458
- });
459
- return this;
460
- }
461
- /**
462
- * Add a multi-select field
463
- */
464
- addMultiSelectField(name, label, options, fieldOptions) {
465
- this.fields.push({
466
- name,
467
- label,
468
- type: "multiselect",
469
- options,
470
- ...fieldOptions
471
- });
472
- return this;
473
- }
474
- /**
475
- * Add an email field
476
- */
477
- addEmailField(name, label, options) {
478
- this.fields.push({
479
- name,
480
- label,
481
- type: "email",
482
- ...options
483
- });
484
- return this;
485
- }
486
- /**
487
- * Add a URL field
488
- */
489
- addUrlField(name, label, options) {
490
- this.fields.push({
491
- name,
492
- label,
493
- type: "url",
494
- ...options
495
- });
496
- return this;
497
- }
498
- /**
499
- * Add a date field
500
- */
501
- addDateField(name, label, options) {
502
- this.fields.push({
503
- name,
504
- label,
505
- type: "date",
506
- ...options
507
- });
508
- return this;
509
- }
510
- /**
511
- * Add a custom field with full control
512
- */
513
- addCustomField(field) {
514
- this.fields.push(field);
515
- return this;
516
- }
517
- /**
518
- * Build the final configuration
519
- */
520
- build() {
521
- return {
522
- ...this.config,
523
- fields: this.fields,
524
- strategy: "form"
525
- };
526
- }
527
- };
528
- var ValidationBuilder = class {
529
- static {
530
- __name(this, "ValidationBuilder");
531
- }
532
- static {
533
- __name2(this, "ValidationBuilder");
534
- }
535
- validation = {};
536
- min(min) {
537
- this.validation.min = min;
538
- return this;
539
- }
540
- max(max) {
541
- this.validation.max = max;
542
- return this;
543
- }
544
- minLength(minLength) {
545
- this.validation.minLength = minLength;
546
- return this;
547
- }
548
- maxLength(maxLength) {
549
- this.validation.maxLength = maxLength;
550
- return this;
551
- }
552
- pattern(pattern) {
553
- this.validation.pattern = pattern;
554
- return this;
555
- }
556
- customValidator(validator) {
557
- this.validation.customValidator = validator;
558
- return this;
559
- }
560
- errorMessage(message) {
561
- this.validation.errorMessage = message;
562
- return this;
563
- }
564
- build() {
565
- return this.validation;
566
- }
567
- };
568
- function validation() {
569
- return new ValidationBuilder();
570
- }
571
- __name(validation, "validation");
572
- __name2(validation, "validation");
573
- export {
574
- Elicitation,
575
- ElicitationError,
576
- ElicitationFormBuilder,
577
- ElicitationStrategyBase,
578
- FormElicitationStrategy,
579
- MultiStepElicitationStrategy,
580
- ValidationBuilder,
581
- buildElicitationRequest,
582
- checkMissingFields,
583
- getElicitationConfig,
584
- getElicitationStrategy,
585
- isElicitationEnabled,
586
- validation
587
- };