8000 Introduce Elicitation capability by ihrpr · Pull Request #520 · modelcontextprotocol/typescript-sdk · GitHub
[go: up one dir, main page]

Skip to content

Introduce Elicitation capability #520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,108 @@ const transport = new StdioServerTransport();
await server.connect(transport);
```

### Eliciting User Input

MCP servers can request additional information from users through the elicitation feature. This is useful for interactive workflows where the server needs user input or confirmation:

```typescript
// Server-side: Restaurant booking tool that asks for alternatives
server.tool(
"book-restaurant",
{
restaurant: z.string(),
date: z.string(),
partySize: z.number()
},
async ({ restaurant, date, partySize }) => {
// Check availability
const available = await checkAvailability(restaurant, date, partySize);

if (!available) {
// Ask user if they want to try alternative dates
const result = await server.server.elicitInput({
message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
requestedSchema: {
type: "object",
properties: {
checkAlternatives: {
type: "boolean",
title: "Check alternative dates",
description: "Would you like me to check other dates?"
},
flexibleDates: {
type: "string",
title: "Date flexibility",
description: "How flexible are your dates?",
enum: ["next_day", "same_week", "next_week"],
enumNames: ["Next day", "Same week", "Next week"]
}
},
required: ["checkAlternatives"]
}
});

if (result.action === "accept" && result.content?.checkAlternatives) {
const alternatives = await findAlternatives(
restaurant,
date,
partySize,
result.content.flexibleDates as string
);
return {
content: [{
type: "text",
text: `Found these alternatives: ${alternatives.join(", ")}`
}]
};
}

return {
content: [{
type: "text",
text: "No booking made. Original date not available."
}]
};
}

// Book the table
await makeBooking(restaurant, date, partySize);
return {
content: [{
type: "text",
text: `Booked table for ${partySize} at ${restaurant} on ${date}`
}]
};
}
);

// Client-side: Handle elicitation requests


// This is a placeholder - implement based on your UI framework
async function getInputFromUser(message: string, schema: any): Promise<{
action: "accept" | "decline" | "cancel";
data?: Record<string, any>;
}> {
// This should be implemented depending on the app
throw new Error("getInputFromUser must be implemented for your platform");
}

client.setRequestHandler(ElicitRequestSchema, async (request) => {
const userResponse = await getInputFromUser(
request.params.message,
request.params.requestedSchema
);

return {
action: userResponse.action,
content: userResponse.action === "accept" ? userResponse.data : undefined
};
});
```

**Note**: Elicitation requires client support. Clients must declare the `elicitation` capability during initialization.

### Writing MCP Clients

The SDK provides a high-level client interface:
Expand Down
38 changes: 38 additions & 0 deletions src/client/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ListToolsRequestSchema,
CallToolRequestSchema,
CreateMessageRequestSchema,
ElicitRequestSchema,
ListRootsRequestSchema,
ErrorCode,
} from "../types.js";
Expand Down Expand Up @@ -597,6 +598,43 @@ test("should only allow setRequestHandler for declared capabilities", () => {
}).toThrow("Client does not support roots capability");
});

test("should allow setRequestHandler for declared elicitation capability", () => {
const client = new Client(
{
name: "test-client",
version: "1.0.0",
},
{
capabilities: {
elicitation: {},
},
},
);

// This should work because elicitation is a declared capability
expect(() => {
client.setRequestHandler(ElicitRequestSchema, () => ({
action: "accept",
content: {
username: "test-user",
confirmed: true,
},
}));
}).not.toThrow();

// This should throw because sampling is not a declared capability
expect(() => {
client.setRequestHandler(CreateMessageRequestSchema, () => ({
model: "test-model",
role: "assistant",
content: {
type: "text",
text: "Test response",
},
}));
}).toThrow("Client does not support sampling capability");
});

/***
* Test: Type Checking
* Test that custom request/notification/result schemas can be used with the Client class.
Expand Down
8 changes: 8 additions & 0 deletions src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,14 @@ export class Client<
}
break;

case "elicitation/create":
if (!this._capabilities.elicitation) {
throw new Error(
`Client does not support elicitation capability (required for ${method})`,
);
}
break;

case "roots/list":
if (!this._capabilities.roots) {
throw new Error(
Expand Down
Loading
0