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