Skip to content
Merged
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: 2 additions & 0 deletions src/mcp/client/experimental/task_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,13 @@ class ExperimentalTaskHandlers:
WARNING: These APIs are experimental and may change without notice.

Example:
```python
handlers = ExperimentalTaskHandlers(
get_task=my_get_task_handler,
list_tasks=my_list_tasks_handler,
)
session = ClientSession(..., experimental_task_handlers=handlers)
```
"""

# Pure task request handlers
Expand Down
6 changes: 6 additions & 0 deletions src/mcp/client/experimental/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
WARNING: These APIs are experimental and may change without notice.

Example:
```python
# Call a tool as a task
result = await session.experimental.call_tool_as_task("tool_name", {"arg": "value"})
task_id = result.task.task_id
Expand All @@ -21,6 +22,7 @@

# Cancel a task
await session.experimental.cancel_task(task_id)
```
"""

from collections.abc import AsyncIterator
Expand Down Expand Up @@ -72,6 +74,7 @@ async def call_tool_as_task(
CreateTaskResult containing the task reference

Example:
```python
# Create task
result = await session.experimental.call_tool_as_task(
"long_running_tool", {"input": "data"}
Expand All @@ -87,6 +90,7 @@ async def call_tool_as_task(

# Get result
final = await session.experimental.get_task_result(task_id, CallToolResult)
```
"""
return await self._session.send_request(
types.CallToolRequest(
Expand Down Expand Up @@ -189,6 +193,7 @@ async def poll_task(self, task_id: str) -> AsyncIterator[types.GetTaskResult]:
GetTaskResult for each poll

Example:
```python
async for status in session.experimental.poll_task(task_id):
print(f"Status: {status.status}")
if status.status == "input_required":
Expand All @@ -197,6 +202,7 @@ async def poll_task(self, task_id: str) -> AsyncIterator[types.GetTaskResult]:

# Task is now terminal, get the result
result = await session.experimental.get_task_result(task_id, CallToolResult)
```
"""
async for status in poll_until_terminal(self.get_task, task_id):
yield status
2 changes: 2 additions & 0 deletions src/mcp/client/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ def experimental(self) -> ExperimentalClientFeatures:
These APIs are experimental and may change without notice.

Example:
```python
status = await session.experimental.get_task(task_id)
result = await session.experimental.get_task_result(task_id, CallToolResult)
```
"""
if self._experimental_features is None:
self._experimental_features = ExperimentalClientFeatures(self)
Expand Down
5 changes: 3 additions & 2 deletions src/mcp/client/session_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,14 @@ class ClientSessionGroup:
For auxiliary handlers, such as resource subscription, this is delegated to
the client and can be accessed via the session.

Example Usage:
Example:
```python
name_fn = lambda name, server_info: f"{(server_info.name)}_{name}"
async with ClientSessionGroup(component_name_hook=name_fn) as group:
for server_param in server_params:
await group.connect_to_server(server_param)
...

```
"""

class _ComponentNames(BaseModel):
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/server/experimental/request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ async def run_task(
RuntimeError: If task support is not enabled or task_metadata is missing

Example:
```python
async def handle_tool(ctx: RequestContext, params: CallToolRequestParams) -> CallToolResult:
async def work(task: ServerTaskContext) -> CallToolResult:
result = await task.elicit(
Expand All @@ -170,6 +171,7 @@ async def work(task: ServerTaskContext) -> CallToolResult:
return CallToolResult(content=[TextContent(text="Done" if confirmed else "Cancelled")])

return await ctx.experimental.run_task(work)
```

WARNING: This API is experimental and may change without notice.
"""
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/server/experimental/task_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class ServerTaskContext:
- Status notifications via the session

Example:
```python
async def my_task_work(task: ServerTaskContext) -> CallToolResult:
await task.update_status("Starting...")

Expand All @@ -68,6 +69,7 @@ async def my_task_work(task: ServerTaskContext) -> CallToolResult:
return CallToolResult(content=[TextContent(text="Done!")])
else:
return CallToolResult(content=[TextContent(text="Cancelled")])
```
"""

def __init__(
Expand Down
10 changes: 8 additions & 2 deletions src/mcp/server/experimental/task_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,20 @@ class TaskSupport:
- Manages a task group for background task execution

Example:
# Simple in-memory setup
Simple in-memory setup:

```python
server.experimental.enable_tasks()
```

Custom store/queue for distributed systems:

# Custom store/queue for distributed systems
```python
server.experimental.enable_tasks(
store=RedisTaskStore(redis_url),
queue=RedisTaskMessageQueue(redis_url),
)
```
"""

store: TaskStore
Expand Down
10 changes: 8 additions & 2 deletions src/mcp/server/lowlevel/experimental.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,20 @@ def enable_tasks(
The TaskSupport configuration object

Example:
# Simple in-memory setup
Simple in-memory setup:

```python
server.experimental.enable_tasks()
```

Custom store/queue for distributed systems:

# Custom store/queue for distributed systems
```python
server.experimental.enable_tasks(
store=RedisTaskStore(redis_url),
queue=RedisTaskMessageQueue(redis_url),
)
```

WARNING: This API is experimental and may change without notice.
"""
Expand Down
14 changes: 14 additions & 0 deletions src/mcp/server/mcpserver/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,19 +535,25 @@ def tool(
- If False, unconditionally creates an unstructured tool

Example:
```python
@server.tool()
def my_tool(x: int) -> str:
return str(x)
```

```python
@server.tool()
async def tool_with_context(x: int, ctx: Context) -> str:
await ctx.info(f"Processing {x}")
return str(x)
```

```python
@server.tool()
async def async_tool(x: int, context: Context) -> str:
await context.report_progress(50, 100)
return str(x)
```
"""
# Check if user passed function directly instead of calling decorator
if callable(name):
Expand Down Expand Up @@ -579,12 +585,14 @@ def completion(self):
- context: Optional CompletionContext with previously resolved arguments

Example:
```python
@mcp.completion()
async def handle_completion(ref, argument, context):
if isinstance(ref, ResourceTemplateReference):
# Return completions based on ref, argument, and context
return Completion(values=["option1", "option2"])
return None
```
"""

def decorator(func: _CallableT) -> _CallableT:
Expand Down Expand Up @@ -647,6 +655,7 @@ def resource(
meta: Optional metadata dictionary for the resource

Example:
```python
@server.resource("resource://my-resource")
def get_data() -> str:
return "Hello, world!"
Expand All @@ -664,6 +673,7 @@ def get_weather(city: str) -> str:
async def get_weather(city: str) -> str:
data = await fetch_weather(city)
return f"Weather for {city}: {data}"
```
"""
# Check if user passed function directly instead of calling decorator
if callable(uri):
Expand Down Expand Up @@ -747,6 +757,7 @@ def prompt(
icons: Optional list of icons for the prompt

Example:
```python
@server.prompt()
def analyze_table(table_name: str) -> list[Message]:
schema = read_table_schema(table_name)
Expand All @@ -772,6 +783,7 @@ async def analyze_file(path: str) -> list[Message]:
}
}
]
```
"""
# Check if user passed function directly instead of calling decorator
if callable(name):
Expand Down Expand Up @@ -813,9 +825,11 @@ def custom_route(
include_in_schema: Whether to include in OpenAPI schema, defaults to True

Example:
```python
@server.custom_route("/health", methods=["GET"])
async def health_check(request: Request) -> Response:
return JSONResponse({"status": "ok"})
```
"""

def decorator( # pragma: no cover
Expand Down
6 changes: 3 additions & 3 deletions src/mcp/server/sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

This module implements a Server-Sent Events (SSE) transport layer for MCP servers.

Example usage:
```
Example:
```python
# Create an SSE transport at an endpoint
sse = SseServerTransport("/messages/")

Expand All @@ -27,7 +27,7 @@ async def handle_sse(request):
# Create and run Starlette app
starlette_app = Starlette(routes=routes)
uvicorn.run(starlette_app, host="127.0.0.1", port=port)
```
```

Note: The handle_sse function must return a Response to avoid a
"TypeError: 'NoneType' object is not callable" error when client disconnects. The example above returns
Expand Down
6 changes: 3 additions & 3 deletions src/mcp/server/stdio.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
that can be used to communicate with an MCP client through standard input/output
streams.

Example usage:
```
Example:
```python
async def run_server():
async with stdio_server() as (read_stream, write_stream):
# read_stream contains incoming JSONRPCMessages from stdin
Expand All @@ -14,7 +14,7 @@ async def run_server():
await server.run(read_stream, write_stream, init_options)

anyio.run(run_server)
```
```
"""

import sys
Expand Down
22 changes: 17 additions & 5 deletions src/mcp/shared/_httpx_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,38 @@ def create_mcp_http_client(
The returned AsyncClient must be used as a context manager to ensure
proper cleanup of connections.

Examples:
# Basic usage with MCP defaults
Example:
Basic usage with MCP defaults:

```python
async with create_mcp_http_client() as client:
response = await client.get("https://api.example.com")
```

With custom headers:

# With custom headers
```python
headers = {"Authorization": "Bearer token"}
async with create_mcp_http_client(headers) as client:
response = await client.get("/endpoint")
```

# With both custom headers and timeout
With both custom headers and timeout:

```python
timeout = httpx.Timeout(60.0, read=300.0)
async with create_mcp_http_client(headers, timeout) as client:
response = await client.get("/long-request")
```

With authentication:

# With authentication
```python
from httpx import BasicAuth
auth = BasicAuth(username="user", password="pass")
async with create_mcp_http_client(headers, timeout, auth) as client:
response = await client.get("/protected-endpoint")
```
"""
# Set MCP defaults
kwargs: dict[str, Any] = {"follow_redirects": True}
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/shared/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@ class UrlElicitationRequiredError(MCPError):
must complete one or more URL elicitations before the request can be processed.

Example:
```python
raise UrlElicitationRequiredError([
ElicitRequestURLParams(
message="Authorization required for your files",
url="https://example.com/oauth/authorize",
elicitation_id="auth-001"
)
])
```
"""

def __init__(self, elicitations: list[ElicitRequestURLParams], message: str | None = None):
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/shared/experimental/tasks/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ async def cancel_task(
- Task is already in a terminal state (completed, failed, cancelled)

Example:
```python
async def handle_cancel(ctx, params: CancelTaskRequestParams) -> CancelTaskResult:
return await cancel_task(store, params.task_id)
```
"""
task = await store.get_task(task_id)
if task is None:
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/shared/metadata_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ def get_display_name(obj: Tool | Resource | Prompt | ResourceTemplate | Implemen
For other objects: title > name

Example:
```python
# In a client displaying available tools
tools = await session.list_tools()
for tool in tools.tools:
display_name = get_display_name(tool)
print(f"Available tool: {display_name}")
```

Args:
obj: An MCP object with name and optional title fields
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/shared/response_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ class ResponseRouter(Protocol):
and deliver the response/error to the appropriate handler.

Example:
```python
class TaskResultHandler(ResponseRouter):
def route_response(self, request_id, response):
resolver = self._pending_requests.pop(request_id, None)
if resolver:
resolver.set_result(response)
return True
return False
```
"""

def route_response(self, request_id: RequestId, response: dict[str, Any]) -> bool:
Expand Down
Loading