Skip to content

Commit 96c9779

Browse files
author
Cloudflare Docs Bot
committed
docs: add MCP elicitation and transport configuration documentation
- Add comprehensive elicitation guide with examples - Document createMcpHandler configuration options - Add storage persistence documentation for transport state - Include code examples for all new features Related to cloudflare/agents#620
1 parent c7de600 commit 96c9779

File tree

2 files changed

+502
-0
lines changed

2 files changed

+502
-0
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/draft/client/elicitation/) is an MCP feature that allows servers to request user input during tool execution. This enables interactive workflows where tools can ask for confirmation, collect form data, or gather additional information before completing their operations.
13+
14+
## How elicitation works
15+
16+
When a tool needs user input, it calls `server.elicitInput()` with a message and a [JSON Schema](https://json-schema.org/) describing the expected input format. The MCP client presents this to the user (typically as a form or dialog), and returns the user's response to the server.
17+
18+
The user can respond in three ways:
19+
- **Accept**: Provide the requested information and continue
20+
- **Decline**: Explicitly reject the request
21+
- **Cancel**: Dismiss without making a choice
22+
23+
## Basic example
24+
25+
Here's a simple tool that asks for user confirmation before incrementing a counter:
26+
27+
<TypeScriptExample>
28+
29+
```ts title="src/index.ts"
30+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
31+
import { Agent } from "agents";
32+
import { createMcpHandler } from "agents/mcp";
33+
import { z } from "zod";
34+
35+
type Env = {
36+
MyAgent: DurableObjectNamespace<MyAgent>;
37+
};
38+
39+
interface State {
40+
counter: number;
41+
}
42+
43+
export class MyAgent extends Agent<Env, State> {
44+
server = new McpServer({
45+
name: "Elicitation Demo",
46+
version: "1.0.0"
47+
});
48+
49+
initialState = {
50+
counter: 0
51+
};
52+
53+
onStart() {
54+
this.server.registerTool(
55+
"increase-counter",
56+
{
57+
description: "Increase the counter",
58+
inputSchema: {
59+
confirm: z.boolean().describe("Do you want to increase the counter?")
60+
}
61+
},
62+
async ({ confirm }) => {
63+
if (!confirm) {
64+
return {
65+
content: [{ type: "text", text: "Counter increase cancelled." }]
66+
};
67+
}
68+
69+
// Request additional input via elicitation
70+
const result = await this.server.server.elicitInput({
71+
message: "By how much do you want to increase the counter?",
72+
requestedSchema: {
73+
type: "object",
74+
properties: {
75+
amount: {
76+
type: "number",
77+
title: "Amount",
78+
description: "The amount to increase the counter by",
79+
minimum: 1
80+
}
81+
},
82+
required: ["amount"]
83+
}
84+
});
85+
86+
if (result.action !== "accept" || !result.content) {
87+
return {
88+
content: [{ type: "text", text: "Counter increase cancelled." }]
89+
};
90+
}
91+
92+
const amount = Number(result.content.amount);
93+
this.setState({
94+
counter: this.state.counter + amount
95+
});
96+
97+
return {
98+
content: [
99+
{
100+
type: "text",
101+
text: `Counter increased by ${amount}, current value is ${this.state.counter}`
102+
}
103+
]
104+
};
105+
}
106+
);
107+
}
108+
109+
async onMcpRequest(request: Request) {
110+
return createMcpHandler(this.server)(request, this.env, {} as ExecutionContext);
111+
}
112+
}
113+
```
114+
115+
</TypeScriptExample>
116+
117+
## Elicitation response types
118+
119+
The `elicitInput()` method returns an `ElicitResult` with one of three action types:
120+
121+
```ts
122+
type ElicitResult = {
123+
action: "accept" | "decline" | "cancel";
124+
content?: Record<string, unknown>;
125+
};
126+
```
127+
128+
- **accept**: User provided the requested information (available in `content`)
129+
- **decline**: User explicitly rejected the request
130+
- **cancel**: User dismissed without making a choice
131+
132+
Always check the `action` before using `content`:
133+
134+
```ts
135+
const result = await this.server.server.elicitInput({
136+
message: "Enter your email",
137+
requestedSchema: {
138+
type: "object",
139+
properties: {
140+
email: { type: "string", format: "email" }
141+
},
142+
required: ["email"]
143+
}
144+
});
145+
146+
if (result.action === "accept" && result.content?.email) {
147+
// Use result.content.email
148+
} else {
149+
// Handle decline or cancel
150+
}
151+
```
152+
153+
## Schema types
154+
155+
Elicitation supports various input types through JSON Schema:
156+
157+
### Boolean (confirmation)
158+
159+
```ts
160+
requestedSchema: {
161+
type: "object",
162+
properties: {
163+
confirmed: {
164+
type: "boolean",
165+
title: "Confirm action",
166+
description: "Check to confirm"
167+
}
168+
}
169+
}
170+
```
171+
172+
### Text input
173+
174+
```ts
175+
requestedSchema: {
176+
type: "object",
177+
properties: {
178+
name: {
179+
type: "string",
180+
title: "Name",
181+
description: "Enter your name"
182+
}
183+
},
184+
required: ["name"]
185+
}
186+
```
187+
188+
### Email input
189+
190+
```ts
191+
requestedSchema: {
192+
type: "object",
193+
properties: {
194+
email: {
195+
type: "string",
196+
format: "email",
197+
title: "Email Address"
198+
}
199+
}
200+
}
201+
```
202+
203+
### Number input
204+
205+
```ts
206+
requestedSchema: {
207+
type: "object",
208+
properties: {
209+
amount: {
210+
type: "number",
211+
title: "Amount",
212+
minimum: 1,
213+
maximum: 100
214+
}
215+
}
216+
}
217+
```
218+
219+
### Select/dropdown
220+
221+
```ts
222+
requestedSchema: {
223+
type: "object",
224+
properties: {
225+
role: {
226+
type: "string",
227+
title: "Role",
228+
enum: ["viewer", "editor", "admin"],
229+
enumNames: ["Viewer", "Editor", "Administrator"]
230+
}
231+
}
232+
}
233+
```
234+
235+
### Multiple fields
236+
237+
```ts
238+
requestedSchema: {
239+
type: "object",
240+
properties: {
241+
username: { type: "string", title: "Username" },
242+
email: { type: "string", format: "email", title: "Email" },
243+
role: {
244+
type: "string",
245+
enum: ["viewer", "editor"],
246+
title: "Role"
247+
},
248+
sendWelcome: {
249+
type: "boolean",
250+
title: "Send welcome email"
251+
}
252+
},
253+
required: ["username", "email", "role"]
254+
}
255+
```
256+
257+
## Best practices
258+
259+
### Keep messages clear
260+
261+
Use clear, action-oriented messages that explain what you're asking for:
262+
263+
```ts
264+
// Good
265+
message: "Enter the email address for the new user account"
266+
267+
// Less clear
268+
message: "Email"
269+
```
270+
271+
### Provide helpful descriptions
272+
273+
Use the `description` field to guide users:
274+
275+
```ts
276+
properties: {
277+
apiKey: {
278+
type: "string",
279+
title: "API Key",
280+
description: "Your API key from the dashboard (Settings > API)"
281+
}
282+
}
283+
```
284+
285+
### Handle all response types
286+
287+
Always handle `accept`, `decline`, and `cancel` appropriately:
288+
289+
```ts
290+
const result = await this.server.server.elicitInput({...});
291+
292+
switch (result.action) {
293+
case "accept":
294+
if (result.content) {
295+
// Process the input
296+
return { content: [{ type: "text", text: "Success!" }] };
297+
}
298+
break;
299+
case "decline":
300+
return { content: [{ type: "text", text: "Action declined." }] };
301+
case "cancel":
302+
return { content: [{ type: "text", text: "Action cancelled." }] };
303+
}
304+
```
305+
306+
### Use required fields appropriately
307+
308+
Mark fields as `required` when they're essential for the operation:
309+
310+
```ts
311+
requestedSchema: {
312+
type: "object",
313+
properties: {
314+
username: { type: "string", title: "Username" },
315+
email: { type: "string", format: "email", title: "Email" }
316+
},
317+
required: ["username", "email"] // Both are mandatory
318+
}
319+
```
320+
321+
## Limitations
322+
323+
- Elicitation is currently only supported in direct MCP connections between clients and servers
324+
- Complex nested schemas may not be supported by all MCP clients
325+
- The visual presentation of elicitation forms depends on the MCP client implementation
326+
327+
## Related resources
328+
329+
- [MCP Elicitation Specification](https://spec.modelcontextprotocol.io/specification/draft/client/elicitation/)
330+
- [MCP Tools Documentation](/agents/model-context-protocol/tools/)
331+
- [JSON Schema Reference](https://json-schema.org/understanding-json-schema/)

0 commit comments

Comments
 (0)