@leanmcp/core 0.4.3 → 0.4.4
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 +485 -485
- package/dist/index.d.mts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +83 -2
- package/dist/index.mjs +76 -2
- package/package.json +78 -78
package/README.md
CHANGED
|
@@ -1,485 +1,485 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img
|
|
3
|
-
src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.png"
|
|
4
|
-
alt="LeanMCP Logo"
|
|
5
|
-
width="400"
|
|
6
|
-
/>
|
|
7
|
-
</p>
|
|
8
|
-
|
|
9
|
-
<p align="center">
|
|
10
|
-
<strong>@leanmcp/core</strong><br/>
|
|
11
|
-
Core library for building MCP servers with TypeScript decorators and declarative schema definition.
|
|
12
|
-
</p>
|
|
13
|
-
|
|
14
|
-
<p align="center">
|
|
15
|
-
<a href="https://www.npmjs.com/package/@leanmcp/core">
|
|
16
|
-
<img src="https://img.shields.io/npm/v/@leanmcp/core" alt="npm version" />
|
|
17
|
-
</a>
|
|
18
|
-
<a href="https://www.npmjs.com/package/@leanmcp/core">
|
|
19
|
-
<img src="https://img.shields.io/npm/dm/@leanmcp/core" alt="npm downloads" />
|
|
20
|
-
</a>
|
|
21
|
-
<a href="https://docs.leanmcp.com/sdk/core">
|
|
22
|
-
<img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
|
|
23
|
-
</a>
|
|
24
|
-
<a href="https://discord.com/invite/DsRcA3GwPy">
|
|
25
|
-
<img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
|
|
26
|
-
</a>
|
|
27
|
-
<a href="https://x.com/LeanMcp">
|
|
28
|
-
<img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
|
|
29
|
-
</a>
|
|
30
|
-
<a href="https://leanmcp.com/">
|
|
31
|
-
<img src="https://img.shields.io/badge/Website-leanmcp-0A66C2?" />
|
|
32
|
-
</a>
|
|
33
|
-
<a href="https://deepwiki.com/LeanMCP/leanmcp-sdk"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
34
|
-
</p>
|
|
35
|
-
|
|
36
|
-
## Features
|
|
37
|
-
|
|
38
|
-
- **Type-Safe Decorators** — `@Tool`, `@Prompt`, `@Resource` with full TypeScript support
|
|
39
|
-
- **Auto-Discovery** — Zero-config service discovery from `./mcp` directory
|
|
40
|
-
- **Schema Generation** — Declarative JSON Schema with `@SchemaConstraint` decorators
|
|
41
|
-
- **HTTP Transport** — Production-ready HTTP server with session management
|
|
42
|
-
- **Input Validation** — Built-in AJV validation for all inputs
|
|
43
|
-
- **Structured Content** — Automatic `structuredContent` for ChatGPT Apps SDK compatibility
|
|
44
|
-
- **MCP Compliant** — Built on official `@modelcontextprotocol/sdk`
|
|
45
|
-
|
|
46
|
-
## Installation
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npm install @leanmcp/core
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
For HTTP server support:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npm install express cors
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Quick Start
|
|
59
|
-
|
|
60
|
-
### Zero-Config (Recommended)
|
|
61
|
-
|
|
62
|
-
The simplest way to create an MCP server with auto-discovery:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import { createHTTPServer } from '@leanmcp/core';
|
|
66
|
-
|
|
67
|
-
await createHTTPServer({
|
|
68
|
-
name: 'my-mcp-server',
|
|
69
|
-
version: '1.0.0',
|
|
70
|
-
port: 3001,
|
|
71
|
-
cors: true,
|
|
72
|
-
logging: true,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// Services are automatically discovered from ./mcp directory
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**Directory Structure:**
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
your-project/
|
|
82
|
-
├── main.ts
|
|
83
|
-
└── mcp/
|
|
84
|
-
├── sentiment/
|
|
85
|
-
│ └── index.ts # export class SentimentService
|
|
86
|
-
├── weather/
|
|
87
|
-
│ └── index.ts # export class WeatherService
|
|
88
|
-
└── config.ts # Optional: shared dependencies
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Define a Service
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// mcp/sentiment/index.ts
|
|
95
|
-
import { Tool, SchemaConstraint, Optional } from '@leanmcp/core';
|
|
96
|
-
|
|
97
|
-
class AnalyzeSentimentInput {
|
|
98
|
-
@SchemaConstraint({
|
|
99
|
-
description: 'Text to analyze',
|
|
100
|
-
minLength: 1,
|
|
101
|
-
})
|
|
102
|
-
text!: string;
|
|
103
|
-
|
|
104
|
-
@Optional()
|
|
105
|
-
@SchemaConstraint({
|
|
106
|
-
description: 'Language code',
|
|
107
|
-
enum: ['en', 'es', 'fr'],
|
|
108
|
-
default: 'en',
|
|
109
|
-
})
|
|
110
|
-
language?: string;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export class SentimentService {
|
|
114
|
-
@Tool({
|
|
115
|
-
description: 'Analyze sentiment of text',
|
|
116
|
-
inputClass: AnalyzeSentimentInput,
|
|
117
|
-
})
|
|
118
|
-
async analyzeSentiment(input: AnalyzeSentimentInput) {
|
|
119
|
-
return {
|
|
120
|
-
sentiment: 'positive',
|
|
121
|
-
score: 0.8,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## Decorators
|
|
130
|
-
|
|
131
|
-
### @Tool
|
|
132
|
-
|
|
133
|
-
Marks a method as a callable MCP tool.
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
class CalculateInput {
|
|
137
|
-
@SchemaConstraint({ description: 'First number' })
|
|
138
|
-
a!: number;
|
|
139
|
-
|
|
140
|
-
@SchemaConstraint({ description: 'Second number' })
|
|
141
|
-
b!: number;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
@Tool({
|
|
145
|
-
description: 'Calculate sum of two numbers',
|
|
146
|
-
inputClass: CalculateInput
|
|
147
|
-
})
|
|
148
|
-
async calculate(input: CalculateInput) {
|
|
149
|
-
return { result: input.a + input.b };
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
**Options:**
|
|
154
|
-
|
|
155
|
-
| Option | Type | Description |
|
|
156
|
-
| ------------- | -------- | --------------------------- |
|
|
157
|
-
| `description` | `string` | Tool description for the AI |
|
|
158
|
-
| `inputClass` | `Class` | Class defining input schema |
|
|
159
|
-
|
|
160
|
-
### @Prompt
|
|
161
|
-
|
|
162
|
-
Marks a method as a reusable prompt template.
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
class CodeReviewInput {
|
|
166
|
-
@SchemaConstraint({ description: 'Code to review' })
|
|
167
|
-
code!: string;
|
|
168
|
-
|
|
169
|
-
@SchemaConstraint({ description: 'Programming language' })
|
|
170
|
-
language!: string;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
@Prompt({ description: 'Generate code review prompt' })
|
|
174
|
-
codeReview(input: CodeReviewInput) {
|
|
175
|
-
return {
|
|
176
|
-
messages: [{
|
|
177
|
-
role: "user",
|
|
178
|
-
content: {
|
|
179
|
-
type: "text",
|
|
180
|
-
text: `Review this ${input.language} code:\n\n${input.code}`
|
|
181
|
-
}
|
|
182
|
-
}]
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### @Resource
|
|
188
|
-
|
|
189
|
-
Marks a method as an MCP resource (data source).
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
@Resource({
|
|
193
|
-
description: 'Get system configuration',
|
|
194
|
-
mimeType: 'application/json'
|
|
195
|
-
})
|
|
196
|
-
async getConfig() {
|
|
197
|
-
return {
|
|
198
|
-
version: "1.0.0",
|
|
199
|
-
environment: process.env.NODE_ENV
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### @SchemaConstraint
|
|
205
|
-
|
|
206
|
-
Add validation constraints to class properties.
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
class UserInput {
|
|
210
|
-
@SchemaConstraint({
|
|
211
|
-
description: 'User email',
|
|
212
|
-
format: 'email',
|
|
213
|
-
minLength: 5,
|
|
214
|
-
maxLength: 100,
|
|
215
|
-
})
|
|
216
|
-
email!: string;
|
|
217
|
-
|
|
218
|
-
@SchemaConstraint({
|
|
219
|
-
description: 'User age',
|
|
220
|
-
minimum: 18,
|
|
221
|
-
maximum: 120,
|
|
222
|
-
})
|
|
223
|
-
age!: number;
|
|
224
|
-
|
|
225
|
-
@Optional()
|
|
226
|
-
@SchemaConstraint({
|
|
227
|
-
description: 'User role',
|
|
228
|
-
enum: ['admin', 'user', 'guest'],
|
|
229
|
-
default: 'user',
|
|
230
|
-
})
|
|
231
|
-
role?: string;
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
**Common constraints:**
|
|
236
|
-
|
|
237
|
-
- `description`, `default` — Documentation
|
|
238
|
-
- `minLength`, `maxLength` — String length
|
|
239
|
-
- `minimum`, `maximum` — Number range
|
|
240
|
-
- `enum` — Allowed values
|
|
241
|
-
- `format` — String format (`email`, `uri`, `date`, etc.)
|
|
242
|
-
- `pattern` — Regex pattern
|
|
243
|
-
|
|
244
|
-
### @Optional
|
|
245
|
-
|
|
246
|
-
Marks a property as optional in the schema.
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
class SearchInput {
|
|
250
|
-
@SchemaConstraint({ description: 'Search query' })
|
|
251
|
-
query!: string;
|
|
252
|
-
|
|
253
|
-
@Optional()
|
|
254
|
-
@SchemaConstraint({ description: 'Max results', default: 10 })
|
|
255
|
-
limit?: number;
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
---
|
|
260
|
-
|
|
261
|
-
## API Reference
|
|
262
|
-
|
|
263
|
-
### createHTTPServer
|
|
264
|
-
|
|
265
|
-
Create and start an HTTP server with auto-discovery.
|
|
266
|
-
|
|
267
|
-
**Simplified API (Recommended):**
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
await createHTTPServer({
|
|
271
|
-
name: string; // Server name (required)
|
|
272
|
-
version: string; // Server version (required)
|
|
273
|
-
port?: number; // Port (default: 3001)
|
|
274
|
-
cors?: boolean | object; // Enable CORS (default: false)
|
|
275
|
-
logging?: boolean; // Enable logging (default: false)
|
|
276
|
-
debug?: boolean; // Verbose debug logs (default: false)
|
|
277
|
-
autoDiscover?: boolean; // Auto-discover services (default: true)
|
|
278
|
-
mcpDir?: string; // Custom mcp directory path
|
|
279
|
-
sessionTimeout?: number; // Session timeout in ms
|
|
280
|
-
stateless?: boolean; // Stateless mode for Lambda/serverless (default: true)
|
|
281
|
-
dashboard?: boolean; // Serve dashboard UI at / (default: true)
|
|
282
|
-
});
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Factory Pattern (Advanced):**
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
const serverFactory = async () => {
|
|
289
|
-
const server = new MCPServer({
|
|
290
|
-
name: 'my-server',
|
|
291
|
-
version: '1.0.0',
|
|
292
|
-
autoDiscover: false, // Disable for manual registration
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
server.registerService(new MyService());
|
|
296
|
-
return server.getServer();
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
await createHTTPServer(serverFactory, {
|
|
300
|
-
port: 3001,
|
|
301
|
-
cors: true,
|
|
302
|
-
});
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### MCPServer
|
|
306
|
-
|
|
307
|
-
Main server class for registering services.
|
|
308
|
-
|
|
309
|
-
```typescript
|
|
310
|
-
const server = new MCPServer({
|
|
311
|
-
name: string; // Server name
|
|
312
|
-
version: string; // Server version
|
|
313
|
-
logging?: boolean; // Enable logging (default: false)
|
|
314
|
-
debug?: boolean; // Verbose debug logs (default: false)
|
|
315
|
-
autoDiscover?: boolean; // Auto-discover from ./mcp (default: true)
|
|
316
|
-
mcpDir?: string; // Custom mcp directory path
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
server.registerService(instance); // Manual registration
|
|
320
|
-
server.getServer(); // Get underlying MCP SDK server
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
## Auto-Discovery
|
|
326
|
-
|
|
327
|
-
Services are automatically discovered from the `./mcp` directory:
|
|
328
|
-
|
|
329
|
-
1. Recursively scans for `index.ts` or `index.js` files
|
|
330
|
-
2. Dynamically imports each file
|
|
331
|
-
3. Looks for exported classes
|
|
332
|
-
4. Instantiates with no-args constructors
|
|
333
|
-
5. Registers all decorated methods
|
|
334
|
-
|
|
335
|
-
### Shared Dependencies
|
|
336
|
-
|
|
337
|
-
For services needing shared configuration (auth, database, etc.), create a `config.ts`:
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
// mcp/config.ts
|
|
341
|
-
import { AuthProvider } from '@leanmcp/auth';
|
|
342
|
-
|
|
343
|
-
export const authProvider = new AuthProvider('cognito', {
|
|
344
|
-
region: process.env.AWS_REGION,
|
|
345
|
-
userPoolId: process.env.COGNITO_USER_POOL_ID,
|
|
346
|
-
clientId: process.env.COGNITO_CLIENT_ID,
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
await authProvider.init();
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
Then import in your services:
|
|
353
|
-
|
|
354
|
-
```typescript
|
|
355
|
-
// mcp/slack/index.ts
|
|
356
|
-
import { Tool } from '@leanmcp/core';
|
|
357
|
-
import { Authenticated } from '@leanmcp/auth';
|
|
358
|
-
import { authProvider } from '../config.js';
|
|
359
|
-
|
|
360
|
-
@Authenticated(authProvider)
|
|
361
|
-
export class SlackService {
|
|
362
|
-
@Tool({ description: 'Send a message' })
|
|
363
|
-
async sendMessage(args: { channel: string; message: string }) {
|
|
364
|
-
// Implementation
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
---
|
|
370
|
-
|
|
371
|
-
## Structured Content
|
|
372
|
-
|
|
373
|
-
Tool return values are automatically exposed as `structuredContent` in the MCP response, enabling ChatGPT Apps SDK compatibility.
|
|
374
|
-
|
|
375
|
-
**Automatic Handling:**
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
@Tool({ description: 'List channels' })
|
|
379
|
-
async listChannels() {
|
|
380
|
-
// Return a plain object - it becomes structuredContent automatically
|
|
381
|
-
return { channels: [...] };
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
The response includes both `content` (text) and `structuredContent` (object):
|
|
386
|
-
|
|
387
|
-
```json
|
|
388
|
-
{
|
|
389
|
-
"content": [{ "type": "text", "text": "{\"channels\": [...]}" }],
|
|
390
|
-
"structuredContent": { "channels": [...] }
|
|
391
|
-
}
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
**Manual MCP Response:**
|
|
395
|
-
|
|
396
|
-
If your tool returns a manual MCP response (with `content` array), the SDK extracts data from `content[0].text`:
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
return {
|
|
400
|
-
content: [{ type: 'text', text: JSON.stringify({ channels }) }],
|
|
401
|
-
};
|
|
402
|
-
// structuredContent will be { channels: [...] }
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## HTTP Endpoints
|
|
408
|
-
|
|
409
|
-
| Endpoint | Method | Description |
|
|
410
|
-
| --------- | ------ | ------------------------------------ |
|
|
411
|
-
| `/mcp` | POST | MCP protocol endpoint (JSON-RPC 2.0) |
|
|
412
|
-
| `/health` | GET | Health check |
|
|
413
|
-
| `/` | GET | Welcome message |
|
|
414
|
-
|
|
415
|
-
## Error Handling
|
|
416
|
-
|
|
417
|
-
Errors are automatically caught and returned in MCP format:
|
|
418
|
-
|
|
419
|
-
```typescript
|
|
420
|
-
@Tool({ description: 'Divide numbers', inputClass: DivideInput })
|
|
421
|
-
async divide(input: DivideInput) {
|
|
422
|
-
if (input.b === 0) {
|
|
423
|
-
throw new Error("Division by zero");
|
|
424
|
-
}
|
|
425
|
-
return { result: input.a / input.b };
|
|
426
|
-
}
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
Returns:
|
|
430
|
-
|
|
431
|
-
```json
|
|
432
|
-
{
|
|
433
|
-
"content": [{ "type": "text", "text": "Error: Division by zero" }],
|
|
434
|
-
"isError": true
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
## Environment Variables
|
|
439
|
-
|
|
440
|
-
```bash
|
|
441
|
-
PORT=3001 # Server port
|
|
442
|
-
NODE_ENV=production # Environment
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
## TypeScript Support
|
|
446
|
-
|
|
447
|
-
**Key Points:**
|
|
448
|
-
|
|
449
|
-
- Input schema is defined via `inputClass` in the decorator
|
|
450
|
-
- Output type is inferred from the return type
|
|
451
|
-
- For tools with no input, omit `inputClass`
|
|
452
|
-
- Use `@SchemaConstraint` for validation and documentation
|
|
453
|
-
|
|
454
|
-
```typescript
|
|
455
|
-
class MyInput {
|
|
456
|
-
@SchemaConstraint({ description: 'Input field' })
|
|
457
|
-
field!: string;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
@Tool({ description: 'My tool', inputClass: MyInput })
|
|
461
|
-
async myTool(input: MyInput): Promise<{ result: string }> {
|
|
462
|
-
return { result: input.field.toUpperCase() };
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
## Documentation
|
|
467
|
-
|
|
468
|
-
- [Full Documentation](https://docs.leanmcp.com/sdk/core)
|
|
469
|
-
|
|
470
|
-
## Related Packages
|
|
471
|
-
|
|
472
|
-
- [@leanmcp/cli](https://www.npmjs.com/package/@leanmcp/cli) — CLI tool for project creation
|
|
473
|
-
- [@leanmcp/auth](https://www.npmjs.com/package/@leanmcp/auth) — Authentication decorators
|
|
474
|
-
- [@leanmcp/ui](https://www.npmjs.com/package/@leanmcp/ui) — MCP App UI components
|
|
475
|
-
- [@leanmcp/elicitation](https://www.npmjs.com/package/@leanmcp/elicitation) — Structured user input
|
|
476
|
-
|
|
477
|
-
## Links
|
|
478
|
-
|
|
479
|
-
- [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
|
|
480
|
-
- [NPM Package](https://www.npmjs.com/package/@leanmcp/core)
|
|
481
|
-
- [MCP Specification](https://spec.modelcontextprotocol.io/)
|
|
482
|
-
|
|
483
|
-
## License
|
|
484
|
-
|
|
485
|
-
MIT
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.png"
|
|
4
|
+
alt="LeanMCP Logo"
|
|
5
|
+
width="400"
|
|
6
|
+
/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>@leanmcp/core</strong><br/>
|
|
11
|
+
Core library for building MCP servers with TypeScript decorators and declarative schema definition.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@leanmcp/core">
|
|
16
|
+
<img src="https://img.shields.io/npm/v/@leanmcp/core" alt="npm version" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@leanmcp/core">
|
|
19
|
+
<img src="https://img.shields.io/npm/dm/@leanmcp/core" alt="npm downloads" />
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://docs.leanmcp.com/sdk/core">
|
|
22
|
+
<img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://discord.com/invite/DsRcA3GwPy">
|
|
25
|
+
<img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://x.com/LeanMcp">
|
|
28
|
+
<img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
|
|
29
|
+
</a>
|
|
30
|
+
<a href="https://leanmcp.com/">
|
|
31
|
+
<img src="https://img.shields.io/badge/Website-leanmcp-0A66C2?" />
|
|
32
|
+
</a>
|
|
33
|
+
<a href="https://deepwiki.com/LeanMCP/leanmcp-sdk"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **Type-Safe Decorators** — `@Tool`, `@Prompt`, `@Resource` with full TypeScript support
|
|
39
|
+
- **Auto-Discovery** — Zero-config service discovery from `./mcp` directory
|
|
40
|
+
- **Schema Generation** — Declarative JSON Schema with `@SchemaConstraint` decorators
|
|
41
|
+
- **HTTP Transport** — Production-ready HTTP server with session management
|
|
42
|
+
- **Input Validation** — Built-in AJV validation for all inputs
|
|
43
|
+
- **Structured Content** — Automatic `structuredContent` for ChatGPT Apps SDK compatibility
|
|
44
|
+
- **MCP Compliant** — Built on official `@modelcontextprotocol/sdk`
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install @leanmcp/core
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For HTTP server support:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install express cors
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### Zero-Config (Recommended)
|
|
61
|
+
|
|
62
|
+
The simplest way to create an MCP server with auto-discovery:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { createHTTPServer } from '@leanmcp/core';
|
|
66
|
+
|
|
67
|
+
await createHTTPServer({
|
|
68
|
+
name: 'my-mcp-server',
|
|
69
|
+
version: '1.0.0',
|
|
70
|
+
port: 3001,
|
|
71
|
+
cors: true,
|
|
72
|
+
logging: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Services are automatically discovered from ./mcp directory
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Directory Structure:**
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
your-project/
|
|
82
|
+
├── main.ts
|
|
83
|
+
└── mcp/
|
|
84
|
+
├── sentiment/
|
|
85
|
+
│ └── index.ts # export class SentimentService
|
|
86
|
+
├── weather/
|
|
87
|
+
│ └── index.ts # export class WeatherService
|
|
88
|
+
└── config.ts # Optional: shared dependencies
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Define a Service
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// mcp/sentiment/index.ts
|
|
95
|
+
import { Tool, SchemaConstraint, Optional } from '@leanmcp/core';
|
|
96
|
+
|
|
97
|
+
class AnalyzeSentimentInput {
|
|
98
|
+
@SchemaConstraint({
|
|
99
|
+
description: 'Text to analyze',
|
|
100
|
+
minLength: 1,
|
|
101
|
+
})
|
|
102
|
+
text!: string;
|
|
103
|
+
|
|
104
|
+
@Optional()
|
|
105
|
+
@SchemaConstraint({
|
|
106
|
+
description: 'Language code',
|
|
107
|
+
enum: ['en', 'es', 'fr'],
|
|
108
|
+
default: 'en',
|
|
109
|
+
})
|
|
110
|
+
language?: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export class SentimentService {
|
|
114
|
+
@Tool({
|
|
115
|
+
description: 'Analyze sentiment of text',
|
|
116
|
+
inputClass: AnalyzeSentimentInput,
|
|
117
|
+
})
|
|
118
|
+
async analyzeSentiment(input: AnalyzeSentimentInput) {
|
|
119
|
+
return {
|
|
120
|
+
sentiment: 'positive',
|
|
121
|
+
score: 0.8,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Decorators
|
|
130
|
+
|
|
131
|
+
### @Tool
|
|
132
|
+
|
|
133
|
+
Marks a method as a callable MCP tool.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
class CalculateInput {
|
|
137
|
+
@SchemaConstraint({ description: 'First number' })
|
|
138
|
+
a!: number;
|
|
139
|
+
|
|
140
|
+
@SchemaConstraint({ description: 'Second number' })
|
|
141
|
+
b!: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@Tool({
|
|
145
|
+
description: 'Calculate sum of two numbers',
|
|
146
|
+
inputClass: CalculateInput
|
|
147
|
+
})
|
|
148
|
+
async calculate(input: CalculateInput) {
|
|
149
|
+
return { result: input.a + input.b };
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Options:**
|
|
154
|
+
|
|
155
|
+
| Option | Type | Description |
|
|
156
|
+
| ------------- | -------- | --------------------------- |
|
|
157
|
+
| `description` | `string` | Tool description for the AI |
|
|
158
|
+
| `inputClass` | `Class` | Class defining input schema |
|
|
159
|
+
|
|
160
|
+
### @Prompt
|
|
161
|
+
|
|
162
|
+
Marks a method as a reusable prompt template.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
class CodeReviewInput {
|
|
166
|
+
@SchemaConstraint({ description: 'Code to review' })
|
|
167
|
+
code!: string;
|
|
168
|
+
|
|
169
|
+
@SchemaConstraint({ description: 'Programming language' })
|
|
170
|
+
language!: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@Prompt({ description: 'Generate code review prompt' })
|
|
174
|
+
codeReview(input: CodeReviewInput) {
|
|
175
|
+
return {
|
|
176
|
+
messages: [{
|
|
177
|
+
role: "user",
|
|
178
|
+
content: {
|
|
179
|
+
type: "text",
|
|
180
|
+
text: `Review this ${input.language} code:\n\n${input.code}`
|
|
181
|
+
}
|
|
182
|
+
}]
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### @Resource
|
|
188
|
+
|
|
189
|
+
Marks a method as an MCP resource (data source).
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
@Resource({
|
|
193
|
+
description: 'Get system configuration',
|
|
194
|
+
mimeType: 'application/json'
|
|
195
|
+
})
|
|
196
|
+
async getConfig() {
|
|
197
|
+
return {
|
|
198
|
+
version: "1.0.0",
|
|
199
|
+
environment: process.env.NODE_ENV
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### @SchemaConstraint
|
|
205
|
+
|
|
206
|
+
Add validation constraints to class properties.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
class UserInput {
|
|
210
|
+
@SchemaConstraint({
|
|
211
|
+
description: 'User email',
|
|
212
|
+
format: 'email',
|
|
213
|
+
minLength: 5,
|
|
214
|
+
maxLength: 100,
|
|
215
|
+
})
|
|
216
|
+
email!: string;
|
|
217
|
+
|
|
218
|
+
@SchemaConstraint({
|
|
219
|
+
description: 'User age',
|
|
220
|
+
minimum: 18,
|
|
221
|
+
maximum: 120,
|
|
222
|
+
})
|
|
223
|
+
age!: number;
|
|
224
|
+
|
|
225
|
+
@Optional()
|
|
226
|
+
@SchemaConstraint({
|
|
227
|
+
description: 'User role',
|
|
228
|
+
enum: ['admin', 'user', 'guest'],
|
|
229
|
+
default: 'user',
|
|
230
|
+
})
|
|
231
|
+
role?: string;
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Common constraints:**
|
|
236
|
+
|
|
237
|
+
- `description`, `default` — Documentation
|
|
238
|
+
- `minLength`, `maxLength` — String length
|
|
239
|
+
- `minimum`, `maximum` — Number range
|
|
240
|
+
- `enum` — Allowed values
|
|
241
|
+
- `format` — String format (`email`, `uri`, `date`, etc.)
|
|
242
|
+
- `pattern` — Regex pattern
|
|
243
|
+
|
|
244
|
+
### @Optional
|
|
245
|
+
|
|
246
|
+
Marks a property as optional in the schema.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
class SearchInput {
|
|
250
|
+
@SchemaConstraint({ description: 'Search query' })
|
|
251
|
+
query!: string;
|
|
252
|
+
|
|
253
|
+
@Optional()
|
|
254
|
+
@SchemaConstraint({ description: 'Max results', default: 10 })
|
|
255
|
+
limit?: number;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## API Reference
|
|
262
|
+
|
|
263
|
+
### createHTTPServer
|
|
264
|
+
|
|
265
|
+
Create and start an HTTP server with auto-discovery.
|
|
266
|
+
|
|
267
|
+
**Simplified API (Recommended):**
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
await createHTTPServer({
|
|
271
|
+
name: string; // Server name (required)
|
|
272
|
+
version: string; // Server version (required)
|
|
273
|
+
port?: number; // Port (default: 3001)
|
|
274
|
+
cors?: boolean | object; // Enable CORS (default: false)
|
|
275
|
+
logging?: boolean; // Enable logging (default: false)
|
|
276
|
+
debug?: boolean; // Verbose debug logs (default: false)
|
|
277
|
+
autoDiscover?: boolean; // Auto-discover services (default: true)
|
|
278
|
+
mcpDir?: string; // Custom mcp directory path
|
|
279
|
+
sessionTimeout?: number; // Session timeout in ms
|
|
280
|
+
stateless?: boolean; // Stateless mode for Lambda/serverless (default: true)
|
|
281
|
+
dashboard?: boolean; // Serve dashboard UI at / (default: true)
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Factory Pattern (Advanced):**
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
const serverFactory = async () => {
|
|
289
|
+
const server = new MCPServer({
|
|
290
|
+
name: 'my-server',
|
|
291
|
+
version: '1.0.0',
|
|
292
|
+
autoDiscover: false, // Disable for manual registration
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
server.registerService(new MyService());
|
|
296
|
+
return server.getServer();
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
await createHTTPServer(serverFactory, {
|
|
300
|
+
port: 3001,
|
|
301
|
+
cors: true,
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### MCPServer
|
|
306
|
+
|
|
307
|
+
Main server class for registering services.
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
const server = new MCPServer({
|
|
311
|
+
name: string; // Server name
|
|
312
|
+
version: string; // Server version
|
|
313
|
+
logging?: boolean; // Enable logging (default: false)
|
|
314
|
+
debug?: boolean; // Verbose debug logs (default: false)
|
|
315
|
+
autoDiscover?: boolean; // Auto-discover from ./mcp (default: true)
|
|
316
|
+
mcpDir?: string; // Custom mcp directory path
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
server.registerService(instance); // Manual registration
|
|
320
|
+
server.getServer(); // Get underlying MCP SDK server
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Auto-Discovery
|
|
326
|
+
|
|
327
|
+
Services are automatically discovered from the `./mcp` directory:
|
|
328
|
+
|
|
329
|
+
1. Recursively scans for `index.ts` or `index.js` files
|
|
330
|
+
2. Dynamically imports each file
|
|
331
|
+
3. Looks for exported classes
|
|
332
|
+
4. Instantiates with no-args constructors
|
|
333
|
+
5. Registers all decorated methods
|
|
334
|
+
|
|
335
|
+
### Shared Dependencies
|
|
336
|
+
|
|
337
|
+
For services needing shared configuration (auth, database, etc.), create a `config.ts`:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
// mcp/config.ts
|
|
341
|
+
import { AuthProvider } from '@leanmcp/auth';
|
|
342
|
+
|
|
343
|
+
export const authProvider = new AuthProvider('cognito', {
|
|
344
|
+
region: process.env.AWS_REGION,
|
|
345
|
+
userPoolId: process.env.COGNITO_USER_POOL_ID,
|
|
346
|
+
clientId: process.env.COGNITO_CLIENT_ID,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await authProvider.init();
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Then import in your services:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// mcp/slack/index.ts
|
|
356
|
+
import { Tool } from '@leanmcp/core';
|
|
357
|
+
import { Authenticated } from '@leanmcp/auth';
|
|
358
|
+
import { authProvider } from '../config.js';
|
|
359
|
+
|
|
360
|
+
@Authenticated(authProvider)
|
|
361
|
+
export class SlackService {
|
|
362
|
+
@Tool({ description: 'Send a message' })
|
|
363
|
+
async sendMessage(args: { channel: string; message: string }) {
|
|
364
|
+
// Implementation
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Structured Content
|
|
372
|
+
|
|
373
|
+
Tool return values are automatically exposed as `structuredContent` in the MCP response, enabling ChatGPT Apps SDK compatibility.
|
|
374
|
+
|
|
375
|
+
**Automatic Handling:**
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
@Tool({ description: 'List channels' })
|
|
379
|
+
async listChannels() {
|
|
380
|
+
// Return a plain object - it becomes structuredContent automatically
|
|
381
|
+
return { channels: [...] };
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
The response includes both `content` (text) and `structuredContent` (object):
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"content": [{ "type": "text", "text": "{\"channels\": [...]}" }],
|
|
390
|
+
"structuredContent": { "channels": [...] }
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Manual MCP Response:**
|
|
395
|
+
|
|
396
|
+
If your tool returns a manual MCP response (with `content` array), the SDK extracts data from `content[0].text`:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
return {
|
|
400
|
+
content: [{ type: 'text', text: JSON.stringify({ channels }) }],
|
|
401
|
+
};
|
|
402
|
+
// structuredContent will be { channels: [...] }
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## HTTP Endpoints
|
|
408
|
+
|
|
409
|
+
| Endpoint | Method | Description |
|
|
410
|
+
| --------- | ------ | ------------------------------------ |
|
|
411
|
+
| `/mcp` | POST | MCP protocol endpoint (JSON-RPC 2.0) |
|
|
412
|
+
| `/health` | GET | Health check |
|
|
413
|
+
| `/` | GET | Welcome message |
|
|
414
|
+
|
|
415
|
+
## Error Handling
|
|
416
|
+
|
|
417
|
+
Errors are automatically caught and returned in MCP format:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
@Tool({ description: 'Divide numbers', inputClass: DivideInput })
|
|
421
|
+
async divide(input: DivideInput) {
|
|
422
|
+
if (input.b === 0) {
|
|
423
|
+
throw new Error("Division by zero");
|
|
424
|
+
}
|
|
425
|
+
return { result: input.a / input.b };
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"content": [{ "type": "text", "text": "Error: Division by zero" }],
|
|
434
|
+
"isError": true
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## Environment Variables
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
PORT=3001 # Server port
|
|
442
|
+
NODE_ENV=production # Environment
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## TypeScript Support
|
|
446
|
+
|
|
447
|
+
**Key Points:**
|
|
448
|
+
|
|
449
|
+
- Input schema is defined via `inputClass` in the decorator
|
|
450
|
+
- Output type is inferred from the return type
|
|
451
|
+
- For tools with no input, omit `inputClass`
|
|
452
|
+
- Use `@SchemaConstraint` for validation and documentation
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
class MyInput {
|
|
456
|
+
@SchemaConstraint({ description: 'Input field' })
|
|
457
|
+
field!: string;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@Tool({ description: 'My tool', inputClass: MyInput })
|
|
461
|
+
async myTool(input: MyInput): Promise<{ result: string }> {
|
|
462
|
+
return { result: input.field.toUpperCase() };
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## Documentation
|
|
467
|
+
|
|
468
|
+
- [Full Documentation](https://docs.leanmcp.com/sdk/core)
|
|
469
|
+
|
|
470
|
+
## Related Packages
|
|
471
|
+
|
|
472
|
+
- [@leanmcp/cli](https://www.npmjs.com/package/@leanmcp/cli) — CLI tool for project creation
|
|
473
|
+
- [@leanmcp/auth](https://www.npmjs.com/package/@leanmcp/auth) — Authentication decorators
|
|
474
|
+
- [@leanmcp/ui](https://www.npmjs.com/package/@leanmcp/ui) — MCP App UI components
|
|
475
|
+
- [@leanmcp/elicitation](https://www.npmjs.com/package/@leanmcp/elicitation) — Structured user input
|
|
476
|
+
|
|
477
|
+
## Links
|
|
478
|
+
|
|
479
|
+
- [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
|
|
480
|
+
- [NPM Package](https://www.npmjs.com/package/@leanmcp/core)
|
|
481
|
+
- [MCP Specification](https://spec.modelcontextprotocol.io/)
|
|
482
|
+
|
|
483
|
+
## License
|
|
484
|
+
|
|
485
|
+
MIT
|