This commit is contained in:
ihrpr 2025-06-10 17:26:48 +01:00
commit c088719985
No known key found for this signature in database

View file

@ -3458,258 +3458,223 @@ describe("prompt()", () => {
expect(typeof receivedRequestId === 'string' || typeof receivedRequestId === 'number').toBe(true);
expect(result.messages[0].content.text).toContain("Received request ID:");
});
});
/**
* Test: Restaurant booking example with elicitation from README
*/
describe("Restaurant booking elicitation example", () => {
// Mock restaurant booking functions
const checkAvailability = jest.fn().mockResolvedValue(false);
const findAlternatives = jest.fn().mockResolvedValue([]);
const makeBooking = jest.fn().mockResolvedValue("BOOKING-123");
describe("elicitInput()", () => {
let mcpServer: McpServer;
let client: Client;
const checkAvailability = jest.fn().mockResolvedValue(false);
const findAlternatives = jest.fn().mockResolvedValue([]);
const makeBooking = jest.fn().mockResolvedValue("BOOKING-123");
beforeEach(() => {
// Reset mocks
jest.clearAllMocks();
// Create server with restaurant booking tool
mcpServer = new McpServer({
name: "restaurant-booking-server",
version: "1.0.0",
});
let mcpServer: McpServer;
let client: Client;
// Register the restaurant booking tool from README example
mcpServer.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 mcpServer.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"]
}
beforeEach(() => {
jest.clearAllMocks();
// Create server with restaurant booking tool
mcpServer = new McpServer({
name: "restaurant-booking-server",
version: "1.0.0",
});
// Register the restaurant booking tool from README example
mcpServer.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 mcpServer.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?"
},
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(", ")}`
}]
};
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: "No booking made. Original date not available."
text: `Found these alternatives: ${alternatives.join(", ")}`
}]
};
}
// Book the table
await makeBooking(restaurant, date, partySize);
return {
content: [{
type: "text",
text: `Booked table for ${partySize} at ${restaurant} on ${date}`
text: "No booking made. Original date not available."
}]
};
}
);
// Create client with elicitation capability
client = new Client(
{
name: "test-client",
version: "1.0.0",
await makeBooking(restaurant, date, partySize);
return {
content: [{
type: "text",
text: `Booked table for ${partySize} at ${restaurant} on ${date}`
}]
};
}
);
// Create client with elicitation capability
client = new Client(
{
name: "test-client",
version: "1.0.0",
},
{
capabilities: {
elicitation: {},
},
{
capabilities: {
elicitation: {},
},
}
);
});
test("should successfully elicit additional information", async () => {
// Mock availability check to return false
checkAvailability.mockResolvedValue(false);
findAlternatives.mockResolvedValue(["2024-12-26", "2024-12-27", "2024-12-28"]);
// Set up client to accept alternative date checking
client.setRequestHandler(ElicitRequestSchema, async (request) => {
expect(request.params.message).toContain("No tables available at ABC Restaurant on 2024-12-25");
return {
action: "accept",
content: {
checkAlternatives: true,
flexibleDates: "same_week"
}
);
};
});
test("should successfully book when table is available", async () => {
// Mock availability check to return true
checkAvailability.mockResolvedValue(true);
makeBooking.mockResolvedValue("BOOKING-123");
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
}
});
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(makeBooking).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(result.content).toEqual([{
type: "text",
text: "Booked table for 2 at ABC Restaurant on 2024-12-25"
}]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
}
});
test("should ask for alternatives when no availability and user accepts", async () => {
// Mock availability check to return false
checkAvailability.mockResolvedValue(false);
findAlternatives.mockResolvedValue(["2024-12-26", "2024-12-27", "2024-12-28"]);
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(findAlternatives).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2, "same_week");
expect(result.content).toEqual([{
type: "text",
text: "Found these alternatives: 2024-12-26, 2024-12-27, 2024-12-28"
}]);
});
// Set up client to accept alternative date checking
client.setRequestHandler(ElicitRequestSchema, async (request) => {
expect(request.params.message).toContain("No tables available at ABC Restaurant on 2024-12-25");
return {
action: "accept",
content: {
checkAlternatives: true,
flexibleDates: "same_week"
}
};
});
test("should handle user declining to elicitation request", async () => {
// Mock availability check to return false
checkAvailability.mockResolvedValue(false);
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
// Set up client to decline alternative date checking
client.setRequestHandler(ElicitRequestSchema, async () => {
return {
action: "accept",
content: {
checkAlternatives: false
}
});
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(findAlternatives).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2, "same_week");
expect(result.content).toEqual([{
type: "text",
text: "Found these alternatives: 2024-12-26, 2024-12-27, 2024-12-28"
}]);
};
});
test("should handle user declining to check alternatives", async () => {
// Mock availability check to return false
checkAvailability.mockResolvedValue(false);
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
// Set up client to decline alternative date checking
client.setRequestHandler(ElicitRequestSchema, async () => {
return {
action: "accept",
content: {
checkAlternatives: false
}
};
});
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
}
});
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(findAlternatives).not.toHaveBeenCalled();
expect(result.content).toEqual([{
type: "text",
text: "No booking made. Original date not available."
}]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
}
});
test("should handle user cancelling the elicitation", async () => {
// Mock availability check to return false
checkAvailability.mockResolvedValue(false);
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(findAlternatives).not.toHaveBeenCalled();
expect(result.content).toEqual([{
type: "text",
text: "No booking made. Original date not available."
}]);
});
// Set up client to cancel the elicitation
client.setRequestHandler(ElicitRequestSchema, async () => {
return {
action: "cancel"
};
});
test("should handle user cancelling the elicitation", async () => {
// Mock availability check to return false
checkAvailability.mockResolvedValue(false);
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
}
});
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(findAlternatives).not.toHaveBeenCalled();
expect(result.content).toEqual([{
type: "text",
text: "No booking made. Original date not available."
}]);
// Set up client to cancel the elicitation
client.setRequestHandler(ElicitRequestSchema, async () => {
return {
action: "cancel"
};
});
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);
// Call the tool
const result = await client.callTool({
name: "book-restaurant",
arguments: {
restaurant: "ABC Restaurant",
date: "2024-12-25",
partySize: 2
}
});
expect(checkAvailability).toHaveBeenCalledWith("ABC Restaurant", "2024-12-25", 2);
expect(findAlternatives).not.toHaveBeenCalled();
expect(result.content).toEqual([{
type: "text",
text: "No booking made. Original date not available."
}]);
});
});