Documentation Index Fetch the complete documentation index at: https://docs.leanmcp.com/llms.txt
Use this file to discover all available pages before exploring further.
@leanmcp/elicitation
Structured user input collection for LeanMCP tools using the MCP elicitation protocol. The @Elicitation decorator automatically intercepts tool calls to request missing required parameters from users.
Features
@Elicitation Decorator Automatically collect missing user inputs before tool execution
Fluent Builder API Programmatic form creation with ElicitationFormBuilder
Multiple Strategies Form and multi-step elicitation strategies
Built-in Validation min/max, pattern matching, custom validators
Installation
npm install @leanmcp/elicitation @leanmcp/core
Quick Start
import { Tool } from "@leanmcp/core" ;
import { Elicitation } from "@leanmcp/elicitation" ;
class SlackService {
@ Tool ({ description: "Create a new Slack channel" })
@ Elicitation ({
title: "Create Channel" ,
description: "Please provide channel details" ,
fields: [
{
name: "channelName" ,
label: "Channel Name" ,
type: "text" ,
required: true ,
validation: {
pattern: "^[a-z0-9-]+$" ,
errorMessage: "Must be lowercase alphanumeric with hyphens"
}
},
{
name: "isPrivate" ,
label: "Private Channel" ,
type: "boolean" ,
defaultValue: false
}
]
})
async createChannel ( args : { channelName : string ; isPrivate : boolean }) {
return { success: true , channelName: args . channelName };
}
}
How It Works
Client calls tool with missing required fields
Decorator intercepts and checks for missing fields
Elicitation request returned with form definition
Client displays form to collect user input
Client calls tool again with complete arguments
Method executes normally
Fluent Builder API
For more complex forms, use ElicitationFormBuilder:
import { Tool } from "@leanmcp/core" ;
import { Elicitation , ElicitationFormBuilder , validation } from "@leanmcp/elicitation" ;
class UserService {
@ Tool ({ description: "Create user account" })
@ Elicitation ({
builder : () => new ElicitationFormBuilder ()
. title ( "User Registration" )
. description ( "Create a new user account" )
. addEmailField ( "email" , "Email Address" , { required: true })
. addTextField ( "username" , "Username" , {
required: true ,
validation: validation ()
. minLength ( 3 )
. maxLength ( 20 )
. pattern ( "^[a-zA-Z0-9_]+$" )
. build ()
})
. addSelectField ( "role" , "Role" , [
{ label: "Admin" , value: "admin" },
{ label: "User" , value: "user" }
])
. build ()
})
async createUser ( args : any ) {
return { success: true , email: args . email };
}
}
Builder Methods
Method Description title(string)Set form title description(string)Set form description condition(fn)Set condition for elicitation addTextField(name, label, opts?)Add text input addTextAreaField(name, label, opts?)Add textarea addNumberField(name, label, opts?)Add number input addBooleanField(name, label, opts?)Add checkbox addSelectField(name, label, options, opts?)Add dropdown addMultiSelectField(name, label, options, opts?)Add multi-select addEmailField(name, label, opts?)Add email input addUrlField(name, label, opts?)Add URL input addDateField(name, label, opts?)Add date picker addCustomField(field)Add custom field build()Build final config
Conditional Elicitation
Only ask for inputs when needed:
@ Tool ({ description: "Send message to Slack" })
@ Elicitation ({
condition : ( args ) => ! args . channelId ,
title: "Select Channel" ,
fields: [
{
name: "channelId" ,
label: "Channel" ,
type: "select" ,
required: true ,
options: [
{ label: "#general" , value: "C12345" },
{ label: "#random" , value: "C67890" }
]
}
]
})
async sendMessage ( args : { channelId? : string ; message : string }) {
// Only elicits if channelId is missing
}
Multi-Step Elicitation
Break input collection into sequential steps:
@ Tool ({ description: "Deploy application" })
@ Elicitation ({
strategy: "multi-step" ,
builder : () => [
{
title: "Step 1: Environment" ,
fields: [
{
name: "environment" ,
label: "Environment" ,
type: "select" ,
required: true ,
options: [
{ label: "Production" , value: "prod" },
{ label: "Staging" , value: "staging" }
]
}
]
},
{
title: "Step 2: Configuration" ,
fields: [
{
name: "replicas" ,
label: "Replicas" ,
type: "number" ,
defaultValue: 3
}
],
condition : ( prev ) => prev . environment === "prod"
}
]
})
async deployApp ( args : any ) {
// Implementation
}
Field Types
Type Description textSingle-line text input textareaMulti-line text area numberNumeric input booleanCheckbox selectDropdown (single choice) multiselectMulti-select emailEmail input urlURL input dateDate picker
Validation
Built-in Validation
{
name : "username" ,
label : "Username" ,
type : "text" ,
validation : {
minLength : 3 ,
maxLength : 20 ,
pattern : "^[a-zA-Z0-9_]+$" ,
errorMessage : "Username must be 3-20 alphanumeric characters"
}
}
Using ValidationBuilder
import { validation } from "@leanmcp/elicitation" ;
validation ()
. minLength ( 8 )
. maxLength ( 100 )
. pattern ( "^[a-zA-Z0-9]+$" )
. customValidator (( value ) => value !== "admin" )
. errorMessage ( "Invalid input" )
. build ()
Elicitation Flow
Request/Response Cycle
First Call (Missing Fields):
// Request
{
"method" : "tools/call" ,
"params" : {
"name" : "createChannel" ,
"arguments" : {}
}
}
// Response (Elicitation Request)
{
"type" : "elicitation" ,
"title" : "Create Channel" ,
"fields" : [
{
"name" : "channelName" ,
"label" : "Channel Name" ,
"type" : "text" ,
"required" : true
}
]
}
Second Call (Complete Fields):
// Request
{
"method" : "tools/call" ,
"params" : {
"name" : "createChannel" ,
"arguments" : {
"channelName" : "my-channel" ,
"isPrivate" : false
}
}
}
// Response (Tool Result)
{
"content" : [{ "type" : "text" , "text" : "{ \" success \" : true}" }]
}
API Reference
ElicitationConfig
interface ElicitationConfig {
strategy ?: 'form' | 'multi-step' ;
title ?: string ;
description ?: string ;
fields ?: ElicitationField [];
condition ?: ( args : any ) => boolean ;
builder ?: ( context : ElicitationContext ) => ElicitationRequest | ElicitationStep [];
}
ElicitationField
interface ElicitationField {
name : string ;
label : string ;
type : 'text' | 'number' | 'boolean' | 'select' | 'multiselect' | 'date' | 'email' | 'url' | 'textarea' ;
description ?: string ;
required ?: boolean ;
defaultValue ?: any ;
options ?: Array <{ label : string ; value : any }>;
validation ?: FieldValidation ;
placeholder ?: string ;
helpText ?: string ;
}
FieldValidation
interface FieldValidation {
min ?: number ;
max ?: number ;
minLength ?: number ;
maxLength ?: number ;
pattern ?: string ;
customValidator ?: ( value : any ) => boolean | string ;
errorMessage ?: string ;
}
Best Practices
Use Conditional Elicitation
Only ask when truly needed using the condition option: @ Elicitation ({
condition : ( args ) => ! args . channelId ,
// ...
})
Provide Sensible Defaults
Reduce user input burden with defaultValue: {
name : "priority" ,
type : "select" ,
defaultValue : "normal" ,
options : [ ... ]
}
Use Builder for Complex Forms
Use helpText and placeholder to guide users: {
name : "email" ,
type : "email" ,
placeholder : "user@example.com" ,
helpText : "We'll send confirmation here"
}
Links