@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 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