Skip to content

Commit 64b38bf

Browse files
JAORMXclaude
andauthored
Add server-side support for MCP tasks (#635)
* feat(mcp): add task types and helper functions Add complete task support types to the MCP protocol implementation: - Task status constants and lifecycle methods - Task request/result types for all operations (get, list, result, cancel) - TasksCapability for capability negotiation - Helper functions for creating tasks and task results - Task status notifications This implements the task primitive as defined in the MCP specification draft, enabling long-running operations with status polling and deferred result retrieval. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(server): add task capability support Add task capabilities to the MCP server: - taskCapabilities struct to track list, cancel, and tool call task support - WithTaskCapabilities() server option for configuration - Task capability negotiation in initialize response - Proper capability advertising following MCP spec patterns Enables servers to advertise support for task-augmented tool calls and task management operations (list, cancel). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(server): implement task storage and lifecycle management Add comprehensive task management to the server: - taskEntry struct to hold task state, session binding, and results - Task storage with session isolation for security - Task lifecycle methods: create, get, list, update, complete, cancel - Automatic TTL-based cleanup for expired tasks - Thread-safe operations with RWMutex - Context cancellation support for running tasks Implements the foundation for task-augmented requests with proper session isolation and resource management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(server): add task methods to code generation data Add task operation entries to code generation system: - tasks/get - Retrieve task status - tasks/list - List all tasks - tasks/result - Get task result - tasks/cancel - Cancel a task This enables automatic generation of request handlers and hooks for all task operations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(server): regenerate hooks and request handlers for tasks Regenerate server code from templates to include task support: - Add task method hooks (GetTask, ListTasks, TaskResult, CancelTask) - Add request handler cases for all task operations - Include capability checks for task support - Add proper error handling and hook invocations Generated from code generation templates based on data.go entries. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * feat(server): implement task request handlers Add handler functions for all task operations: - handleGetTask: Retrieve current task status with session isolation - handleListTasks: List all tasks for current session with pagination support - handleTaskResult: Wait for and retrieve task results, blocking if needed - handleCancelTask: Cancel running tasks and update status Handlers follow existing patterns with proper error handling, context cancellation support, and request/result struct usage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test(server): add comprehensive tests for task functionality Add complete test coverage for task operations: - Capability negotiation and configuration - Task lifecycle (create, get, list, complete, cancel) - Handler functions for all task operations - Error handling and edge cases - TTL-based cleanup - Blocking result retrieval - Session isolation - Terminal status validation - JSON marshaling All tests pass. Covers happy paths, error cases, and concurrent operations. Co-Authored-By: Claude <noreply@anthropic.com> * fix(server): address PR review feedback for task implementation - Fix NewTasksCapabilityWithToolsOnly to only enable tool call support (List and Cancel are now nil as the function name implies) - Fix potential nil dereference in createTask when ttl/pollInterval are nil - Fix data races by returning task copies from getTask and listTasks - Fix risk of double-closing done channel by adding completed flag guard - Add test for NewTasksCapabilityWithToolsOnly behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix(server): resolve linter warnings in task implementation Remove empty if branch and unused updateTaskStatus function. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 919c4bf commit 64b38bf

File tree

7 files changed

+1503
-0
lines changed

7 files changed

+1503
-0
lines changed

mcp/tasks.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package mcp
2+
3+
import (
4+
"time"
5+
)
6+
7+
// TaskOption is a function that configures a Task.
8+
// It provides a flexible way to set various properties of a Task using the functional options pattern.
9+
type TaskOption func(*Task)
10+
11+
//
12+
// Core Task Functions
13+
//
14+
15+
// NewTask creates a new Task with the given ID and options.
16+
// The task will be configured based on the provided options.
17+
// Options are applied in order, allowing for flexible task configuration.
18+
func NewTask(taskId string, opts ...TaskOption) Task {
19+
task := Task{
20+
TaskId: taskId,
21+
Status: TaskStatusWorking,
22+
CreatedAt: time.Now().UTC().Format(time.RFC3339),
23+
}
24+
25+
for _, opt := range opts {
26+
opt(&task)
27+
}
28+
29+
return task
30+
}
31+
32+
// WithTaskStatus sets the status of the task.
33+
func WithTaskStatus(status TaskStatus) TaskOption {
34+
return func(t *Task) {
35+
t.Status = status
36+
}
37+
}
38+
39+
// WithTaskStatusMessage sets a human-readable status message for the task.
40+
func WithTaskStatusMessage(message string) TaskOption {
41+
return func(t *Task) {
42+
t.StatusMessage = message
43+
}
44+
}
45+
46+
// WithTaskTTL sets the time-to-live for the task in milliseconds.
47+
// After this duration from creation, the task may be deleted.
48+
func WithTaskTTL(ttlMs int64) TaskOption {
49+
return func(t *Task) {
50+
t.TTL = &ttlMs
51+
}
52+
}
53+
54+
// WithTaskPollInterval sets the suggested polling interval in milliseconds.
55+
func WithTaskPollInterval(intervalMs int64) TaskOption {
56+
return func(t *Task) {
57+
t.PollInterval = &intervalMs
58+
}
59+
}
60+
61+
// WithTaskCreatedAt sets a specific creation timestamp for the task.
62+
// By default, NewTask uses the current time.
63+
func WithTaskCreatedAt(createdAt string) TaskOption {
64+
return func(t *Task) {
65+
t.CreatedAt = createdAt
66+
}
67+
}
68+
69+
//
70+
// Task Helper Functions
71+
//
72+
73+
// NewTaskParams creates TaskParams with the given TTL.
74+
func NewTaskParams(ttlMs *int64) TaskParams {
75+
return TaskParams{
76+
TTL: ttlMs,
77+
}
78+
}
79+
80+
// NewCreateTaskResult creates a CreateTaskResult with the given task.
81+
func NewCreateTaskResult(task Task) CreateTaskResult {
82+
return CreateTaskResult{
83+
Task: task,
84+
}
85+
}
86+
87+
// NewGetTaskResult creates a GetTaskResult from a Task.
88+
func NewGetTaskResult(task Task) GetTaskResult {
89+
return GetTaskResult{
90+
Task: task,
91+
}
92+
}
93+
94+
// NewListTasksResult creates a ListTasksResult with the given tasks.
95+
func NewListTasksResult(tasks []Task) ListTasksResult {
96+
return ListTasksResult{
97+
Tasks: tasks,
98+
}
99+
}
100+
101+
// NewCancelTaskResult creates a CancelTaskResult from a Task.
102+
func NewCancelTaskResult(task Task) CancelTaskResult {
103+
return CancelTaskResult{
104+
Task: task,
105+
}
106+
}
107+
108+
// NewTaskStatusNotification creates a notification for a task status change.
109+
func NewTaskStatusNotification(task Task) TaskStatusNotification {
110+
return TaskStatusNotification{
111+
Notification: Notification{
112+
Method: string(MethodNotificationTasksStatus),
113+
},
114+
Params: TaskStatusNotificationParams{
115+
Task: task,
116+
},
117+
}
118+
}
119+
120+
//
121+
// Task Capability Helper Functions
122+
//
123+
124+
// NewTasksCapability creates a TasksCapability with all operations enabled.
125+
func NewTasksCapability() *TasksCapability {
126+
return &TasksCapability{
127+
List: &struct{}{},
128+
Cancel: &struct{}{},
129+
Requests: &TaskRequestsCapability{
130+
Tools: &struct {
131+
Call *struct{} `json:"call,omitempty"`
132+
}{
133+
Call: &struct{}{},
134+
},
135+
},
136+
}
137+
}
138+
139+
// NewTasksCapabilityWithToolsOnly creates a TasksCapability with only tool call support.
140+
// List and Cancel operations are not enabled with this capability.
141+
func NewTasksCapabilityWithToolsOnly() *TasksCapability {
142+
return &TasksCapability{
143+
Requests: &TaskRequestsCapability{
144+
Tools: &struct {
145+
Call *struct{} `json:"call,omitempty"`
146+
}{
147+
Call: &struct{}{},
148+
},
149+
},
150+
}
151+
}

mcp/types.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ const (
6363
// https://modelcontextprotocol.io/specification/2025-06-18/client/roots
6464
MethodListRoots MCPMethod = "roots/list"
6565

66+
// MethodTasksGet retrieves the current status of a task.
67+
// https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks
68+
MethodTasksGet MCPMethod = "tasks/get"
69+
70+
// MethodTasksList lists all tasks for the current session.
71+
// https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks
72+
MethodTasksList MCPMethod = "tasks/list"
73+
74+
// MethodTasksResult retrieves the result of a completed task.
75+
// https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks
76+
MethodTasksResult MCPMethod = "tasks/result"
77+
78+
// MethodTasksCancel cancels an in-progress task.
79+
// https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks
80+
MethodTasksCancel MCPMethod = "tasks/cancel"
81+
6682
// MethodNotificationResourcesListChanged notifies when the list of available resources changes.
6783
// https://modelcontextprotocol.io/specification/2025-03-26/server/resources#list-changed-notification
6884
MethodNotificationResourcesListChanged = "notifications/resources/list_changed"
@@ -80,6 +96,10 @@ const (
8096
// MethodNotificationRootsListChanged notifies when the list of available roots changes.
8197
// https://modelcontextprotocol.io/specification/2025-06-18/client/roots#root-list-changes
8298
MethodNotificationRootsListChanged = "notifications/roots/list_changed"
99+
100+
// MethodNotificationTasksStatus notifies when a task's status changes.
101+
// https://modelcontextprotocol.io/specification/draft/basic/utilities/tasks
102+
MethodNotificationTasksStatus = "notifications/tasks/status"
83103
)
84104

85105
type URITemplate struct {
@@ -491,6 +511,8 @@ type ClientCapabilities struct {
491511
Sampling *struct{} `json:"sampling,omitempty"`
492512
// Present if the client supports elicitation requests from the server.
493513
Elicitation *struct{} `json:"elicitation,omitempty"`
514+
// Present if the client supports task-based execution.
515+
Tasks *TasksCapability `json:"tasks,omitempty"`
494516
}
495517

496518
// ServerCapabilities represents capabilities that a server may support. Known
@@ -525,6 +547,8 @@ type ServerCapabilities struct {
525547
Elicitation *struct{} `json:"elicitation,omitempty"`
526548
// Present if the server supports roots requests to the client.
527549
Roots *struct{} `json:"roots,omitempty"`
550+
// Present if the server supports task-based execution.
551+
Tasks *TasksCapability `json:"tasks,omitempty"`
528552
}
529553

530554
// Icon represents a visual identifier for MCP entities.
@@ -1214,6 +1238,164 @@ type RootsListChangedNotification struct {
12141238
Notification
12151239
}
12161240

1241+
/* Tasks */
1242+
1243+
// TasksCapability represents the task capabilities that a client or server may support.
1244+
// Tasks enable long-running, asynchronous operations with status polling.
1245+
type TasksCapability struct {
1246+
// Whether the party supports the tasks/list operation.
1247+
List *struct{} `json:"list,omitempty"`
1248+
// Whether the party supports the tasks/cancel operation.
1249+
Cancel *struct{} `json:"cancel,omitempty"`
1250+
// Requests that can be augmented with task metadata.
1251+
Requests *TaskRequestsCapability `json:"requests,omitempty"`
1252+
}
1253+
1254+
// TaskRequestsCapability indicates which request types support task augmentation.
1255+
type TaskRequestsCapability struct {
1256+
// Tool-related capabilities.
1257+
Tools *struct {
1258+
// Whether tools/call can be augmented with task metadata.
1259+
Call *struct{} `json:"call,omitempty"`
1260+
} `json:"tools,omitempty"`
1261+
// Sampling-related capabilities.
1262+
Sampling *struct {
1263+
// Whether sampling/createMessage can be augmented with task metadata.
1264+
CreateMessage *struct{} `json:"createMessage,omitempty"`
1265+
} `json:"sampling,omitempty"`
1266+
// Elicitation-related capabilities.
1267+
Elicitation *struct {
1268+
// Whether elicitation/create can be augmented with task metadata.
1269+
Create *struct{} `json:"create,omitempty"`
1270+
} `json:"elicitation,omitempty"`
1271+
}
1272+
1273+
// TaskStatus represents the execution state of a task.
1274+
type TaskStatus string
1275+
1276+
const (
1277+
// TaskStatusWorking indicates the request is currently being processed.
1278+
TaskStatusWorking TaskStatus = "working"
1279+
// TaskStatusInputRequired indicates the receiver needs input from the requestor.
1280+
TaskStatusInputRequired TaskStatus = "input_required"
1281+
// TaskStatusCompleted indicates the request completed successfully.
1282+
TaskStatusCompleted TaskStatus = "completed"
1283+
// TaskStatusFailed indicates the request did not complete successfully.
1284+
TaskStatusFailed TaskStatus = "failed"
1285+
// TaskStatusCancelled indicates the request was cancelled before completion.
1286+
TaskStatusCancelled TaskStatus = "cancelled"
1287+
)
1288+
1289+
// IsTerminal returns true if the task status is terminal (completed, failed, or cancelled).
1290+
func (s TaskStatus) IsTerminal() bool {
1291+
return s == TaskStatusCompleted || s == TaskStatusFailed || s == TaskStatusCancelled
1292+
}
1293+
1294+
// Task represents the execution state of a request.
1295+
type Task struct {
1296+
// Unique identifier for the task.
1297+
TaskId string `json:"taskId"`
1298+
// Current state of the task execution.
1299+
Status TaskStatus `json:"status"`
1300+
// Optional human-readable message describing the current state.
1301+
StatusMessage string `json:"statusMessage,omitempty"`
1302+
// ISO 8601 timestamp when the task was created.
1303+
CreatedAt string `json:"createdAt"`
1304+
// Time in milliseconds from creation before task may be deleted.
1305+
// If null, the task has no expiration.
1306+
TTL *int64 `json:"ttl"`
1307+
// Suggested time in milliseconds between status checks.
1308+
PollInterval *int64 `json:"pollInterval,omitempty"`
1309+
}
1310+
1311+
// TaskParams represents the task metadata included when augmenting a request.
1312+
type TaskParams struct {
1313+
// Requested duration in milliseconds to retain task from creation.
1314+
TTL *int64 `json:"ttl,omitempty"`
1315+
}
1316+
1317+
// CreateTaskResult is returned immediately when a task-augmented request is accepted.
1318+
// It contains task metadata rather than the actual operation result.
1319+
type CreateTaskResult struct {
1320+
Result
1321+
Task Task `json:"task"`
1322+
}
1323+
1324+
// GetTaskRequest retrieves the current status of a task.
1325+
type GetTaskRequest struct {
1326+
Request
1327+
Header http.Header `json:"-"`
1328+
Params GetTaskParams `json:"params"`
1329+
}
1330+
1331+
type GetTaskParams struct {
1332+
TaskId string `json:"taskId"`
1333+
}
1334+
1335+
// GetTaskResult returns the current state of a task.
1336+
type GetTaskResult struct {
1337+
Result
1338+
Task
1339+
}
1340+
1341+
// ListTasksRequest retrieves a paginated list of tasks.
1342+
type ListTasksRequest struct {
1343+
PaginatedRequest
1344+
Header http.Header `json:"-"`
1345+
}
1346+
1347+
// ListTasksResult returns a list of tasks.
1348+
type ListTasksResult struct {
1349+
PaginatedResult
1350+
Tasks []Task `json:"tasks"`
1351+
}
1352+
1353+
// TaskResultRequest retrieves the result of a completed task.
1354+
type TaskResultRequest struct {
1355+
Request
1356+
Header http.Header `json:"-"`
1357+
Params TaskResultParams `json:"params"`
1358+
}
1359+
1360+
type TaskResultParams struct {
1361+
TaskId string `json:"taskId"`
1362+
}
1363+
1364+
// TaskResultResult contains the actual operation result.
1365+
// The structure depends on the original request type.
1366+
type TaskResultResult struct {
1367+
Result
1368+
// The actual result varies by request type (e.g., CallToolResult for tools/call).
1369+
// This will be handled by the specific implementation.
1370+
}
1371+
1372+
// CancelTaskRequest cancels an in-progress task.
1373+
type CancelTaskRequest struct {
1374+
Request
1375+
Header http.Header `json:"-"`
1376+
Params CancelTaskParams `json:"params"`
1377+
}
1378+
1379+
type CancelTaskParams struct {
1380+
TaskId string `json:"taskId"`
1381+
}
1382+
1383+
// CancelTaskResult returns the cancelled task state.
1384+
type CancelTaskResult struct {
1385+
Result
1386+
Task
1387+
}
1388+
1389+
// TaskStatusNotification is sent when a task's status changes.
1390+
type TaskStatusNotification struct {
1391+
Notification
1392+
Params TaskStatusNotificationParams `json:"params"`
1393+
}
1394+
1395+
type TaskStatusNotificationParams struct {
1396+
Task
1397+
}
1398+
12171399
// ClientRequest represents any request that can be sent from client to server.
12181400
type ClientRequest any
12191401

0 commit comments

Comments
 (0)