@leanmcp/elicitation 0.1.0 → 0.1.2

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.
Files changed (3) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +351 -564
  3. package/package.json +64 -58
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 LeanMCP Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 LeanMCP Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,564 +1,351 @@
1
- # @leanmcp/elicitation
2
-
3
- Structured user input collection for LeanMCP tools using the MCP elicitation protocol. The `@Elicitation` decorator automatically intercepts tool calls to request missing required fields from users before execution.
4
-
5
- ## Features
6
-
7
- - **@Elicitation decorator** - Declarative way to collect missing user inputs
8
- - **Method wrapping** - Automatically intercepts calls and returns elicitation requests
9
- - **Multiple strategies** - Form, multi-step, and conversational elicitation
10
- - **Fluent builder API** - Programmatic form creation with type safety
11
- - **Built-in validation** - Email, URL, pattern matching, custom validators
12
- - **Conditional elicitation** - Only ask for inputs when needed
13
- - **Type-safe** - Full TypeScript support with type inference
14
-
15
- ## Installation
16
-
17
- ```bash
18
- npm install @leanmcp/elicitation @leanmcp/core
19
- ```
20
-
21
- ## Quick Start
22
-
23
- ### 1. Simple Form Elicitation
24
-
25
- ```typescript
26
- import { Tool } from "@leanmcp/core";
27
- import { Elicitation } from "@leanmcp/elicitation";
28
-
29
- class SlackService {
30
- @Tool({ description: "Create a new Slack channel" })
31
- @Elicitation({
32
- title: "Create Channel",
33
- description: "Please provide channel details",
34
- fields: [
35
- {
36
- name: "channelName",
37
- label: "Channel Name",
38
- type: "text",
39
- required: true,
40
- validation: {
41
- pattern: "^[a-z0-9-]+$",
42
- errorMessage: "Must be lowercase alphanumeric with hyphens"
43
- }
44
- },
45
- {
46
- name: "isPrivate",
47
- label: "Private Channel",
48
- type: "boolean",
49
- defaultValue: false
50
- }
51
- ]
52
- })
53
- async createChannel(args: { channelName: string; isPrivate: boolean }) {
54
- // Implementation
55
- return { success: true, channelName: args.channelName };
56
- }
57
- }
58
- ```
59
-
60
- ### 2. Conditional Elicitation
61
-
62
- Only ask for inputs when they're missing:
63
-
64
- ```typescript
65
- @Tool({ description: "Send message to Slack" })
66
- @Elicitation({
67
- condition: (args) => !args.channelId,
68
- title: "Select Channel",
69
- fields: [
70
- {
71
- name: "channelId",
72
- label: "Channel",
73
- type: "select",
74
- required: true,
75
- options: [
76
- { label: "#general", value: "C12345" },
77
- { label: "#random", value: "C67890" }
78
- ]
79
- }
80
- ]
81
- })
82
- async sendMessage(args: { channelId?: string; message: string }) {
83
- // Only elicits if channelId is missing
84
- }
85
- ```
86
-
87
- ### 3. Fluent Builder API
88
-
89
- More programmatic approach:
90
-
91
- ```typescript
92
- import { ElicitationFormBuilder, validation } from "@leanmcp/elicitation";
93
-
94
- @Tool({ description: "Create user account" })
95
- @Elicitation({
96
- builder: () => new ElicitationFormBuilder()
97
- .title("User Registration")
98
- .description("Create a new user account")
99
- .addEmailField("email", "Email Address", { required: true })
100
- .addTextField("username", "Username", {
101
- required: true,
102
- validation: validation()
103
- .minLength(3)
104
- .maxLength(20)
105
- .pattern("^[a-zA-Z0-9_]+$")
106
- .build()
107
- })
108
- .addSelectField("role", "Role", [
109
- { label: "Admin", value: "admin" },
110
- { label: "User", value: "user" }
111
- ])
112
- .build()
113
- })
114
- async createUser(args: any) {
115
- // Implementation
116
- }
117
- ```
118
-
119
- ### 4. Multi-Step Elicitation
120
-
121
- Break input collection into multiple steps:
122
-
123
- ```typescript
124
- @Tool({ description: "Deploy application" })
125
- @Elicitation({
126
- strategy: "multi-step",
127
- builder: () => [
128
- {
129
- title: "Step 1: Environment",
130
- fields: [
131
- {
132
- name: "environment",
133
- label: "Environment",
134
- type: "select",
135
- required: true,
136
- options: [
137
- { label: "Production", value: "prod" },
138
- { label: "Staging", value: "staging" }
139
- ]
140
- }
141
- ]
142
- },
143
- {
144
- title: "Step 2: Configuration",
145
- fields: [
146
- {
147
- name: "replicas",
148
- label: "Replicas",
149
- type: "number",
150
- defaultValue: 3
151
- }
152
- ],
153
- condition: (prev) => prev.environment === "prod"
154
- }
155
- ]
156
- })
157
- async deployApp(args: any) {
158
- // Implementation
159
- }
160
- ```
161
-
162
- ## Field Types
163
-
164
- ### Text Fields
165
-
166
- ```typescript
167
- {
168
- name: "description",
169
- label: "Description",
170
- type: "text",
171
- placeholder: "Enter description...",
172
- validation: {
173
- minLength: 10,
174
- maxLength: 500
175
- }
176
- }
177
- ```
178
-
179
- ### Textarea
180
-
181
- ```typescript
182
- {
183
- name: "content",
184
- label: "Content",
185
- type: "textarea",
186
- placeholder: "Enter long text..."
187
- }
188
- ```
189
-
190
- ### Number
191
-
192
- ```typescript
193
- {
194
- name: "age",
195
- label: "Age",
196
- type: "number",
197
- validation: {
198
- min: 18,
199
- max: 120
200
- }
201
- }
202
- ```
203
-
204
- ### Boolean (Checkbox)
205
-
206
- ```typescript
207
- {
208
- name: "agree",
209
- label: "I agree to terms",
210
- type: "boolean",
211
- defaultValue: false
212
- }
213
- ```
214
-
215
- ### Select (Dropdown)
216
-
217
- ```typescript
218
- {
219
- name: "country",
220
- label: "Country",
221
- type: "select",
222
- options: [
223
- { label: "United States", value: "US" },
224
- { label: "Canada", value: "CA" }
225
- ]
226
- }
227
- ```
228
-
229
- ### Multi-Select
230
-
231
- ```typescript
232
- {
233
- name: "tags",
234
- label: "Tags",
235
- type: "multiselect",
236
- options: [
237
- { label: "JavaScript", value: "js" },
238
- { label: "TypeScript", value: "ts" },
239
- { label: "Python", value: "py" }
240
- ]
241
- }
242
- ```
243
-
244
- ### Email
245
-
246
- ```typescript
247
- {
248
- name: "email",
249
- label: "Email",
250
- type: "email",
251
- required: true
252
- }
253
- ```
254
-
255
- ### URL
256
-
257
- ```typescript
258
- {
259
- name: "website",
260
- label: "Website",
261
- type: "url",
262
- placeholder: "https://example.com"
263
- }
264
- ```
265
-
266
- ### Date
267
-
268
- ```typescript
269
- {
270
- name: "birthdate",
271
- label: "Birth Date",
272
- type: "date"
273
- }
274
- ```
275
-
276
- ## Validation
277
-
278
- ### Built-in Validators
279
-
280
- ```typescript
281
- {
282
- name: "username",
283
- label: "Username",
284
- type: "text",
285
- validation: {
286
- minLength: 3,
287
- maxLength: 20,
288
- pattern: "^[a-zA-Z0-9_]+$",
289
- errorMessage: "Username must be 3-20 alphanumeric characters"
290
- }
291
- }
292
- ```
293
-
294
- ### Custom Validators
295
-
296
- ```typescript
297
- {
298
- name: "password",
299
- label: "Password",
300
- type: "text",
301
- validation: {
302
- customValidator: (value) => {
303
- const hasUpper = /[A-Z]/.test(value);
304
- const hasLower = /[a-z]/.test(value);
305
- const hasNumber = /[0-9]/.test(value);
306
-
307
- if (!hasUpper || !hasLower || !hasNumber) {
308
- return "Password must contain uppercase, lowercase, and numbers";
309
- }
310
-
311
- return true; // Valid
312
- }
313
- }
314
- }
315
- ```
316
-
317
- ### Using ValidationBuilder
318
-
319
- ```typescript
320
- import { validation } from "@leanmcp/elicitation";
321
-
322
- validation()
323
- .minLength(8)
324
- .maxLength(100)
325
- .pattern("^[a-zA-Z0-9]+$")
326
- .customValidator((value) => value !== "admin")
327
- .errorMessage("Invalid input")
328
- .build()
329
- ```
330
-
331
- ## How It Works
332
-
333
- 1. **Client calls tool** with missing required fields
334
- 2. **Decorator intercepts** the method call before execution
335
- 3. **Elicitation check** determines if required fields are missing
336
- 4. **Elicitation request returned** if fields are missing
337
- 5. **Client displays form** to collect user input
338
- 6. **Client calls tool again** with complete arguments
339
- 7. **Method executes** normally with all required fields
340
-
341
- **Key Benefits:**
342
- - **Automatic interception** - No need to modify `@leanmcp/core`
343
- - **Clean separation** - Elicitation logic separate from business logic
344
- - **MCP compliant** - Follows MCP elicitation protocol
345
- - **Type-safe** - Full TypeScript support
346
-
347
- ## Strategies
348
-
349
- ### Form Strategy (Default)
350
-
351
- Collect all fields at once:
352
-
353
- ```typescript
354
- @Elicitation({
355
- strategy: "form", // or omit, form is default
356
- title: "User Information",
357
- fields: [/* ... */]
358
- })
359
- ```
360
-
361
- ### Multi-Step Strategy
362
-
363
- Break input collection into sequential steps:
364
-
365
- ```typescript
366
- @Elicitation({
367
- strategy: "multi-step",
368
- builder: () => [
369
- {
370
- title: "Step 1: Basic Info",
371
- fields: [/* step 1 fields */]
372
- },
373
- {
374
- title: "Step 2: Details",
375
- fields: [/* step 2 fields */],
376
- condition: (prev) => prev.needsDetails === true
377
- }
378
- ]
379
- })
380
- ```
381
-
382
- ## Elicitation Flow
383
-
384
- ### Request/Response Cycle
385
-
386
- **First Call (Missing Fields):**
387
- ```json
388
- // Request
389
- {
390
- "method": "tools/call",
391
- "params": {
392
- "name": "createChannel",
393
- "arguments": {}
394
- }
395
- }
396
-
397
- // Response (Elicitation Request)
398
- {
399
- "content": [{
400
- "type": "text",
401
- "text": "{\n \"type\": \"elicitation\",\n \"title\": \"Create Channel\",\n \"fields\": [...]\n}"
402
- }]
403
- }
404
- ```
405
-
406
- **Second Call (Complete Fields):**
407
- ```json
408
- // Request
409
- {
410
- "method": "tools/call",
411
- "params": {
412
- "name": "createChannel",
413
- "arguments": {
414
- "channelName": "my-channel",
415
- "isPrivate": false
416
- }
417
- }
418
- }
419
-
420
- // Response (Tool Result)
421
- {
422
- "content": [{
423
- "type": "text",
424
- "text": "{\"success\": true, \"channelId\": \"C123\"}"
425
- }]
426
- }
427
- ```
428
-
429
- ## API Reference
430
-
431
- ### ElicitationConfig
432
-
433
- ```typescript
434
- interface ElicitationConfig {
435
- strategy?: 'form' | 'multi-step';
436
- title?: string;
437
- description?: string;
438
- fields?: ElicitationField[];
439
- condition?: (args: any) => boolean;
440
- builder?: (context: ElicitationContext) => ElicitationRequest | ElicitationStep[];
441
- }
442
- ```
443
-
444
- ### ElicitationField
445
-
446
- ```typescript
447
- interface ElicitationField {
448
- name: string;
449
- label: string;
450
- type: 'text' | 'number' | 'boolean' | 'select' | 'multiselect' | 'date' | 'email' | 'url' | 'textarea';
451
- description?: string;
452
- required?: boolean;
453
- defaultValue?: any;
454
- options?: Array<{ label: string; value: any }>;
455
- validation?: FieldValidation;
456
- placeholder?: string;
457
- helpText?: string;
458
- }
459
- ```
460
-
461
- ### FieldValidation
462
-
463
- ```typescript
464
- interface FieldValidation {
465
- min?: number;
466
- max?: number;
467
- minLength?: number;
468
- maxLength?: number;
469
- pattern?: string;
470
- customValidator?: (value: any) => boolean | string;
471
- errorMessage?: string;
472
- }
473
- ```
474
-
475
- ## Complete Example
476
-
477
- See [examples/slack-with-elicitation](../../examples/slack-with-elicitation) for a complete working example.
478
-
479
- ```typescript
480
- import { createHTTPServer, MCPServer, Tool } from "@leanmcp/core";
481
- import { Elicitation, ElicitationFormBuilder, validation } from "@leanmcp/elicitation";
482
-
483
- class SlackService {
484
- @Tool({ description: "Create a new Slack channel" })
485
- @Elicitation({
486
- title: "Create Channel",
487
- description: "Please provide channel details",
488
- fields: [
489
- {
490
- name: "channelName",
491
- label: "Channel Name",
492
- type: "text",
493
- required: true,
494
- validation: {
495
- pattern: "^[a-z0-9-]+$",
496
- errorMessage: "Must be lowercase alphanumeric with hyphens"
497
- }
498
- },
499
- {
500
- name: "isPrivate",
501
- label: "Private Channel",
502
- type: "boolean",
503
- defaultValue: false
504
- }
505
- ]
506
- })
507
- async createChannel(args: { channelName: string; isPrivate: boolean }) {
508
- return {
509
- success: true,
510
- channelId: `C${Date.now()}`,
511
- channelName: args.channelName
512
- };
513
- }
514
- }
515
-
516
- // Start server
517
- const serverFactory = () => {
518
- const server = new MCPServer({ name: "slack-server", version: "1.0.0" });
519
- server.registerService(new SlackService());
520
- return server.getServer();
521
- };
522
-
523
- await createHTTPServer(serverFactory, { port: 3000 });
524
- ```
525
-
526
- ## Error Handling
527
-
528
- ```typescript
529
- try {
530
- const result = await service.createChannel({ channelName: "test" });
531
- console.log(result);
532
- } catch (error) {
533
- console.error('Tool execution failed:', error);
534
- }
535
- ```
536
-
537
- If elicitation is needed, the method returns an `ElicitationRequest` object instead of throwing an error.
538
-
539
- ## Best Practices
540
-
541
- 1. **Use conditional elicitation** - Only ask when truly needed
542
- 2. **Provide sensible defaults** - Reduce user input burden
543
- 3. **Clear field labels** - Make fields self-explanatory
544
- 4. **Validate early** - Catch errors before submission
545
- 5. **Group related fields** - Use multi-step for complex forms
546
- 6. **Test thoroughly** - Test both elicitation and execution paths
547
- 7. **Use builder for complex forms** - Fluent API is more maintainable
548
- 8. **Add help text** - Guide users with helpful descriptions
549
-
550
- ## Related Packages
551
-
552
- - [@leanmcp/core](../core) - Core MCP server functionality
553
- - [@leanmcp/auth](../auth) - Authentication for MCP tools
554
- - [@leanmcp/utils](../utils) - Utility functions
555
-
556
- ## Links
557
-
558
- - [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
559
- - [Documentation](https://github.com/LeanMCP/leanmcp-sdk#readme)
560
- - [Example Implementation](../../examples/slack-with-elicitation)
561
-
562
- ## License
563
-
564
- 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/elicitation</strong><br/>
11
+ Structured user input collection for MCP tools using the elicitation protocol.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@leanmcp/elicitation">
16
+ <img src="https://img.shields.io/npm/v/@leanmcp/elicitation" alt="npm version" />
17
+ </a>
18
+ <a href="https://www.npmjs.com/package/@leanmcp/elicitation">
19
+ <img src="https://img.shields.io/npm/dm/@leanmcp/elicitation" alt="npm downloads" />
20
+ </a>
21
+ <a href="https://docs.leanmcp.com/sdk/elicitation">
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
+ - **@Elicitation Decorator** — Automatically collect missing user inputs before tool execution
35
+ - **Fluent Builder API** — Programmatic form creation with `ElicitationFormBuilder`
36
+ - **Multiple Strategies** — Form and multi-step elicitation
37
+ - **Built-in Validation** — min/max, pattern matching, custom validators
38
+ - **Conditional Elicitation** — Only ask for inputs when needed
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install @leanmcp/elicitation @leanmcp/core
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ### Simple Form Elicitation
49
+
50
+ ```typescript
51
+ import { Tool } from "@leanmcp/core";
52
+ import { Elicitation } from "@leanmcp/elicitation";
53
+
54
+ class SlackService {
55
+ @Tool({ description: "Create a new Slack channel" })
56
+ @Elicitation({
57
+ title: "Create Channel",
58
+ description: "Please provide channel details",
59
+ fields: [
60
+ {
61
+ name: "channelName",
62
+ label: "Channel Name",
63
+ type: "text",
64
+ required: true,
65
+ validation: {
66
+ pattern: "^[a-z0-9-]+$",
67
+ errorMessage: "Must be lowercase alphanumeric with hyphens"
68
+ }
69
+ },
70
+ {
71
+ name: "isPrivate",
72
+ label: "Private Channel",
73
+ type: "boolean",
74
+ defaultValue: false
75
+ }
76
+ ]
77
+ })
78
+ async createChannel(args: { channelName: string; isPrivate: boolean }) {
79
+ return { success: true, channelName: args.channelName };
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### How It Works
85
+
86
+ 1. **Client calls tool** with missing required fields
87
+ 2. **Decorator intercepts** and checks for missing fields
88
+ 3. **Elicitation request returned** with form definition
89
+ 4. **Client displays form** to collect user input
90
+ 5. **Client calls tool again** with complete arguments
91
+ 6. **Method executes** normally
92
+
93
+ ---
94
+
95
+ ## Fluent Builder API
96
+
97
+ For more complex forms, use `ElicitationFormBuilder`:
98
+
99
+ ```typescript
100
+ import { Tool } from "@leanmcp/core";
101
+ import { Elicitation, ElicitationFormBuilder, validation } from "@leanmcp/elicitation";
102
+
103
+ class UserService {
104
+ @Tool({ description: "Create user account" })
105
+ @Elicitation({
106
+ builder: () => new ElicitationFormBuilder()
107
+ .title("User Registration")
108
+ .description("Create a new user account")
109
+ .addEmailField("email", "Email Address", { required: true })
110
+ .addTextField("username", "Username", {
111
+ required: true,
112
+ validation: validation()
113
+ .minLength(3)
114
+ .maxLength(20)
115
+ .pattern("^[a-zA-Z0-9_]+$")
116
+ .build()
117
+ })
118
+ .addSelectField("role", "Role", [
119
+ { label: "Admin", value: "admin" },
120
+ { label: "User", value: "user" }
121
+ ])
122
+ .build()
123
+ })
124
+ async createUser(args: any) {
125
+ return { success: true, email: args.email };
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Builder Methods
131
+
132
+ | Method | Description |
133
+ |--------|-------------|
134
+ | `title(string)` | Set form title |
135
+ | `description(string)` | Set form description |
136
+ | `condition(fn)` | Set condition for elicitation |
137
+ | `addTextField(name, label, opts?)` | Add text input |
138
+ | `addTextAreaField(name, label, opts?)` | Add textarea |
139
+ | `addNumberField(name, label, opts?)` | Add number input |
140
+ | `addBooleanField(name, label, opts?)` | Add checkbox |
141
+ | `addSelectField(name, label, options, opts?)` | Add dropdown |
142
+ | `addMultiSelectField(name, label, options, opts?)` | Add multi-select |
143
+ | `addEmailField(name, label, opts?)` | Add email input |
144
+ | `addUrlField(name, label, opts?)` | Add URL input |
145
+ | `addDateField(name, label, opts?)` | Add date picker |
146
+ | `addCustomField(field)` | Add custom field |
147
+ | `build()` | Build final config |
148
+
149
+ ---
150
+
151
+ ## Conditional Elicitation
152
+
153
+ Only ask for inputs when needed:
154
+
155
+ ```typescript
156
+ @Tool({ description: "Send message to Slack" })
157
+ @Elicitation({
158
+ condition: (args) => !args.channelId,
159
+ title: "Select Channel",
160
+ fields: [
161
+ {
162
+ name: "channelId",
163
+ label: "Channel",
164
+ type: "select",
165
+ required: true,
166
+ options: [
167
+ { label: "#general", value: "C12345" },
168
+ { label: "#random", value: "C67890" }
169
+ ]
170
+ }
171
+ ]
172
+ })
173
+ async sendMessage(args: { channelId?: string; message: string }) {
174
+ // Only elicits if channelId is missing
175
+ }
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Multi-Step Elicitation
181
+
182
+ Break input collection into sequential steps:
183
+
184
+ ```typescript
185
+ @Tool({ description: "Deploy application" })
186
+ @Elicitation({
187
+ strategy: "multi-step",
188
+ builder: () => [
189
+ {
190
+ title: "Step 1: Environment",
191
+ fields: [
192
+ {
193
+ name: "environment",
194
+ label: "Environment",
195
+ type: "select",
196
+ required: true,
197
+ options: [
198
+ { label: "Production", value: "prod" },
199
+ { label: "Staging", value: "staging" }
200
+ ]
201
+ }
202
+ ]
203
+ },
204
+ {
205
+ title: "Step 2: Configuration",
206
+ fields: [
207
+ {
208
+ name: "replicas",
209
+ label: "Replicas",
210
+ type: "number",
211
+ defaultValue: 3
212
+ }
213
+ ],
214
+ condition: (prev) => prev.environment === "prod"
215
+ }
216
+ ]
217
+ })
218
+ async deployApp(args: any) {
219
+ // Implementation
220
+ }
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Field Types
226
+
227
+ | Type | Description |
228
+ |------|-------------|
229
+ | `text` | Single-line text input |
230
+ | `textarea` | Multi-line text area |
231
+ | `number` | Numeric input |
232
+ | `boolean` | Checkbox |
233
+ | `select` | Dropdown (single choice) |
234
+ | `multiselect` | Multi-select |
235
+ | `email` | Email input |
236
+ | `url` | URL input |
237
+ | `date` | Date picker |
238
+
239
+ ---
240
+
241
+ ## Validation
242
+
243
+ ### Built-in Validation
244
+
245
+ ```typescript
246
+ {
247
+ name: "username",
248
+ label: "Username",
249
+ type: "text",
250
+ validation: {
251
+ minLength: 3,
252
+ maxLength: 20,
253
+ pattern: "^[a-zA-Z0-9_]+$",
254
+ errorMessage: "Username must be 3-20 alphanumeric characters"
255
+ }
256
+ }
257
+ ```
258
+
259
+ ### Using ValidationBuilder
260
+
261
+ ```typescript
262
+ import { validation } from "@leanmcp/elicitation";
263
+
264
+ validation()
265
+ .minLength(8)
266
+ .maxLength(100)
267
+ .pattern("^[a-zA-Z0-9]+$")
268
+ .customValidator((value) => value !== "admin")
269
+ .errorMessage("Invalid input")
270
+ .build()
271
+ ```
272
+
273
+ ---
274
+
275
+ ## API Reference
276
+
277
+ ### ElicitationConfig
278
+
279
+ ```typescript
280
+ interface ElicitationConfig {
281
+ strategy?: 'form' | 'multi-step';
282
+ title?: string;
283
+ description?: string;
284
+ fields?: ElicitationField[];
285
+ condition?: (args: any) => boolean;
286
+ builder?: (context: ElicitationContext) => ElicitationRequest | ElicitationStep[];
287
+ }
288
+ ```
289
+
290
+ ### ElicitationField
291
+
292
+ ```typescript
293
+ interface ElicitationField {
294
+ name: string;
295
+ label: string;
296
+ type: 'text' | 'number' | 'boolean' | 'select' | 'multiselect' | 'date' | 'email' | 'url' | 'textarea';
297
+ description?: string;
298
+ required?: boolean;
299
+ defaultValue?: any;
300
+ options?: Array<{ label: string; value: any }>;
301
+ validation?: FieldValidation;
302
+ placeholder?: string;
303
+ helpText?: string;
304
+ }
305
+ ```
306
+
307
+ ### FieldValidation
308
+
309
+ ```typescript
310
+ interface FieldValidation {
311
+ min?: number;
312
+ max?: number;
313
+ minLength?: number;
314
+ maxLength?: number;
315
+ pattern?: string;
316
+ customValidator?: (value: any) => boolean | string;
317
+ errorMessage?: string;
318
+ }
319
+ ```
320
+
321
+ ---
322
+
323
+ ## Best Practices
324
+
325
+ 1. **Use conditional elicitation** — Only ask when truly needed
326
+ 2. **Provide sensible defaults** — Reduce user input burden
327
+ 3. **Clear field labels** — Make fields self-explanatory
328
+ 4. **Validate early** — Catch errors before submission
329
+ 5. **Use builder for complex forms** — Fluent API is more maintainable
330
+
331
+ ---
332
+
333
+ ## Documentation
334
+
335
+ - [Full Documentation](https://docs.leanmcp.com/sdk/elicitation)
336
+
337
+ ## Related Packages
338
+
339
+ - [@leanmcp/core](https://www.npmjs.com/package/@leanmcp/core) Core MCP server functionality
340
+ - [@leanmcp/auth](https://www.npmjs.com/package/@leanmcp/auth) — Authentication decorators
341
+ - [@leanmcp/cli](https://www.npmjs.com/package/@leanmcp/cli) — CLI for project scaffolding
342
+
343
+ ## Links
344
+
345
+ - [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
346
+ - [NPM Package](https://www.npmjs.com/package/@leanmcp/elicitation)
347
+ - [MCP Specification](https://spec.modelcontextprotocol.io/)
348
+
349
+ ## License
350
+
351
+ MIT
package/package.json CHANGED
@@ -1,58 +1,64 @@
1
- {
2
- "name": "@leanmcp/elicitation",
3
- "version": "0.1.0",
4
- "description": "Elicitation support for LeanMCP - structured user input collection",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "require": "./dist/index.js",
12
- "import": "./dist/index.mjs"
13
- }
14
- },
15
- "files": [
16
- "dist",
17
- "README.md",
18
- "LICENSE"
19
- ],
20
- "scripts": {
21
- "build": "tsup src/index.ts --format cjs,esm --dts",
22
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
23
- "clean": "rm -rf dist",
24
- "test": "jest",
25
- "lint": "eslint src"
26
- },
27
- "dependencies": {
28
- "reflect-metadata": "^0.2.0"
29
- },
30
- "peerDependencies": {
31
- "@leanmcp/core": "*"
32
- },
33
- "keywords": [
34
- "mcp",
35
- "model-context-protocol",
36
- "typescript",
37
- "decorators",
38
- "elicitation",
39
- "forms",
40
- "validation",
41
- "user-input",
42
- "leanmcp"
43
- ],
44
- "repository": {
45
- "type": "git",
46
- "url": "git+https://github.com/LeanMCP/leanmcp-sdk.git",
47
- "directory": "packages/elicitation"
48
- },
49
- "homepage": "https://github.com/LeanMCP/leanmcp-sdk#readme",
50
- "bugs": {
51
- "url": "https://github.com/LeanMCP/leanmcp-sdk/issues"
52
- },
53
- "author": "LeanMCP <admin@leanmcp.com>",
54
- "license": "MIT",
55
- "publishConfig": {
56
- "access": "public"
57
- }
58
- }
1
+ {
2
+ "name": "@leanmcp/elicitation",
3
+ "version": "0.1.2",
4
+ "description": "Elicitation support for LeanMCP - structured user input collection",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.js",
12
+ "import": "./dist/index.mjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts --format cjs,esm --dts",
22
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
23
+ "clean": "rm -rf dist",
24
+ "test": "jest --passWithNoTests",
25
+ "lint": "eslint src"
26
+ },
27
+ "dependencies": {
28
+ "reflect-metadata": "^0.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/jest": "^29.5.0",
32
+ "@types/node": "^20.0.0",
33
+ "jest": "^29.7.0",
34
+ "ts-jest": "^29.1.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@leanmcp/core": "*"
38
+ },
39
+ "keywords": [
40
+ "mcp",
41
+ "model-context-protocol",
42
+ "typescript",
43
+ "decorators",
44
+ "elicitation",
45
+ "forms",
46
+ "validation",
47
+ "user-input",
48
+ "leanmcp"
49
+ ],
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/LeanMCP/leanmcp-sdk.git",
53
+ "directory": "packages/elicitation"
54
+ },
55
+ "homepage": "https://github.com/LeanMCP/leanmcp-sdk#readme",
56
+ "bugs": {
57
+ "url": "https://github.com/LeanMCP/leanmcp-sdk/issues"
58
+ },
59
+ "author": "LeanMCP <admin@leanmcp.com>",
60
+ "license": "MIT",
61
+ "publishConfig": {
62
+ "access": "public"
63
+ }
64
+ }