Elicitation
This guide explains how to use elicitation on the server side to request additional user input or external actions during tool execution.
What is Elicitation?
Elicitation allows a server tool to:
- Request structured user input (forms with schema validation)
- Ask the client to perform an external action (e.g. open a payment URL)
- Pause execution until the elicitation is accepted, rejected, or completed
Typical use cases:
- Collecting contact or configuration data
- User confirmation steps
- Payments or OAuth-style redirects
To use elicitation, inject Context into your tool handler and call the elicit() method with either form of URL elicit request params.
Defining a Form Elicitation
Forms use a JSON schema to define and validate structured input.
#[json_schema(de)]
struct Contact {
name: String,
email: String,
age: u32,
}
With #[json_schema] attribute macro you can control the serialization/deserialization performed by serde for your struct:
all- Applies alsoderive(serde::Serialize, serde::Deserialize).serde- Applies alsoderive(serde::Serialize, serde::Deserialize).ser- Applies alsoderive(serde::Serialize).de- Applies alsoderive(serde::Deserialize).
Creating and Sending a Form Request
To create elicit request form params you need to use the ElicitRequestParams::form() method with the following with_contract() that specifies the expected JSON schema.
#[tool]
async fn generate_business_card(mut ctx: Context) -> Result<String, Error> {
let params = ElicitRequestParams::form(
"Please provide your contact information"
)
.with_schema::<Contact>();
ctx.elicit(params.into())
.await?
.map(format_contact)
}
fn format_contact(c: Contact) -> String {
format!("Name: {}, Age: {}, email: {}", c.name, c.age, c.email)
}
Execution flow:
- Server sends a form elicitation request
- Client receives and validates the data
- Tool resumes with the validated payload
- The result is mapped to the tool output
Defining a URL Elicitation
URL elicitations are used when the user must perform an external action. You can create the ElicitRequestUrlParams by leveraging the ElicitRequestParams::url() method.
#[tool]
async fn pay_a_bill(mut ctx: Context) -> Result<&'static str, Error> {
let params = ElicitRequestParams::url(
"https://www.paypal.com/us/webapps/mpp/paypal-payment",
"Please pay your bill using PayPal"
);
let elicitation_id = params.id.clone();
ctx.elicit(params.into()).await?;
// Send the `notifications/elicitation/complete`
ctx.complete_elicitation(elicitation_id).await?;
Ok("Payment successful")
}
You may also send a notifications/elicitation/complete notification when an out-of-band interaction is completed. This allows clients to react programmatically if appropriate.
- The server controls when the elicitation is considered completed
- The client only confirms acceptance
- Useful for payments, SSO, external confirmations
Learn By Example
A complete working example is available here.