Chapter 23: Advanced Communication: Elicitation
TL;DR
- What: Elicitation allows your backend tool to pause and ask the user for input mid-execution.
- Simple Method (Auto-Forms):
- From your backend tool, return an
elicitresponse. - In the response, provide a
requested_schemausing JSON Schema to define the form fields you need. - The Worka Host will auto-generate a form, show it to the user, and send their input back to your tool.
- From your backend tool, return an
- Advanced Method (Custom A2UI):
- From your backend, return an
elicitresponse with aworka_uipayload (A2UI JSON). - The Host renders that UI in a modal/surface instead of the auto-generated form.
- Your backend receives the structured response when the user submits.
- From your backend, return an
Sometimes, a backend tool needs more information from the user after it has already started running. For example, it might need an API key, a confirmation to proceed with a destructive action, or a choice from a dynamic list of options.
The Elicitation protocol is Worka's powerful solution for this. It allows a backend tool to pause its execution, request input from the user via the frontend, and then resume its work once that input is provided.
The Simple Way: Auto-Generated Forms
The easiest way to get user input is to let the Worka Host build the user interface for you. You do this by providing a schema for the data you need.
Step 1: The Backend Request
In your MCP server tool function, when you determine you need user input, you construct and return an elicit response. The key property of this response is requested_schema, where you define the inputs you need using the JSON Schema standard.
Example (conceptual Rust):
async fn configure_service(req: ToolRequest) -> ToolResponse {
// ... logic ...
// We need an API key and a region from the user.
let schema = json!({
"type": "object",
"properties": {
"api_key": { "type": "string", "description": "Your API Key" },
"region": { "type": "string", "enum": ["us-east-1", "eu-west-2"] }
},
"required": ["api_key"]
});
// Instead of ToolResponse::success, we elicit.
return ToolResponse::elicit(CreateElicitationRequestParam {
message: "Please configure the service".to_string(),
requested_schema: Some(schema),
// ...
});
}
Step 2: The Host Renders the Form
The Worka Host receives this elicit response. It inspects the requested_schema and dynamically generates a clean, user-friendly form with an "API Key" text input, a "Region" dropdown, and a submit button.
Step 3: Resuming Execution
When the user fills out the form and clicks submit, the Host sends the collected data back to your MCP server as the successful result of the elicitation. Your tool function, which was paused waiting for the elicitation to complete, now resumes with the data it needed to continue its work.
The Advanced Way: Custom A2UI
Sometimes, a generic form isn't enough. You might need a multi‑step OAuth flow, a custom file picker, or a branded UI. For this, you can provide a full A2UI payload in the elicitation response.
Step 1: The Backend Request (with A2UI payload)
This time, your backend sends an elicit request without a requested_schema. Instead, you include a worka_ui payload in the metadata.
Example (conceptual Rust):
// ...
let meta = Meta(serde_json::Map::from_iter(vec![(
"worka_ui".to_string(),
json!({
"type": "column",
"children": [
{ "type": "text", "props": { "text": "Connect your account" } },
{ "type": "button", "props": { "label": "Continue", "onClick": { "type": "submit" } } }
]
})
)]));
return ToolResponse::elicit(CreateElicitationRequestParam {
message: "Please connect your account".to_string(),
meta: Some(meta),
// No requested_schema this time
});
Step 2: Host Rendering
When the Host receives an elicitation with worka_ui, it renders that A2UI in a modal/surface. When the user submits, the structured response is delivered back to your tool.