docs: add code fences to Example: docstring blocks (#2104)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@ This module provides client methods for interacting with MCP tasks.
|
||||
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
|
||||
@@ -21,6 +22,7 @@ Example:
|
||||
|
||||
# Cancel a task
|
||||
await session.experimental.cancel_task(task_id)
|
||||
```
|
||||
"""
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
@@ -72,6 +74,7 @@ class ExperimentalClientFeatures:
|
||||
CreateTaskResult containing the task reference
|
||||
|
||||
Example:
|
||||
```python
|
||||
# Create task
|
||||
result = await session.experimental.call_tool_as_task(
|
||||
"long_running_tool", {"input": "data"}
|
||||
@@ -87,6 +90,7 @@ class ExperimentalClientFeatures:
|
||||
|
||||
# Get result
|
||||
final = await session.experimental.get_task_result(task_id, CallToolResult)
|
||||
```
|
||||
"""
|
||||
return await self._session.send_request(
|
||||
types.CallToolRequest(
|
||||
@@ -189,6 +193,7 @@ class ExperimentalClientFeatures:
|
||||
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":
|
||||
@@ -197,6 +202,7 @@ class ExperimentalClientFeatures:
|
||||
|
||||
# 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
|
||||
|
||||
@@ -206,8 +206,10 @@ class ClientSession(
|
||||
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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -160,6 +160,7 @@ class Experimental:
|
||||
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(
|
||||
@@ -170,6 +171,7 @@ class Experimental:
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -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...")
|
||||
|
||||
@@ -68,6 +69,7 @@ class ServerTaskContext:
|
||||
return CallToolResult(content=[TextContent(text="Done!")])
|
||||
else:
|
||||
return CallToolResult(content=[TextContent(text="Cancelled")])
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -31,14 +31,20 @@ class TaskSupport:
|
||||
- Manages a task group for background task execution
|
||||
|
||||
Example:
|
||||
# Simple in-memory setup
|
||||
server.experimental.enable_tasks()
|
||||
Simple in-memory setup:
|
||||
|
||||
# Custom store/queue for distributed systems
|
||||
```python
|
||||
server.experimental.enable_tasks()
|
||||
```
|
||||
|
||||
Custom store/queue for distributed systems:
|
||||
|
||||
```python
|
||||
server.experimental.enable_tasks(
|
||||
store=RedisTaskStore(redis_url),
|
||||
queue=RedisTaskMessageQueue(redis_url),
|
||||
)
|
||||
```
|
||||
"""
|
||||
|
||||
store: TaskStore
|
||||
|
||||
@@ -118,14 +118,20 @@ class ExperimentalHandlers(Generic[LifespanResultT]):
|
||||
The TaskSupport configuration object
|
||||
|
||||
Example:
|
||||
# Simple in-memory setup
|
||||
server.experimental.enable_tasks()
|
||||
Simple in-memory setup:
|
||||
|
||||
# Custom store/queue for distributed systems
|
||||
```python
|
||||
server.experimental.enable_tasks()
|
||||
```
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -535,19 +535,25 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
- 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):
|
||||
@@ -579,12 +585,14 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
- 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:
|
||||
@@ -647,6 +655,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
meta: Optional metadata dictionary for the resource
|
||||
|
||||
Example:
|
||||
```python
|
||||
@server.resource("resource://my-resource")
|
||||
def get_data() -> str:
|
||||
return "Hello, world!"
|
||||
@@ -664,6 +673,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
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):
|
||||
@@ -747,6 +757,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
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)
|
||||
@@ -772,6 +783,7 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
"""
|
||||
# Check if user passed function directly instead of calling decorator
|
||||
if callable(name):
|
||||
@@ -813,9 +825,11 @@ class MCPServer(Generic[LifespanResultT]):
|
||||
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
|
||||
|
||||
@@ -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/")
|
||||
|
||||
@@ -27,7 +27,7 @@ Example usage:
|
||||
# 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
|
||||
|
||||
@@ -4,8 +4,8 @@ This module provides functionality for creating an stdio-based transport layer
|
||||
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
|
||||
@@ -14,7 +14,7 @@ Example usage:
|
||||
await server.run(read_stream, write_stream, init_options)
|
||||
|
||||
anyio.run(run_server)
|
||||
```
|
||||
```
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -65,6 +65,7 @@ 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",
|
||||
@@ -72,6 +73,7 @@ class UrlElicitationRequiredError(MCPError):
|
||||
elicitation_id="auth-001"
|
||||
)
|
||||
])
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(self, elicitations: list[ElicitRequestURLParams], message: str | None = None):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,6 +25,7 @@ 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)
|
||||
@@ -32,6 +33,7 @@ class ResponseRouter(Protocol):
|
||||
resolver.set_result(response)
|
||||
return True
|
||||
return False
|
||||
```
|
||||
"""
|
||||
|
||||
def route_response(self, request_id: RequestId, response: dict[str, Any]) -> bool:
|
||||
|
||||
@@ -60,8 +60,10 @@ class RequestResponder(Generic[ReceiveRequestT, SendResultT]):
|
||||
cancellation handling:
|
||||
|
||||
Example:
|
||||
```python
|
||||
with request_responder as resp:
|
||||
await resp.respond(result)
|
||||
```
|
||||
|
||||
The context manager ensures:
|
||||
1. Proper cancellation scope setup and cleanup
|
||||
|
||||
@@ -77,7 +77,8 @@ class StreamSpyCollection:
|
||||
def stream_spy() -> Generator[Callable[[], StreamSpyCollection], None, None]:
|
||||
"""Fixture that provides spies for both client and server write streams.
|
||||
|
||||
Example usage:
|
||||
Example:
|
||||
```python
|
||||
async def test_something(stream_spy):
|
||||
# ... set up server and client ...
|
||||
|
||||
@@ -92,6 +93,7 @@ def stream_spy() -> Generator[Callable[[], StreamSpyCollection], None, None]:
|
||||
|
||||
# Clear for the next operation
|
||||
spies.clear()
|
||||
```
|
||||
"""
|
||||
client_spy = None
|
||||
server_spy = None
|
||||
|
||||
Reference in New Issue
Block a user