Skip to content

Commit 7aec2ef

Browse files
committed
add log utility tests
1 parent 59786e0 commit 7aec2ef

File tree

1 file changed

+167
-1
lines changed

1 file changed

+167
-1
lines changed

src/server/mcp.test.ts

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
LoggingMessageNotificationSchema,
1515
Notification,
1616
TextContent,
17-
ElicitRequestSchema
17+
ElicitRequestSchema,
18+
SetLevelRequestSchema
1819
} from '../types.js';
1920
import { ResourceTemplate } from './mcp.js';
2021
import { completable } from './completable.js';
@@ -319,6 +320,62 @@ describe('Context', () => {
319320
expect(parsed.userId).toBe('user-123');
320321
expect(parsed.attempt).toBe(1);
321322
});
323+
324+
const logLevelsThroughContext = ['debug', 'info', 'warning', 'error'] as const;
325+
326+
//it.each for each log level, test that logging message is sent to client
327+
it.each(logLevelsThroughContext)('should send logging message to client for %s level from Context', async (level) => {
328+
const mcpServer = new McpServer({ name: 'ctx-test', version: '1.0' }, {
329+
capabilities: {
330+
logging: {}
331+
}
332+
});
333+
const client = new Client({ name: 'ctx-client', version: '1.0' }, {
334+
capabilities: {
335+
logging: {}
336+
}
337+
});
338+
339+
let seen = 0;
340+
341+
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
342+
seen++;
343+
expect(notification.params.level).toBe(level);
344+
expect(notification.params.data).toBe('Test message');
345+
expect(notification.params.test).toBe('test');
346+
expect(notification.params.sessionId).toBe('sample-session-id');
347+
return;
348+
});
349+
350+
351+
mcpServer.tool('ctx-log-test', { name: z.string() }, async (_args: { name: string }, extra) => {
352+
await extra[level]('Test message', { test: 'test' }, 'sample-session-id');
353+
await extra.log({
354+
level,
355+
data: 'Test message',
356+
logger: 'test-logger-namespace'
357+
}, 'sample-session-id');
358+
return { content: [{ type: 'text', text: 'ok' }] };
359+
});
360+
361+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
362+
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
363+
364+
const result = await client.request(
365+
{
366+
method: 'tools/call',
367+
params: { name: 'ctx-log-test', arguments: { name: 'ctx-log-test-name' } }
368+
},
369+
CallToolResultSchema
370+
);
371+
372+
// two messages should have been sent - one from the .log method and one from the .debug/info/warning/error method
373+
expect(seen).toBe(2);
374+
375+
expect(result.content).toHaveLength(1);
376+
expect(result.content[0].type).toBe('text');
377+
expect(result.content[0].text).toBe('ok');
378+
});
322379
});
323380

324381
describe('ResourceTemplate', () => {
@@ -4132,6 +4189,115 @@ describe('elicitInput()', () => {
41324189
);
41334190
});
41344191

4192+
test('should be able to call elicit from Context', async () => {
4193+
mcpServer.tool(
4194+
'elicit-through-ctx-tool',
4195+
{
4196+
restaurant: z.string(),
4197+
date: z.string(),
4198+
partySize: z.number()
4199+
},
4200+
async ({ restaurant, date, partySize }, extra) => {
4201+
// Check availability
4202+
const available = await checkAvailability(restaurant, date, partySize);
4203+
4204+
if (!available) {
4205+
// Ask user if they want to try alternative dates
4206+
const result = await extra.elicit({
4207+
message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
4208+
requestedSchema: {
4209+
type: 'object',
4210+
properties: {
4211+
checkAlternatives: {
4212+
type: 'boolean',
4213+
title: 'Check alternative dates',
4214+
description: 'Would you like me to check other dates?'
4215+
},
4216+
flexibleDates: {
4217+
type: 'string',
4218+
title: 'Date flexibility',
4219+
description: 'How flexible are your dates?',
4220+
enum: ['next_day', 'same_week', 'next_week'],
4221+
enumNames: ['Next day', 'Same week', 'Next week']
4222+
}
4223+
},
4224+
required: ['checkAlternatives']
4225+
}
4226+
});
4227+
4228+
if (result.action === 'accept' && result.content?.checkAlternatives) {
4229+
const alternatives = await findAlternatives(restaurant, date, partySize, result.content.flexibleDates as string);
4230+
return {
4231+
content: [
4232+
{
4233+
type: 'text',
4234+
text: `Found these alternatives: ${alternatives.join(', ')}`
4235+
}
4236+
]
4237+
};
4238+
}
4239+
4240+
return {
4241+
content: [
4242+
{
4243+
type: 'text',
4244+
text: 'No booking made. Original date not available.'
4245+
}
4246+
]
4247+
};
4248+
}
4249+
4250+
await makeBooking(restaurant, date, partySize);
4251+
return {
4252+
content: [
4253+
{
4254+
type: 'text',
4255+
text: `Booked table for ${partySize} at ${restaurant} on ${date}`
4256+
}
4257+
]
4258+
};
4259+
}
4260+
);
4261+
4262+
checkAvailability.mockResolvedValue(false);
4263+
findAlternatives.mockResolvedValue(['2024-12-26', '2024-12-27', '2024-12-28']);
4264+
4265+
// Set up client to accept alternative date checking
4266+
client.setRequestHandler(ElicitRequestSchema, async request => {
4267+
expect(request.params.message).toContain('No tables available at ABC Restaurant on 2024-12-25');
4268+
return {
4269+
action: 'accept',
4270+
content: {
4271+
checkAlternatives: true,
4272+
flexibleDates: 'same_week'
4273+
}
4274+
};
4275+
});
4276+
4277+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
4278+
4279+
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
4280+
4281+
// Call the tool
4282+
const result = await client.callTool({
4283+
name: 'book-restaurant',
4284+
arguments: {
4285+
restaurant: 'ABC Restaurant',
4286+
date: '2024-12-25',
4287+
partySize: 2
4288+
}
4289+
});
4290+
4291+
expect(checkAvailability).toHaveBeenCalledWith('ABC Restaurant', '2024-12-25', 2);
4292+
expect(findAlternatives).toHaveBeenCalledWith('ABC Restaurant', '2024-12-25', 2, 'same_week');
4293+
expect(result.content).toEqual([
4294+
{
4295+
type: 'text',
4296+
text: 'Found these alternatives: 2024-12-26, 2024-12-27, 2024-12-28'
4297+
}
4298+
]);
4299+
});
4300+
41354301
test('should successfully elicit additional information', async () => {
41364302
// Mock availability check to return false
41374303
checkAvailability.mockResolvedValue(false);

0 commit comments

Comments
 (0)