Skip to content
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
2 changes: 1 addition & 1 deletion src/shared/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export type RequestHandlerExtra<SendRequestT extends Request, SendNotificationT

taskStore?: RequestTaskStore;

taskRequestedTtl?: number | null;
taskRequestedTtl?: number;

/**
* The original HTTP request.
Expand Down
5 changes: 2 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ export const CursorSchema = z.string();
*/
export const TaskCreationParamsSchema = z.looseObject({
/**
* Time in milliseconds to keep task results available after completion.
* If null, the task has unlimited lifetime until manually cleaned up.
* Requested duration in milliseconds to retain task from creation.
*/
ttl: z.union([z.number(), z.null()]).optional(),
ttl: z.number().optional(),

/**
* Time in milliseconds to wait between task status requests.
Expand Down
11 changes: 5 additions & 6 deletions test/experimental/tasks/stores/in-memory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,17 +487,16 @@ describe('InMemoryTaskStore', () => {
expect(task).toBeNull();
});

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

// The returned task should have null TTL
// The returned task should have null TTL (unlimited)
expect(createdTask.ttl).toBeNull();

// Task should not be cleaned up even after a long time
Expand Down
28 changes: 28 additions & 0 deletions test/experimental/tasks/task.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest';
import { isTerminal } from '../../../src/experimental/tasks/interfaces.js';
import type { Task } from '../../../src/types.js';
import { TaskCreationParamsSchema } from '../../../src/types.js';

describe('Task utility functions', () => {
describe('isTerminal', () => {
Expand Down Expand Up @@ -115,3 +116,30 @@ describe('Task Schema Validation', () => {
});
});
});

describe('TaskCreationParams Schema Validation', () => {
it('should accept ttl as a number', () => {
const result = TaskCreationParamsSchema.safeParse({ ttl: 60000 });
expect(result.success).toBe(true);
});

it('should accept missing ttl (optional)', () => {
const result = TaskCreationParamsSchema.safeParse({});
expect(result.success).toBe(true);
});

it('should reject null ttl (not allowed in request, only response)', () => {
const result = TaskCreationParamsSchema.safeParse({ ttl: null });
expect(result.success).toBe(false);
});

it('should accept pollInterval as a number', () => {
const result = TaskCreationParamsSchema.safeParse({ pollInterval: 1000 });
expect(result.success).toBe(true);
});

it('should accept both ttl and pollInterval', () => {
const result = TaskCreationParamsSchema.safeParse({ ttl: 60000, pollInterval: 1000 });
expect(result.success).toBe(true);
});
});
Loading