Skip to content

Commit 2a15851

Browse files
[v1.x] fix: disallow null (infinite) requested TTL (#1339)
Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com>
1 parent 13e30f1 commit 2a15851

File tree

4 files changed

+36
-10
lines changed

4 files changed

+36
-10
lines changed

src/shared/protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ export type RequestHandlerExtra<SendRequestT extends Request, SendNotificationT
265265

266266
taskStore?: RequestTaskStore;
267267

268-
taskRequestedTtl?: number | null;
268+
taskRequestedTtl?: number;
269269

270270
/**
271271
* The original HTTP request.

src/types.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@ export const CursorSchema = z.string();
3535
*/
3636
export const TaskCreationParamsSchema = z.looseObject({
3737
/**
38-
* Time in milliseconds to keep task results available after completion.
39-
* If null, the task has unlimited lifetime until manually cleaned up.
38+
* Requested duration in milliseconds to retain task from creation.
4039
*/
41-
ttl: z.union([z.number(), z.null()]).optional(),
40+
ttl: z.number().optional(),
4241

4342
/**
4443
* Time in milliseconds to wait between task status requests.

test/experimental/tasks/stores/in-memory.test.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,17 +487,16 @@ describe('InMemoryTaskStore', () => {
487487
expect(task).toBeNull();
488488
});
489489

490-
it('should support null TTL for unlimited lifetime', async () => {
491-
// Test that null TTL means unlimited lifetime
492-
const taskParams: TaskCreationParams = {
493-
ttl: null
494-
};
490+
it('should support omitted TTL for unlimited lifetime', async () => {
491+
// Test that omitting TTL means unlimited lifetime (server returns null)
492+
// Per spec: clients omit ttl to let server decide, server returns null for unlimited
493+
const taskParams: TaskCreationParams = {};
495494
const createdTask = await store.createTask(taskParams, 2222, {
496495
method: 'tools/call',
497496
params: {}
498497
});
499498

500-
// The returned task should have null TTL
499+
// The returned task should have null TTL (unlimited)
501500
expect(createdTask.ttl).toBeNull();
502501

503502
// Task should not be cleaned up even after a long time

test/experimental/tasks/task.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, it, expect } from 'vitest';
22
import { isTerminal } from '../../../src/experimental/tasks/interfaces.js';
33
import type { Task } from '../../../src/types.js';
4+
import { TaskCreationParamsSchema } from '../../../src/types.js';
45

56
describe('Task utility functions', () => {
67
describe('isTerminal', () => {
@@ -115,3 +116,30 @@ describe('Task Schema Validation', () => {
115116
});
116117
});
117118
});
119+
120+
describe('TaskCreationParams Schema Validation', () => {
121+
it('should accept ttl as a number', () => {
122+
const result = TaskCreationParamsSchema.safeParse({ ttl: 60000 });
123+
expect(result.success).toBe(true);
124+
});
125+
126+
it('should accept missing ttl (optional)', () => {
127+
const result = TaskCreationParamsSchema.safeParse({});
128+
expect(result.success).toBe(true);
129+
});
130+
131+
it('should reject null ttl (not allowed in request, only response)', () => {
132+
const result = TaskCreationParamsSchema.safeParse({ ttl: null });
133+
expect(result.success).toBe(false);
134+
});
135+
136+
it('should accept pollInterval as a number', () => {
137+
const result = TaskCreationParamsSchema.safeParse({ pollInterval: 1000 });
138+
expect(result.success).toBe(true);
139+
});
140+
141+
it('should accept both ttl and pollInterval', () => {
142+
const result = TaskCreationParamsSchema.safeParse({ ttl: 60000, pollInterval: 1000 });
143+
expect(result.success).toBe(true);
144+
});
145+
});

0 commit comments

Comments
 (0)