v2 ESlint fixes (#1403)
Co-authored-by: Matt <77928207+mattzcarey@users.noreply.github.com>
This commit is contained in:
parent
9296459ac0
commit
9fc7e28f48
11 changed files with 1672 additions and 1346 deletions
|
|
@ -11,6 +11,7 @@ await server.connect(transport);
|
|||
|
||||
const exit = async () => {
|
||||
await server.close();
|
||||
// eslint-disable-next-line unicorn/no-process-exit
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2407,7 +2407,7 @@ describe('Task-based execution', () => {
|
|||
// Client creates task on server via tool call
|
||||
await client.callTool({ name: 'test-tool', arguments: {} }, CallToolResultSchema, {
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -2482,7 +2482,7 @@ describe('Task-based execution', () => {
|
|||
|
||||
// Create a task
|
||||
await client.callTool({ name: 'test-tool', arguments: {} }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
});
|
||||
|
||||
// Query task status by listing tasks and getting the first one
|
||||
|
|
@ -2560,7 +2560,7 @@ describe('Task-based execution', () => {
|
|||
// Create a task using callToolStream to capture the task ID
|
||||
let taskId: string | undefined;
|
||||
const stream = client.experimental.tasks.callToolStream({ name: 'test-tool', arguments: {} }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
});
|
||||
|
||||
for await (const message of stream) {
|
||||
|
|
@ -2643,7 +2643,7 @@ describe('Task-based execution', () => {
|
|||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
await client.callTool({ name: 'test-tool', arguments: {} }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
});
|
||||
|
||||
// Get the task ID from the task list
|
||||
|
|
@ -2759,7 +2759,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -2849,7 +2849,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -2941,7 +2941,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -3034,7 +3034,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure and capture taskId
|
||||
|
|
@ -3141,7 +3141,7 @@ describe('Task-based execution', () => {
|
|||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await client.callTool({ name: 'test-tool', arguments: { id: `task-${i + 1}` } }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
});
|
||||
|
||||
// Get the task ID from the task list
|
||||
|
|
@ -3412,7 +3412,7 @@ test('should respect server task capabilities', async () => {
|
|||
// These should work because server supports tasks
|
||||
await expect(
|
||||
client.callTool({ name: 'test-tool', arguments: {} }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
})
|
||||
).resolves.not.toThrow();
|
||||
await expect(client.experimental.tasks.listTasks()).resolves.not.toThrow();
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ describe('Task Schema Validation', () => {
|
|||
const task: Task = {
|
||||
taskId: 'test-123',
|
||||
status: 'working',
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
createdAt,
|
||||
lastUpdatedAt: createdAt,
|
||||
pollInterval: 1000
|
||||
};
|
||||
|
||||
expect(task.ttl).toBe(60000);
|
||||
expect(task.ttl).toBe(60_000);
|
||||
expect(task.createdAt).toBeDefined();
|
||||
expect(typeof task.createdAt).toBe('string');
|
||||
});
|
||||
|
|
@ -76,7 +76,7 @@ describe('Task Schema Validation', () => {
|
|||
const task: Task = {
|
||||
taskId: 'test-iso',
|
||||
status: 'working',
|
||||
ttl: 30000,
|
||||
ttl: 30_000,
|
||||
createdAt,
|
||||
lastUpdatedAt: createdAt
|
||||
};
|
||||
|
|
@ -91,7 +91,7 @@ describe('Task Schema Validation', () => {
|
|||
const task: Task = {
|
||||
taskId: 'test-iso',
|
||||
status: 'working',
|
||||
ttl: 30000,
|
||||
ttl: 30_000,
|
||||
createdAt,
|
||||
lastUpdatedAt: createdAt
|
||||
};
|
||||
|
|
@ -103,7 +103,7 @@ describe('Task Schema Validation', () => {
|
|||
const statuses: Task['status'][] = ['working', 'input_required', 'completed', 'failed', 'cancelled'];
|
||||
|
||||
const createdAt = new Date().toISOString();
|
||||
statuses.forEach(status => {
|
||||
for (const status of statuses) {
|
||||
const task: Task = {
|
||||
taskId: `test-${status}`,
|
||||
status,
|
||||
|
|
@ -112,6 +112,6 @@ describe('Task Schema Validation', () => {
|
|||
lastUpdatedAt: createdAt
|
||||
};
|
||||
expect(task.status).toBe(status);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
129
test/integration/test/experimental/tasks/taskListing.test.ts
Normal file
129
test/integration/test/experimental/tasks/taskListing.test.ts
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import { ErrorCode, McpError } from '@modelcontextprotocol/core';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import { createInMemoryTaskEnvironment } from '../../helpers/mcp.js';
|
||||
|
||||
describe('Task Listing with Pagination', () => {
|
||||
let client: Awaited<ReturnType<typeof createInMemoryTaskEnvironment>>['client'];
|
||||
let server: Awaited<ReturnType<typeof createInMemoryTaskEnvironment>>['server'];
|
||||
let taskStore: Awaited<ReturnType<typeof createInMemoryTaskEnvironment>>['taskStore'];
|
||||
|
||||
beforeEach(async () => {
|
||||
const env = await createInMemoryTaskEnvironment();
|
||||
client = env.client;
|
||||
server = env.server;
|
||||
taskStore = env.taskStore;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
taskStore.cleanup();
|
||||
await client.close();
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('should return empty list when no tasks exist', async () => {
|
||||
const result = await client.experimental.tasks.listTasks();
|
||||
|
||||
expect(result.tasks).toEqual([]);
|
||||
expect(result.nextCursor).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return all tasks when less than page size', async () => {
|
||||
// Create 3 tasks
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await taskStore.createTask({}, i, {
|
||||
method: 'tools/call',
|
||||
params: { name: 'test-tool' }
|
||||
});
|
||||
}
|
||||
|
||||
const result = await client.experimental.tasks.listTasks();
|
||||
|
||||
expect(result.tasks).toHaveLength(3);
|
||||
expect(result.nextCursor).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should paginate when more than page size exists', async () => {
|
||||
// Create 15 tasks (page size is 10 in InMemoryTaskStore)
|
||||
for (let i = 0; i < 15; i++) {
|
||||
await taskStore.createTask({}, i, {
|
||||
method: 'tools/call',
|
||||
params: { name: 'test-tool' }
|
||||
});
|
||||
}
|
||||
|
||||
// Get first page
|
||||
const page1 = await client.experimental.tasks.listTasks();
|
||||
expect(page1.tasks).toHaveLength(10);
|
||||
expect(page1.nextCursor).toBeDefined();
|
||||
|
||||
// Get second page using cursor
|
||||
const page2 = await client.experimental.tasks.listTasks(page1.nextCursor);
|
||||
expect(page2.tasks).toHaveLength(5);
|
||||
expect(page2.nextCursor).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should treat cursor as opaque token', async () => {
|
||||
// Create 5 tasks
|
||||
for (let i = 0; i < 5; i++) {
|
||||
await taskStore.createTask({}, i, {
|
||||
method: 'tools/call',
|
||||
params: { name: 'test-tool' }
|
||||
});
|
||||
}
|
||||
|
||||
// Get all tasks to get a valid cursor
|
||||
const allTasks = taskStore.getAllTasks();
|
||||
const validCursor = allTasks[2]!.taskId;
|
||||
|
||||
// Use the cursor - should work even though we don't know its internal structure
|
||||
const result = await client.experimental.tasks.listTasks(validCursor);
|
||||
expect(result.tasks).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should return error code -32602 for invalid cursor', async () => {
|
||||
await taskStore.createTask({}, 1, {
|
||||
method: 'tools/call',
|
||||
params: { name: 'test-tool' }
|
||||
});
|
||||
|
||||
// Try to use an invalid cursor - should return -32602 (Invalid params) per MCP spec
|
||||
await expect(client.experimental.tasks.listTasks('invalid-cursor')).rejects.toSatisfy((error: McpError) => {
|
||||
expect(error).toBeInstanceOf(McpError);
|
||||
expect(error.code).toBe(ErrorCode.InvalidParams);
|
||||
expect(error.message).toContain('Invalid cursor');
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should ensure tasks accessible via tasks/get are also accessible via tasks/list', async () => {
|
||||
// Create a task
|
||||
const task = await taskStore.createTask({}, 1, {
|
||||
method: 'tools/call',
|
||||
params: { name: 'test-tool' }
|
||||
});
|
||||
|
||||
// Verify it's accessible via tasks/get
|
||||
const getResult = await client.experimental.tasks.getTask(task.taskId);
|
||||
expect(getResult.taskId).toBe(task.taskId);
|
||||
|
||||
// Verify it's also accessible via tasks/list
|
||||
const listResult = await client.experimental.tasks.listTasks();
|
||||
expect(listResult.tasks).toHaveLength(1);
|
||||
expect(listResult.tasks[0]!.taskId).toBe(task.taskId);
|
||||
});
|
||||
|
||||
it('should not include related-task metadata in list response', async () => {
|
||||
// Create a task
|
||||
await taskStore.createTask({}, 1, {
|
||||
method: 'tools/call',
|
||||
params: { name: 'test-tool' }
|
||||
});
|
||||
|
||||
const result = await client.experimental.tasks.listTasks();
|
||||
|
||||
// The response should have _meta but not include related-task metadata
|
||||
expect(result._meta).toBeDefined();
|
||||
expect(result._meta?.['io.modelcontextprotocol/related-task']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Regression test for https://github.com/modelcontextprotocol/typescript-sdk/issues/1277
|
||||
*
|
||||
* Zod v4 stores `.describe()` descriptions directly on the schema object,
|
||||
* not in `._zod.def.description`. This test verifies that descriptions are
|
||||
* correctly extracted for prompt arguments.
|
||||
*/
|
||||
|
||||
import { Client } from '@modelcontextprotocol/client';
|
||||
import { InMemoryTransport, ListPromptsResultSchema } from '@modelcontextprotocol/core';
|
||||
import { McpServer } from '@modelcontextprotocol/server';
|
||||
import type { ZodMatrixEntry } from '@modelcontextprotocol/test-helpers';
|
||||
import { zodTestMatrix } from '@modelcontextprotocol/test-helpers';
|
||||
|
||||
describe.each(zodTestMatrix)('Issue #1277: $zodVersionLabel', (entry: ZodMatrixEntry) => {
|
||||
const { z } = entry;
|
||||
|
||||
test('should preserve argument descriptions from .describe()', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
});
|
||||
const client = new Client({
|
||||
name: 'test client',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
mcpServer.prompt(
|
||||
'test',
|
||||
{
|
||||
name: z.string().describe('The user name'),
|
||||
value: z.string().describe('The value to set')
|
||||
},
|
||||
async ({ name, value }) => ({
|
||||
messages: [
|
||||
{
|
||||
role: 'assistant',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: `${name}: ${value}`
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
|
||||
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
|
||||
|
||||
const result = await client.request(
|
||||
{
|
||||
method: 'prompts/list'
|
||||
},
|
||||
ListPromptsResultSchema
|
||||
);
|
||||
|
||||
expect(result.prompts).toHaveLength(1);
|
||||
expect(result.prompts[0]!.name).toBe('test');
|
||||
expect(result.prompts[0]!.arguments).toEqual([
|
||||
{ name: 'name', required: true, description: 'The user name' },
|
||||
{ name: 'value', required: true, description: 'The value to set' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -102,11 +102,11 @@ describe('Process cleanup', () => {
|
|||
try {
|
||||
process.kill(serverPid, 9);
|
||||
throw new Error('Expected server to be dead but it is alive');
|
||||
} catch (err: unknown) {
|
||||
} catch (error: unknown) {
|
||||
// 'ESRCH' the process doesn't exist
|
||||
if (err && typeof err === 'object' && 'code' in err && err.code === 'ESRCH') {
|
||||
if (error && typeof error === 'object' && 'code' in error && error.code === 'ESRCH') {
|
||||
// success
|
||||
} else throw err;
|
||||
} else throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1302,11 +1302,7 @@ test('should allow elicitation reject and cancel without validation', async () =
|
|||
let requestCount = 0;
|
||||
client.setRequestHandler(ElicitRequestSchema, _request => {
|
||||
requestCount++;
|
||||
if (requestCount === 1) {
|
||||
return { action: 'decline' };
|
||||
} else {
|
||||
return { action: 'cancel' };
|
||||
}
|
||||
return requestCount === 1 ? { action: 'decline' } : { action: 'cancel' };
|
||||
});
|
||||
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
|
|
@ -2090,7 +2086,7 @@ describe('createMcpExpressApp', () => {
|
|||
expect(response.body).toEqual({
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32000,
|
||||
code: -32_000,
|
||||
message: 'Invalid Host: evil.com'
|
||||
},
|
||||
id: null
|
||||
|
|
@ -2308,7 +2304,7 @@ describe('Task-based execution', () => {
|
|||
let taskId: string | undefined;
|
||||
const stream = client.experimental.tasks.callToolStream({ name: 'test-tool', arguments: {} }, CallToolResultSchema, {
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -2535,7 +2531,7 @@ describe('Task-based execution', () => {
|
|||
let taskId: string | undefined;
|
||||
const stream = client.experimental.tasks.callToolStream({ name: 'collect-info', arguments: {} }, CallToolResultSchema, {
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -2645,7 +2641,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -2722,7 +2718,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -2804,7 +2800,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -2897,7 +2893,7 @@ describe('Task-based execution', () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure and capture taskId
|
||||
|
|
@ -3008,7 +3004,7 @@ describe('Task-based execution', () => {
|
|||
// Create multiple tasks concurrently
|
||||
const pendingRequests = Array.from({ length: 4 }, (_, index) =>
|
||||
client.callTool({ name: 'async-tool', arguments: { delay: 10 + index * 5, taskNum: index + 1 } }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -3024,12 +3020,12 @@ describe('Task-based execution', () => {
|
|||
const taskIds = taskList.tasks.map(t => t.taskId);
|
||||
|
||||
// Verify all tasks completed successfully
|
||||
for (let i = 0; i < taskIds.length; i++) {
|
||||
const task = await client.experimental.tasks.getTask(taskIds[i]!);
|
||||
for (const [i, taskId] of taskIds.entries()) {
|
||||
const task = await client.experimental.tasks.getTask(taskId!);
|
||||
expect(task.status).toBe('completed');
|
||||
expect(task.taskId).toBe(taskIds[i]!);
|
||||
expect(task.taskId).toBe(taskId!);
|
||||
|
||||
const result = await client.experimental.tasks.getTaskResult(taskIds[i]!, CallToolResultSchema);
|
||||
const result = await client.experimental.tasks.getTaskResult(taskId!, CallToolResultSchema);
|
||||
expect(result.content).toEqual([{ type: 'text', text: `Completed task ${i + 1}` }]);
|
||||
}
|
||||
|
||||
|
|
@ -3253,7 +3249,7 @@ test('should respect client task capabilities', async () => {
|
|||
}
|
||||
},
|
||||
CreateTaskResultSchema,
|
||||
{ task: { ttl: 60000 } }
|
||||
{ task: { ttl: 60_000 } }
|
||||
);
|
||||
|
||||
// Verify CreateTaskResult structure
|
||||
|
|
@ -3275,7 +3271,7 @@ test('should respect client task capabilities', async () => {
|
|||
}
|
||||
},
|
||||
CreateMessageResultSchema,
|
||||
{ task: { taskId: 'test-task-2', keepAlive: 60000 } }
|
||||
{ task: { taskId: 'test-task-2', keepAlive: 60_000 } }
|
||||
)
|
||||
).rejects.toThrow('Client does not support task creation for sampling/createMessage');
|
||||
|
||||
|
|
|
|||
|
|
@ -1902,7 +1902,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async (_args, extra) => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000 });
|
||||
return { task };
|
||||
},
|
||||
getTask: async (_args, extra) => {
|
||||
|
|
@ -1971,7 +1971,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async (_args, extra) => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000 });
|
||||
return { task };
|
||||
},
|
||||
getTask: async (_args, extra) => {
|
||||
|
|
@ -3184,7 +3184,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
);
|
||||
|
||||
expect(listResult.prompts[0]!.arguments).toHaveLength(2);
|
||||
expect(listResult.prompts[0]!.arguments!.map(a => a.name).sort()).toEqual(['name', 'value']);
|
||||
expect(listResult.prompts[0]!.arguments!.map(a => a.name).toSorted()).toEqual(['name', 'value']);
|
||||
|
||||
// Call the prompt with the new schema
|
||||
const getResult = await client.request(
|
||||
|
|
@ -3325,7 +3325,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
let result = await client.request({ method: 'prompts/list' }, ListPromptsResultSchema);
|
||||
|
||||
expect(result.prompts).toHaveLength(2);
|
||||
expect(result.prompts.map(p => p.name).sort()).toEqual(['prompt1', 'prompt2']);
|
||||
expect(result.prompts.map(p => p.name).toSorted()).toEqual(['prompt1', 'prompt2']);
|
||||
|
||||
expect(notifications).toHaveLength(0);
|
||||
|
||||
|
|
@ -4375,12 +4375,17 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
}),
|
||||
name: completable(z.string(), (value, context) => {
|
||||
const department = context?.arguments?.['department'];
|
||||
if (department === 'engineering') {
|
||||
return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));
|
||||
} else if (department === 'sales') {
|
||||
return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));
|
||||
} else if (department === 'marketing') {
|
||||
return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));
|
||||
switch (department) {
|
||||
case 'engineering': {
|
||||
return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));
|
||||
}
|
||||
case 'sales': {
|
||||
return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));
|
||||
}
|
||||
case 'marketing': {
|
||||
return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));
|
||||
}
|
||||
// No default
|
||||
}
|
||||
return ['Guest'].filter(n => n.startsWith(value));
|
||||
})
|
||||
|
|
@ -4739,15 +4744,13 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
]);
|
||||
|
||||
server.registerTool('contact', { inputSchema: unionSchema }, async args => {
|
||||
if (args.type === 'email') {
|
||||
return {
|
||||
content: [{ type: 'text' as const, text: `Email contact: ${args.email}` }]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
content: [{ type: 'text' as const, text: `Phone contact: ${args.phone}` }]
|
||||
};
|
||||
}
|
||||
return args.type === 'email'
|
||||
? {
|
||||
content: [{ type: 'text' as const, text: `Email contact: ${args.email}` }]
|
||||
}
|
||||
: {
|
||||
content: [{ type: 'text' as const, text: `Phone contact: ${args.phone}` }]
|
||||
};
|
||||
});
|
||||
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
|
|
@ -4854,11 +4857,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
|
||||
server.registerTool('process', { inputSchema: schema }, async args => {
|
||||
const processed = args.items.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return item.content.toUpperCase();
|
||||
} else {
|
||||
return item.value * 2;
|
||||
}
|
||||
return item.type === 'text' ? item.content.toUpperCase() : item.value * 2;
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
|
|
@ -5093,7 +5092,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
const obj = input as Record<string, unknown>;
|
||||
// Convert string numbers to actual numbers
|
||||
if (typeof obj.count === 'string') {
|
||||
return { ...obj, count: parseInt(obj.count, 10) };
|
||||
return { ...obj, count: Number.parseInt(obj.count, 10) };
|
||||
}
|
||||
}
|
||||
return input;
|
||||
|
|
@ -5662,12 +5661,17 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
}),
|
||||
name: completable(z.string(), (value, context) => {
|
||||
const department = context?.arguments?.['department'];
|
||||
if (department === 'engineering') {
|
||||
return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));
|
||||
} else if (department === 'sales') {
|
||||
return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));
|
||||
} else if (department === 'marketing') {
|
||||
return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));
|
||||
switch (department) {
|
||||
case 'engineering': {
|
||||
return ['Alice', 'Bob', 'Charlie'].filter(n => n.startsWith(value));
|
||||
}
|
||||
case 'sales': {
|
||||
return ['David', 'Eve', 'Frank'].filter(n => n.startsWith(value));
|
||||
}
|
||||
case 'marketing': {
|
||||
return ['Grace', 'Henry', 'Iris'].filter(n => n.startsWith(value));
|
||||
}
|
||||
// No default
|
||||
}
|
||||
return ['Guest'].filter(n => n.startsWith(value));
|
||||
})
|
||||
|
|
@ -6027,15 +6031,13 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
]);
|
||||
|
||||
server.registerTool('contact', { inputSchema: unionSchema }, async args => {
|
||||
if (args.type === 'email') {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Email contact: ${args.email}` }]
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Phone contact: ${args.phone}` }]
|
||||
};
|
||||
}
|
||||
return args.type === 'email'
|
||||
? {
|
||||
content: [{ type: 'text', text: `Email contact: ${args.email}` }]
|
||||
}
|
||||
: {
|
||||
content: [{ type: 'text', text: `Phone contact: ${args.phone}` }]
|
||||
};
|
||||
});
|
||||
|
||||
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
||||
|
|
@ -6142,11 +6144,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
|
||||
server.registerTool('process', { inputSchema: schema }, async args => {
|
||||
const processed = args.items.map(item => {
|
||||
if (item.type === 'text') {
|
||||
return item.content.toUpperCase();
|
||||
} else {
|
||||
return item.value * 2;
|
||||
}
|
||||
return item.type === 'text' ? item.content.toUpperCase() : item.value * 2;
|
||||
});
|
||||
return {
|
||||
content: [
|
||||
|
|
@ -6301,7 +6299,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async ({ input }, extra) => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000, pollInterval: 100 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000, pollInterval: 100 });
|
||||
|
||||
// Capture taskStore for use in setTimeout
|
||||
const store = extra.taskStore;
|
||||
|
|
@ -6406,7 +6404,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async ({ value }, extra) => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000, pollInterval: 100 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000, pollInterval: 100 });
|
||||
|
||||
// Capture taskStore for use in setTimeout
|
||||
const store = extra.taskStore;
|
||||
|
|
@ -6514,7 +6512,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async ({ data }, extra) => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000, pollInterval: 100 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000, pollInterval: 100 });
|
||||
|
||||
// Capture taskStore for use in setTimeout
|
||||
const store = extra.taskStore;
|
||||
|
|
@ -6554,7 +6552,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
params: {
|
||||
name: 'task-tool',
|
||||
arguments: { data: 'test' },
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
}
|
||||
},
|
||||
z.object({
|
||||
|
|
@ -6631,7 +6629,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async extra => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000, pollInterval: 100 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000, pollInterval: 100 });
|
||||
|
||||
// Capture taskStore for use in setTimeout
|
||||
const store = extra.taskStore;
|
||||
|
|
@ -6737,7 +6735,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async extra => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000, pollInterval: 100 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000, pollInterval: 100 });
|
||||
|
||||
// Capture taskStore for use in setTimeout
|
||||
const store = extra.taskStore;
|
||||
|
|
@ -6824,7 +6822,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
},
|
||||
{
|
||||
createTask: async (_args, extra) => {
|
||||
const task = await extra.taskStore.createTask({ ttl: 60000, pollInterval: 100 });
|
||||
const task = await extra.taskStore.createTask({ ttl: 60_000, pollInterval: 100 });
|
||||
return { task };
|
||||
},
|
||||
getTask: async (_args, extra) => {
|
||||
|
|
|
|||
|
|
@ -12,81 +12,81 @@ import {
|
|||
ListToolsResultSchema,
|
||||
McpServer
|
||||
} from '@modelcontextprotocol/server';
|
||||
import type { ZodMatrixEntry } from '@modelcontextprotocol/test-helpers';
|
||||
import type { ZNamespace, ZodMatrixEntry } from '@modelcontextprotocol/test-helpers';
|
||||
import { listenOnRandomPort, zodTestMatrix } from '@modelcontextprotocol/test-helpers';
|
||||
|
||||
async function setupServer(withSessionManagement: boolean, z: ZNamespace) {
|
||||
const server: Server = createServer();
|
||||
const mcpServer = new McpServer(
|
||||
{ name: 'test-server', version: '1.0.0' },
|
||||
{
|
||||
capabilities: {
|
||||
logging: {},
|
||||
tools: {},
|
||||
resources: {},
|
||||
prompts: {}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Add a simple resource
|
||||
mcpServer.resource('test-resource', '/test', { description: 'A test resource' }, async () => ({
|
||||
contents: [
|
||||
{
|
||||
uri: '/test',
|
||||
text: 'This is a test resource content'
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
mcpServer.prompt('test-prompt', 'A test prompt', async () => ({
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: 'This is a test prompt'
|
||||
}
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
mcpServer.tool(
|
||||
'greet',
|
||||
'A simple greeting tool',
|
||||
{
|
||||
name: z.string().describe('Name to greet').default('World')
|
||||
},
|
||||
async ({ name }) => {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Hello, ${name}!` }]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Create transport with or without session management
|
||||
const serverTransport = new NodeStreamableHTTPServerTransport({
|
||||
sessionIdGenerator: withSessionManagement
|
||||
? () => randomUUID() // With session management, generate UUID
|
||||
: undefined // Without session management, return undefined
|
||||
});
|
||||
|
||||
await mcpServer.connect(serverTransport);
|
||||
|
||||
server.on('request', async (req, res) => {
|
||||
await serverTransport.handleRequest(req, res);
|
||||
});
|
||||
|
||||
// Start the server on a random port
|
||||
const baseUrl = await listenOnRandomPort(server);
|
||||
|
||||
return { server, mcpServer, serverTransport, baseUrl };
|
||||
}
|
||||
|
||||
describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
||||
const { z } = entry;
|
||||
describe('Streamable HTTP Transport Session Management', () => {
|
||||
// Function to set up the server with optional session management
|
||||
async function setupServer(withSessionManagement: boolean) {
|
||||
const server: Server = createServer();
|
||||
const mcpServer = new McpServer(
|
||||
{ name: 'test-server', version: '1.0.0' },
|
||||
{
|
||||
capabilities: {
|
||||
logging: {},
|
||||
tools: {},
|
||||
resources: {},
|
||||
prompts: {}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Add a simple resource
|
||||
mcpServer.resource('test-resource', '/test', { description: 'A test resource' }, async () => ({
|
||||
contents: [
|
||||
{
|
||||
uri: '/test',
|
||||
text: 'This is a test resource content'
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
mcpServer.prompt('test-prompt', 'A test prompt', async () => ({
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: {
|
||||
type: 'text',
|
||||
text: 'This is a test prompt'
|
||||
}
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
mcpServer.tool(
|
||||
'greet',
|
||||
'A simple greeting tool',
|
||||
{
|
||||
name: z.string().describe('Name to greet').default('World')
|
||||
},
|
||||
async ({ name }) => {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Hello, ${name}!` }]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Create transport with or without session management
|
||||
const serverTransport = new NodeStreamableHTTPServerTransport({
|
||||
sessionIdGenerator: withSessionManagement
|
||||
? () => randomUUID() // With session management, generate UUID
|
||||
: undefined // Without session management, return undefined
|
||||
});
|
||||
|
||||
await mcpServer.connect(serverTransport);
|
||||
|
||||
server.on('request', async (req, res) => {
|
||||
await serverTransport.handleRequest(req, res);
|
||||
});
|
||||
|
||||
// Start the server on a random port
|
||||
const baseUrl = await listenOnRandomPort(server);
|
||||
|
||||
return { server, mcpServer, serverTransport, baseUrl };
|
||||
}
|
||||
|
||||
describe('Stateless Mode', () => {
|
||||
let server: Server;
|
||||
let mcpServer: McpServer;
|
||||
|
|
@ -94,7 +94,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
let baseUrl: URL;
|
||||
|
||||
beforeEach(async () => {
|
||||
const setup = await setupServer(false);
|
||||
const setup = await setupServer(false, z);
|
||||
server = setup.server;
|
||||
mcpServer = setup.mcpServer;
|
||||
serverTransport = setup.serverTransport;
|
||||
|
|
@ -258,7 +258,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
let baseUrl: URL;
|
||||
|
||||
beforeEach(async () => {
|
||||
const setup = await setupServer(true);
|
||||
const setup = await setupServer(true, z);
|
||||
server = setup.server;
|
||||
mcpServer = setup.mcpServer;
|
||||
serverTransport = setup.serverTransport;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
{
|
||||
async createTask({ duration, shouldFail }, extra) {
|
||||
const task = await extra.taskStore.createTask({
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
pollInterval: 100
|
||||
});
|
||||
|
||||
|
|
@ -75,16 +75,14 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
await new Promise(resolve => setTimeout(resolve, duration));
|
||||
|
||||
try {
|
||||
if (shouldFail) {
|
||||
await extra.taskStore.storeTaskResult(task.taskId, 'failed', {
|
||||
content: [{ type: 'text', text: 'Task failed as requested' }],
|
||||
isError: true
|
||||
});
|
||||
} else {
|
||||
await extra.taskStore.storeTaskResult(task.taskId, 'completed', {
|
||||
content: [{ type: 'text', text: `Completed after ${duration}ms` }]
|
||||
});
|
||||
}
|
||||
await (shouldFail
|
||||
? extra.taskStore.storeTaskResult(task.taskId, 'failed', {
|
||||
content: [{ type: 'text', text: 'Task failed as requested' }],
|
||||
isError: true
|
||||
})
|
||||
: extra.taskStore.storeTaskResult(task.taskId, 'completed', {
|
||||
content: [{ type: 'text', text: `Completed after ${duration}ms` }]
|
||||
}));
|
||||
} catch {
|
||||
// Task may have been cleaned up if test ended
|
||||
}
|
||||
|
|
@ -119,7 +117,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
{
|
||||
async createTask({ userName }, extra) {
|
||||
const task = await extra.taskStore.createTask({
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
pollInterval: 100
|
||||
});
|
||||
|
||||
|
|
@ -128,7 +126,16 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
// If userName not provided, request it via elicitation
|
||||
if (!userName) {
|
||||
if (userName) {
|
||||
// Complete immediately if userName was provided
|
||||
try {
|
||||
await extra.taskStore.storeTaskResult(task.taskId, 'completed', {
|
||||
content: [{ type: 'text', text: `Hello, ${userName}!` }]
|
||||
});
|
||||
} catch {
|
||||
// Task may have been cleaned up if test ended
|
||||
}
|
||||
} else {
|
||||
const elicitationResult = await extra.sendRequest(
|
||||
{
|
||||
method: 'elicitation/create',
|
||||
|
|
@ -160,15 +167,6 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
} catch {
|
||||
// Task may have been cleaned up if test ended
|
||||
}
|
||||
} else {
|
||||
// Complete immediately if userName was provided
|
||||
try {
|
||||
await extra.taskStore.storeTaskResult(task.taskId, 'completed', {
|
||||
content: [{ type: 'text', text: `Hello, ${userName}!` }]
|
||||
});
|
||||
} catch {
|
||||
// Task may have been cleaned up if test ended
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
|
@ -232,7 +230,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
shouldFail: false
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -243,7 +241,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(createResult).toHaveProperty('task');
|
||||
expect(createResult.task).toHaveProperty('taskId');
|
||||
expect(createResult.task.status).toBe('working');
|
||||
expect(createResult.task.ttl).toBe(60000);
|
||||
expect(createResult.task.ttl).toBe(60_000);
|
||||
expect(createResult.task.createdAt).toBeDefined();
|
||||
expect(createResult.task.pollInterval).toBe(100);
|
||||
|
||||
|
|
@ -288,7 +286,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
shouldFail: true
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -332,7 +330,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
duration: 5000
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -381,7 +379,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
duration: 100
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -423,7 +421,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
{
|
||||
async createTask({ requestCount }, extra) {
|
||||
const task = await extra.taskStore.createTask({
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
pollInterval: 100
|
||||
});
|
||||
|
||||
|
|
@ -533,7 +531,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
requestCount: 3
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -575,7 +573,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(task.status).toBe('completed');
|
||||
|
||||
await transport.close();
|
||||
}, 10000);
|
||||
}, 10_000);
|
||||
});
|
||||
|
||||
describe('Input Required Flow', () => {
|
||||
|
|
@ -629,7 +627,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
name: 'input-task',
|
||||
arguments: {},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -694,7 +692,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(finalTask.status).toBe('completed');
|
||||
|
||||
await transport.close();
|
||||
}, 15000);
|
||||
}, 15_000);
|
||||
});
|
||||
|
||||
describe('Task Listing and Pagination', () => {
|
||||
|
|
@ -719,7 +717,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
duration: 1000
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -757,7 +755,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
duration: 5000
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -880,7 +878,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
const taskId = createResult.task.taskId;
|
||||
|
||||
// Verify TTL is set correctly
|
||||
expect(createResult.task.ttl).toBe(60000); // The task store uses 60000 as default
|
||||
expect(createResult.task.ttl).toBe(60_000); // The task store uses 60000 as default
|
||||
|
||||
// Task should exist
|
||||
const task = await client.request(
|
||||
|
|
@ -891,7 +889,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
TaskSchema
|
||||
);
|
||||
expect(task).toBeDefined();
|
||||
expect(task.ttl).toBe(60000);
|
||||
expect(task.ttl).toBe(60_000);
|
||||
|
||||
await transport.close();
|
||||
});
|
||||
|
|
@ -912,7 +910,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
{
|
||||
async createTask({ messageCount }, extra) {
|
||||
const task = await extra.taskStore.createTask({
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
pollInterval: 100
|
||||
});
|
||||
|
||||
|
|
@ -1012,7 +1010,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
messageCount: 2
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1091,7 +1089,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(elicitationCallCount).toBe(0);
|
||||
|
||||
await transport.close();
|
||||
}, 10000);
|
||||
}, 10_000);
|
||||
});
|
||||
|
||||
describe('Continuous Message Delivery', () => {
|
||||
|
|
@ -1110,7 +1108,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
{
|
||||
async createTask({ messageCount, delayBetweenMessages }, extra) {
|
||||
const task = await extra.taskStore.createTask({
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
pollInterval: 100
|
||||
});
|
||||
|
||||
|
|
@ -1240,7 +1238,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
delayBetweenMessages: 300
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1308,7 +1306,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(task.status).toBe('completed');
|
||||
|
||||
await transport.close();
|
||||
}, 15000); // Increase timeout to 15 seconds to allow for message delays
|
||||
}, 15_000); // Increase timeout to 15 seconds to allow for message delays
|
||||
});
|
||||
|
||||
describe('Terminal Task with Queued Messages', () => {
|
||||
|
|
@ -1326,7 +1324,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
{
|
||||
async createTask({ messageCount }, extra) {
|
||||
const task = await extra.taskStore.createTask({
|
||||
ttl: 60000,
|
||||
ttl: 60_000,
|
||||
pollInterval: 100
|
||||
});
|
||||
|
||||
|
|
@ -1445,7 +1443,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
messageCount: 2
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1493,7 +1491,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(result2.content).toEqual([{ type: 'text', text: 'Task completed quickly' }]);
|
||||
|
||||
await transport.close();
|
||||
}, 10000);
|
||||
}, 10_000);
|
||||
});
|
||||
|
||||
describe('Concurrent Operations', () => {
|
||||
|
|
@ -1517,7 +1515,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
duration: 500
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1558,7 +1556,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
duration: 2000
|
||||
},
|
||||
task: {
|
||||
ttl: 60000
|
||||
ttl: 60_000
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1626,7 +1624,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
|
||||
// Use callToolStream instead of raw request()
|
||||
const stream = client.experimental.tasks.callToolStream({ name: 'input-task', arguments: {} }, CallToolResultSchema, {
|
||||
task: { ttl: 60000 }
|
||||
task: { ttl: 60_000 }
|
||||
});
|
||||
|
||||
// Collect all stream messages
|
||||
|
|
@ -1647,7 +1645,7 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(statusMessages.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
// Last message should be result
|
||||
const lastMessage = messages[messages.length - 1]!;
|
||||
const lastMessage = messages.at(-1)!;
|
||||
expect(lastMessage.type).toBe('result');
|
||||
expect(lastMessage.result).toBeDefined();
|
||||
|
||||
|
|
@ -1660,6 +1658,6 @@ describe('Task Lifecycle Integration Tests', () => {
|
|||
expect(result.content).toEqual([{ type: 'text', text: 'Hello, StreamUser!' }]);
|
||||
|
||||
await transport.close();
|
||||
}, 15000);
|
||||
}, 15_000);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class InMemoryEventStore implements EventStore {
|
|||
if (!streamId) return '';
|
||||
|
||||
let found = false;
|
||||
const sorted = [...this.events.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
||||
const sorted = [...this.events.entries()].toSorted((a, b) => a[0].localeCompare(b[0]));
|
||||
for (const [eventId, { streamId: sid, message }] of sorted) {
|
||||
if (sid !== streamId) continue;
|
||||
if (eventId === lastEventId) {
|
||||
|
|
@ -244,11 +244,11 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
|
|||
// any in-progress requests, which is expected behavior
|
||||
await transport1.close();
|
||||
// Save the promise so we can catch it after closing
|
||||
const catchPromise = toolPromise.catch(err => {
|
||||
const catchPromise = toolPromise.catch(error => {
|
||||
// This error is expected - the connection was intentionally closed
|
||||
if (err?.code !== -32000) {
|
||||
if (error?.code !== -32_000) {
|
||||
// ConnectionClosed error code
|
||||
console.error('Unexpected error type during transport close:', err);
|
||||
console.error('Unexpected error type during transport close:', error);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue