Skip to content

Commit a5d4751

Browse files
committed
client
1 parent 10c605a commit a5d4751

File tree

10 files changed

+664
-0
lines changed

10 files changed

+664
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"""
2+
Experimental client features.
3+
4+
WARNING: These APIs are experimental and may change without notice.
5+
"""
6+
7+
from mcp.client.experimental.tasks import ExperimentalClientFeatures
8+
9+
__all__ = ["ExperimentalClientFeatures"]
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"""
2+
Experimental client-side task support.
3+
4+
This module provides client methods for interacting with MCP tasks.
5+
6+
WARNING: These APIs are experimental and may change without notice.
7+
8+
Example:
9+
# Get task status
10+
status = await session.experimental.get_task(task_id)
11+
12+
# Get task result when complete
13+
if status.status == "completed":
14+
result = await session.experimental.get_task_result(task_id, CallToolResult)
15+
16+
# List all tasks
17+
tasks = await session.experimental.list_tasks()
18+
19+
# Cancel a task
20+
await session.experimental.cancel_task(task_id)
21+
"""
22+
23+
from typing import TYPE_CHECKING, TypeVar
24+
25+
import mcp.types as types
26+
27+
if TYPE_CHECKING:
28+
from mcp.client.session import ClientSession
29+
30+
ResultT = TypeVar("ResultT", bound=types.Result)
31+
32+
33+
class ExperimentalClientFeatures:
34+
"""
35+
Experimental client features for tasks and other experimental APIs.
36+
37+
WARNING: These APIs are experimental and may change without notice.
38+
39+
Access via session.experimental:
40+
status = await session.experimental.get_task(task_id)
41+
"""
42+
43+
def __init__(self, session: "ClientSession") -> None:
44+
self._session = session
45+
46+
async def get_task(self, task_id: str) -> types.GetTaskResult:
47+
"""
48+
Get the current status of a task.
49+
50+
Args:
51+
task_id: The task identifier
52+
53+
Returns:
54+
GetTaskResult containing the task status and metadata
55+
"""
56+
return await self._session.send_request(
57+
types.ClientRequest(
58+
types.GetTaskRequest(
59+
params=types.GetTaskRequestParams(taskId=task_id),
60+
)
61+
),
62+
types.GetTaskResult,
63+
)
64+
65+
async def get_task_result(
66+
self,
67+
task_id: str,
68+
result_type: type[ResultT],
69+
) -> ResultT:
70+
"""
71+
Get the result of a completed task.
72+
73+
The result type depends on the original request type:
74+
- tools/call tasks return CallToolResult
75+
- Other request types return their corresponding result type
76+
77+
Args:
78+
task_id: The task identifier
79+
result_type: The expected result type (e.g., CallToolResult)
80+
81+
Returns:
82+
The task result, validated against result_type
83+
"""
84+
return await self._session.send_request(
85+
types.ClientRequest(
86+
types.GetTaskPayloadRequest(
87+
params=types.GetTaskPayloadRequestParams(taskId=task_id),
88+
)
89+
),
90+
result_type,
91+
)
92+
93+
async def list_tasks(
94+
self,
95+
cursor: str | None = None,
96+
) -> types.ListTasksResult:
97+
"""
98+
List all tasks.
99+
100+
Args:
101+
cursor: Optional pagination cursor
102+
103+
Returns:
104+
ListTasksResult containing tasks and optional next cursor
105+
"""
106+
params = types.PaginatedRequestParams(cursor=cursor) if cursor else None
107+
return await self._session.send_request(
108+
types.ClientRequest(
109+
types.ListTasksRequest(params=params),
110+
),
111+
types.ListTasksResult,
112+
)
113+
114+
async def cancel_task(self, task_id: str) -> types.CancelTaskResult:
115+
"""
116+
Cancel a running task.
117+
118+
Args:
119+
task_id: The task identifier
120+
121+
Returns:
122+
CancelTaskResult with the updated task state
123+
"""
124+
return await self._session.send_request(
125+
types.ClientRequest(
126+
types.CancelTaskRequest(
127+
params=types.CancelTaskRequestParams(taskId=task_id),
128+
)
129+
),
130+
types.CancelTaskResult,
131+
)

src/mcp/client/session.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing_extensions import deprecated
1010

1111
import mcp.types as types
12+
from mcp.client.experimental import ExperimentalClientFeatures
1213
from mcp.shared.context import RequestContext
1314
from mcp.shared.message import SessionMessage
1415
from mcp.shared.session import BaseSession, ProgressFnT, RequestResponder
@@ -135,6 +136,7 @@ def __init__(
135136
self._message_handler = message_handler or _default_message_handler
136137
self._tool_output_schemas: dict[str, dict[str, Any] | None] = {}
137138
self._server_capabilities: types.ServerCapabilities | None = None
139+
self._experimental: ExperimentalClientFeatures | None = None
138140

139141
async def initialize(self) -> types.InitializeResult:
140142
sampling = types.SamplingCapability() if self._sampling_callback is not _default_sampling_callback else None
@@ -184,6 +186,20 @@ def get_server_capabilities(self) -> types.ServerCapabilities | None:
184186
"""
185187
return self._server_capabilities
186188

189+
@property
190+
def experimental(self) -> "ExperimentalClientFeatures":
191+
"""Experimental APIs for tasks and other features.
192+
193+
WARNING: These APIs are experimental and may change without notice.
194+
195+
Example:
196+
status = await session.experimental.get_task(task_id)
197+
result = await session.experimental.get_task_result(task_id, CallToolResult)
198+
"""
199+
if self._experimental is None:
200+
self._experimental = ExperimentalClientFeatures(self)
201+
return self._experimental
202+
187203
async def send_ping(self) -> types.EmptyResult:
188204
"""Send a ping request."""
189205
return await self.send_request(

tests/experimental/tasks/client/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)