Skip to content

Commit 9736258

Browse files
author
Cloudflare AI Agent
committed
docs: add MCP elicitation guide and transport storage API
Synced from cloudflare/agents#620 Changes: - Add comprehensive elicitation documentation with examples - Document new WorkerTransport storage API for session persistence - Update transport docs with MCPStorageApi and TransportState interfaces - Simplify MCP elicitation example (removed complex UI demo) Related PR: cloudflare/agents#620
1 parent 620eb23 commit 9736258

File tree

2 files changed

+366
-87
lines changed

2 files changed

+366
-87
lines changed
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
---
2+
pcx_content_type: concept
3+
title: Elicitation
4+
tags:
5+
- MCP
6+
sidebar:
7+
order: 7
8+
---
9+
10+
import { Render, TypeScriptExample } from "~/components";
11+
12+
[Elicitation](https://spec.modelcontextprotocol.io/specification/2025-03-26/client/elicitation/) is an MCP feature that allows servers to request additional input from users during tool execution. This enables interactive workflows where tools can prompt users for clarification, confirmation, or additional details.
13+
14+
## What is Elicitation?
15+
16+
Elicitation provides a standardized way for MCP servers to:
17+
18+
- Request user confirmation before performing sensitive operations
19+
- Collect additional information that wasn't included in the initial tool call
20+
- Present forms for structured data input
21+
- Enable interactive, multi-step workflows
22+
23+
The MCP client (like Claude Desktop) presents elicitation requests to users through appropriate UI elements (buttons, forms, etc.) and returns the user's response to the server.
24+
25+
## Using Elicitation in MCP Servers
26+
27+
When building MCP servers with the Agents SDK, you can use the built-in `elicitInput()` method from the MCP SDK to request user input during tool execution.
28+
29+
### Basic Example
30+
31+
Here's a simple example that uses elicitation to request user confirmation and collect additional information:
32+
33+
<TypeScriptExample>
34+
35+
```ts title="src/index.ts"
36+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
37+
import { createMcpHandler, WorkerTransport, type TransportState } from "agents/mcp";
38+
import { Agent, getAgentByName } from "agents";
39+
import { z } from "zod";
40+
import { CfWorkerJsonSchemaValidator } from "@modelcontextprotocol/sdk/validation/cfworker-provider.js";
41+
42+
const STATE_KEY = "mcp_transport_state";
43+
44+
type Env = {
45+
MyAgent: DurableObjectNamespace<MyAgent>;
46+
};
47+
48+
interface State {
49+
counter: number;
50+
}
51+
52+
export class MyAgent extends Agent<Env, State> {
53+
server = new McpServer(
54+
{
55+
name: "elicitation-demo",
56+
version: "1.0.0"
57+
},
58+
{
59+
jsonSchemaValidator: new CfWorkerJsonSchemaValidator()
60+
}
61+
);
62+
63+
// Create transport with storage for session persistence
64+
transport = new WorkerTransport({
65+
sessionIdGenerator: () => this.name,
66+
storage: {
67+
get: () => {
68+
return this.ctx.storage.kv.get<TransportState>(STATE_KEY);
69+
},
70+
set: (state: TransportState) => {
71+
this.ctx.storage.kv.put<TransportState>(STATE_KEY, state);
72+
}
73+
}
74+
});
75+
76+
initialState = {
77+
counter: 0
78+
};
79+
80+
onStart(): void | Promise<void> {
81+
// Register a tool that uses elicitation
82+
this.server.registerTool(
83+
"increase-counter",
84+
{
85+
description: "Increase the counter with user confirmation",
86+
inputSchema: {
87+
confirm: z.boolean().describe("Do you want to increase the counter?")
88+
}
89+
},
90+
async ({ confirm }) => {
91+
if (!confirm) {
92+
return {
93+
content: [{ type: "text", text: "Counter increase cancelled." }]
94+
};
95+
}
96+
97+
try {
98+
// Use elicitation to ask for the increment amount
99+
const result = await this.server.server.elicitInput({
100+
message: "By how much do you want to increase the counter?",
101+
requestedSchema: {
102+
type: "object",
103+
properties: {
104+
amount: {
105+
type: "number",
106+
title: "Amount",
107+
description: "The amount to increase the counter by",
108+
minimum: 1
109+
}
110+
},
111+
required: ["amount"]
112+
}
113+
});
114+
115+
// Handle the user's response
116+
if (result.action !== "accept" || !result.content) {
117+
return {
118+
content: [{ type: "text", text: "Counter increase cancelled." }]
119+
};
120+
}
121+
122+
// Update state with the user-provided amount
123+
const amount = Number(result.content.amount);
124+
if (amount) {
125+
this.setState({
126+
...this.state,
127+
counter: this.state.counter + amount
128+
});
129+
130+
return {
131+
content: [
132+
{
133+
type: "text",
134+
text: `Counter increased by ${amount}, current value is ${this.state.counter}`
135+
}
136+
]
137+
};
138+
}
139+
140+
return {
141+
content: [
142+
{ type: "text", text: "Invalid amount provided." }
143+
]
144+
};
145+
} catch (error) {
146+
console.error(error);
147+
return {
148+
content: [{ type: "text", text: "Counter increase failed." }]
149+
};
150+
}
151+
}
152+
);
153+
}
154+
155+
async onMcpRequest(request: Request) {
156+
return createMcpHandler(this.server, {
157+
transport: this.transport
158+
})(request, this.env, {} as ExecutionContext);
159+
}
160+
}
161+
162+
export default {
163+
async fetch(request: Request, env: Env, _ctx: ExecutionContext) {
164+
const sessionId = request.headers.get("mcp-session-id") ?? crypto.randomUUID();
165+
const agent = await getAgentByName(env.MyAgent, sessionId);
166+
return await agent.onMcpRequest(request);
167+
}
168+
};
169+
```
170+
171+
</TypeScriptExample>
172+
173+
## Elicitation Response Schema
174+
175+
The `requestedSchema` follows JSON Schema format and supports various input types:
176+
177+
### Boolean Confirmation
178+
179+
```ts
180+
{
181+
type: "object",
182+
properties: {
183+
confirmed: {
184+
type: "boolean",
185+
title: "Confirm action",
186+
description: "Check to confirm"
187+
}
188+
},
189+
required: ["confirmed"]
190+
}
191+
```
192+
193+
### Text Input
194+
195+
```ts
196+
{
197+
type: "object",
198+
properties: {
199+
username: {
200+
type: "string",
201+
title: "Username",
202+
description: "Enter your username"
203+
},
204+
email: {
205+
type: "string",
206+
format: "email",
207+
title: "Email Address"
208+
}
209+
},
210+
required: ["username", "email"]
211+
}
212+
```
213+
214+
### Dropdown Selection
215+
216+
```ts
217+
{
218+
type: "object",
219+
properties: {
220+
role: {
221+
type: "string",
222+
title: "User Role",
223+
enum: ["viewer", "editor", "admin"],
224+
enumNames: ["Viewer", "Editor", "Administrator"]
225+
}
226+
},
227+
required: ["role"]
228+
}
229+
```
230+
231+
### Numeric Input
232+
233+
```ts
234+
{
235+
type: "object",
236+
properties: {
237+
amount: {
238+
type: "number",
239+
title: "Amount",
240+
description: "Enter an amount",
241+
minimum: 1,
242+
maximum: 100
243+
}
244+
},
245+
required: ["amount"]
246+
}
247+
```
248+
249+
## Handling Elicitation Responses
250+
251+
The `elicitInput()` method returns a result with an `action` field:
252+
253+
- `"accept"`: User accepted and provided data (available in `result.content`)
254+
- `"decline"`: User explicitly declined the request
255+
- `"cancel"`: User dismissed the request without a choice
256+
257+
```ts
258+
const result = await this.server.server.elicitInput({
259+
message: "Are you sure?",
260+
requestedSchema: { /* ... */ }
261+
});
262+
263+
if (result.action === "accept" && result.content) {
264+
// User accepted and provided data
265+
const userData = result.content;
266+
// Process the data...
267+
} else if (result.action === "decline") {
268+
// User explicitly declined
269+
return { content: [{ type: "text", text: "Action declined." }] };
270+
} else {
271+
// User cancelled (dismissed)
272+
return { content: [{ type: "text", text: "Action cancelled." }] };
273+
}
274+
```
275+
276+
## Session Persistence with Storage
277+
278+
When using elicitation with the Agents SDK, you should provide a storage implementation to persist session state across requests. This is important because elicitation involves multiple round trips between the server and client.
279+
280+
The storage API uses two methods:
281+
282+
- `get()`: Retrieve stored transport state
283+
- `set(state)`: Persist transport state
284+
285+
```ts
286+
const transport = new WorkerTransport({
287+
sessionIdGenerator: () => this.name,
288+
storage: {
289+
get: () => {
290+
return this.ctx.storage.kv.get<TransportState>(STATE_KEY);
291+
},
292+
set: (state: TransportState) => {
293+
this.ctx.storage.kv.put<TransportState>(STATE_KEY, state);
294+
}
295+
}
296+
});
297+
```
298+
299+
This ensures that:
300+
- Session IDs are preserved across requests
301+
- The transport state remains consistent during elicitation flows
302+
- Multiple elicitation requests can be handled sequentially
303+
304+
## Best Practices
305+
306+
1. **Validate user input**: Always validate the data returned from elicitation requests before using it.
307+
308+
2. **Provide clear messages**: Use descriptive messages and titles to explain what information you're requesting and why.
309+
310+
3. **Handle all response types**: Check for `accept`, `decline`, and `cancel` actions to handle all user responses appropriately.
311+
312+
4. **Use appropriate input types**: Choose the right schema type (boolean, string, number, enum) for the data you need.
313+
314+
5. **Keep forms simple**: Request only essential information in each elicitation to avoid overwhelming users.
315+
316+
6. **Provide defaults**: Use JSON Schema defaults where appropriate to suggest common values.
317+
318+
## Client Support
319+
320+
Elicitation requires MCP client support. Currently supported by:
321+
322+
- Claude Desktop (version 0.7.0+)
323+
- Other MCP clients implementing the [MCP elicitation specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/client/elicitation/)
324+
325+
Check your MCP client's documentation for specific elicitation support details.
326+
327+
## Related Resources
328+
329+
- [MCP Elicitation Specification](https://spec.modelcontextprotocol.io/specification/2025-03-26/client/elicitation/)
330+
- [MCP Elicitation Example](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation)
331+
- [MCP Server API Reference](/agents/model-context-protocol/mcp-agent-api/)

0 commit comments

Comments
 (0)