@leanmcp/core 0.3.16 → 0.3.17

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