diff --git a/src/mcp/client/experimental/task_handlers.py b/src/mcp/client/experimental/task_handlers.py index 28ff2b1f2..0ab513236 100644 --- a/src/mcp/client/experimental/task_handlers.py +++ b/src/mcp/client/experimental/task_handlers.py @@ -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 diff --git a/src/mcp/client/experimental/tasks.py b/src/mcp/client/experimental/tasks.py index 2e2fdf735..a566df766 100644 --- a/src/mcp/client/experimental/tasks.py +++ b/src/mcp/client/experimental/tasks.py @@ -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 @@ -21,6 +22,7 @@ # Cancel a task await session.experimental.cancel_task(task_id) + ``` """ from collections.abc import AsyncIterator @@ -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"} @@ -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( @@ -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": @@ -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 diff --git a/src/mcp/client/session.py b/src/mcp/client/session.py index 0687f98c3..a0ca751bd 100644 --- a/src/mcp/client/session.py +++ b/src/mcp/client/session.py @@ -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) diff --git a/src/mcp/client/session_group.py b/src/mcp/client/session_group.py index 17f41025b..961021264 100644 --- a/src/mcp/client/session_group.py +++ b/src/mcp/client/session_group.py @@ -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): diff --git a/src/mcp/server/experimental/request_context.py b/src/mcp/server/experimental/request_context.py index 138c02110..3eba65822 100644 --- a/src/mcp/server/experimental/request_context.py +++ b/src/mcp/server/experimental/request_context.py @@ -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( @@ -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. """ diff --git a/src/mcp/server/experimental/task_context.py b/src/mcp/server/experimental/task_context.py index 9b626c986..1fc45badf 100644 --- a/src/mcp/server/experimental/task_context.py +++ b/src/mcp/server/experimental/task_context.py @@ -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 @@ async def my_task_work(task: ServerTaskContext) -> CallToolResult: return CallToolResult(content=[TextContent(text="Done!")]) else: return CallToolResult(content=[TextContent(text="Cancelled")]) + ``` """ def __init__( diff --git a/src/mcp/server/experimental/task_support.py b/src/mcp/server/experimental/task_support.py index 23b5d9cc8..b54219504 100644 --- a/src/mcp/server/experimental/task_support.py +++ b/src/mcp/server/experimental/task_support.py @@ -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 diff --git a/src/mcp/server/lowlevel/experimental.py b/src/mcp/server/lowlevel/experimental.py index 8ac268728..5a907b640 100644 --- a/src/mcp/server/lowlevel/experimental.py +++ b/src/mcp/server/lowlevel/experimental.py @@ -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. """ diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index 21b6af7b9..9c7105a7b 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -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): @@ -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: @@ -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!" @@ -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): @@ -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) @@ -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): @@ -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 diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index 827ec3591..9007230ce 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -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 @@ 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 diff --git a/src/mcp/server/stdio.py b/src/mcp/server/stdio.py index 864d387bd..e526bab56 100644 --- a/src/mcp/server/stdio.py +++ b/src/mcp/server/stdio.py @@ -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 @@ -14,7 +14,7 @@ async def run_server(): await server.run(read_stream, write_stream, init_options) anyio.run(run_server) -``` + ``` """ import sys diff --git a/src/mcp/shared/_httpx_utils.py b/src/mcp/shared/_httpx_utils.py index 8cf7bda2a..251469eaa 100644 --- a/src/mcp/shared/_httpx_utils.py +++ b/src/mcp/shared/_httpx_utils.py @@ -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} diff --git a/src/mcp/shared/exceptions.py b/src/mcp/shared/exceptions.py index 6c3a7745c..f153ea319 100644 --- a/src/mcp/shared/exceptions.py +++ b/src/mcp/shared/exceptions.py @@ -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): diff --git a/src/mcp/shared/experimental/tasks/helpers.py b/src/mcp/shared/experimental/tasks/helpers.py index bd1781cb5..3f91cd0d0 100644 --- a/src/mcp/shared/experimental/tasks/helpers.py +++ b/src/mcp/shared/experimental/tasks/helpers.py @@ -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: diff --git a/src/mcp/shared/metadata_utils.py b/src/mcp/shared/metadata_utils.py index 3b6b27dfe..6e4d33da0 100644 --- a/src/mcp/shared/metadata_utils.py +++ b/src/mcp/shared/metadata_utils.py @@ -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 diff --git a/src/mcp/shared/response_router.py b/src/mcp/shared/response_router.py index 7ec4a443c..fe24b016f 100644 --- a/src/mcp/shared/response_router.py +++ b/src/mcp/shared/response_router.py @@ -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 @@ def route_response(self, request_id, response): resolver.set_result(response) return True return False + ``` """ def route_response(self, request_id: RequestId, response: dict[str, Any]) -> bool: diff --git a/src/mcp/shared/session.py b/src/mcp/shared/session.py index d3f36ded4..b617d702f 100644 --- a/src/mcp/shared/session.py +++ b/src/mcp/shared/session.py @@ -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 diff --git a/tests/client/conftest.py b/tests/client/conftest.py index 268e968aa..2e39f1363 100644 --- a/tests/client/conftest.py +++ b/tests/client/conftest.py @@ -77,7 +77,8 @@ def get_server_notifications(self, method: str | None = None) -> list[JSONRPCNot 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 @@ async def test_something(stream_spy): # Clear for the next operation spies.clear() + ``` """ client_spy = None server_spy = None