@leanmcp/elicitation 0.1.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/LICENSE +21 -0
- package/README.md +564 -0
- package/dist/index.d.mts +345 -0
- package/dist/index.d.ts +345 -0
- package/dist/index.js +700 -0
- package/dist/index.mjs +665 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +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.
|
package/README.md
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
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
|