From 8ef5ac8e7d9a03ca96760a6cbaf0a4c67380dcda Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Wed, 18 Feb 2026 20:52:42 -0600 Subject: [PATCH] docs: fix docstrings across public API surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix typos (e.g., "respose", "loosing", "ommited", "beging"), grammar issues (article agreement, capitalization of "Pydantic"), and formatting (missing periods, PEP 257 blank lines after summary, erroneous literal `\n` in `cli.py` docstrings). Correct several inaccuracies in docstring content: - Update `JSONRPCMessage` references to `SessionMessage` in websocket docs - Fix `Returns` → `Yields` for `create_client_server_memory_streams()` - Fix return type description in `handle_protected_resource_response()` - Use `async def` / `await` in `Context` and `MCPServer` examples - Fix `requestedSchema` → `requested_schema` in example code - Fix `sampling/create_message` → `sampling/createMessage` - Fix `async get_data()` → `async def get_data()` syntax in example - Add missing parameter docs (`httpx_client_factory`, `icons`, `meta`, etc.) --- src/mcp/cli/cli.py | 8 +-- .../auth/extensions/client_credentials.py | 2 +- src/mcp/client/auth/oauth2.py | 3 +- src/mcp/client/auth/utils.py | 20 +++---- src/mcp/client/client.py | 8 +-- src/mcp/client/experimental/tasks.py | 4 +- src/mcp/client/session_group.py | 10 ++-- src/mcp/client/sse.py | 1 + src/mcp/client/stdio.py | 6 +- src/mcp/client/streamable_http.py | 2 +- src/mcp/client/websocket.py | 4 +- src/mcp/os/win32/utilities.py | 15 ++--- src/mcp/server/auth/handlers/revoke.py | 2 +- src/mcp/server/auth/middleware/client_auth.py | 8 ++- src/mcp/server/auth/provider.py | 17 +++--- src/mcp/server/auth/routes.py | 6 +- src/mcp/server/elicitation.py | 4 +- .../server/experimental/request_context.py | 8 +-- src/mcp/server/lowlevel/server.py | 3 - src/mcp/server/mcpserver/resources/types.py | 2 +- src/mcp/server/mcpserver/server.py | 56 +++++++++++-------- .../mcpserver/utilities/func_metadata.py | 30 +++++----- src/mcp/server/mcpserver/utilities/logging.py | 6 +- src/mcp/server/models.py | 2 +- src/mcp/server/session.py | 28 +++++----- src/mcp/server/sse.py | 8 +-- src/mcp/server/streamable_http.py | 15 ++--- src/mcp/server/websocket.py | 4 +- src/mcp/shared/auth.py | 7 +-- src/mcp/shared/memory.py | 2 +- src/mcp/shared/metadata_utils.py | 2 +- src/mcp/shared/session.py | 5 +- src/mcp/types/_types.py | 42 +++++++------- 33 files changed, 177 insertions(+), 163 deletions(-) diff --git a/src/mcp/cli/cli.py b/src/mcp/cli/cli.py index 858ab7db2..62334a4a2 100644 --- a/src/mcp/cli/cli.py +++ b/src/mcp/cli/cli.py @@ -317,12 +317,12 @@ def run( ) -> None: # pragma: no cover """Run an MCP server. - The server can be specified in two ways:\n - 1. Module approach: server.py - runs the module directly, expecting a server.run() call.\n - 2. Import approach: server.py:app - imports and runs the specified server object.\n\n + The server can be specified in two ways: + 1. Module approach: server.py - runs the module directly, expecting a server.run() call. + 2. Import approach: server.py:app - imports and runs the specified server object. Note: This command runs the server directly. You are responsible for ensuring - all dependencies are available.\n + all dependencies are available. For dependency management, use `mcp install` or `mcp dev` instead. """ # noqa: E501 file, server_object = _parse_file_path(file_spec) diff --git a/src/mcp/client/auth/extensions/client_credentials.py b/src/mcp/client/auth/extensions/client_credentials.py index 07f6180bf..cb6dafb40 100644 --- a/src/mcp/client/auth/extensions/client_credentials.py +++ b/src/mcp/client/auth/extensions/client_credentials.py @@ -450,7 +450,7 @@ def _add_client_authentication_jwt(self, *, token_data: dict[str, Any]): # prag # When using private_key_jwt, in a client_credentials flow, we use RFC 7523 Section 2.2 token_data["client_assertion"] = assertion token_data["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" - # We need to set the audience to the resource server, the audience is difference from the one in claims + # We need to set the audience to the resource server, the audience is different from the one in claims # it represents the resource server that will validate the token token_data["audience"] = self.context.get_resource_url() diff --git a/src/mcp/client/auth/oauth2.py b/src/mcp/client/auth/oauth2.py index f46407754..7f5af5186 100644 --- a/src/mcp/client/auth/oauth2.py +++ b/src/mcp/client/auth/oauth2.py @@ -215,6 +215,7 @@ def prepare_token_auth( class OAuthClientProvider(httpx.Auth): """OAuth2 authentication for httpx. + Handles OAuth flow with automatic client registration and token storage. """ @@ -241,7 +242,7 @@ def __init__( callback_handler: Handler for authorization callbacks. timeout: Timeout for the OAuth flow. client_metadata_url: URL-based client ID. When provided and the server - advertises client_id_metadata_document_supported=true, this URL will be + advertises client_id_metadata_document_supported=True, this URL will be used as the client_id instead of performing dynamic client registration. Must be a valid HTTPS URL with a non-root pathname. validate_resource_url: Optional callback to override resource URL validation. diff --git a/src/mcp/client/auth/utils.py b/src/mcp/client/auth/utils.py index 1aa960b9c..0ca36b98d 100644 --- a/src/mcp/client/auth/utils.py +++ b/src/mcp/client/auth/utils.py @@ -38,7 +38,7 @@ def extract_field_from_www_auth(response: Response, field_name: str) -> str | No def extract_scope_from_www_auth(response: Response) -> str | None: - """Extract scope parameter from WWW-Authenticate header as per RFC6750. + """Extract scope parameter from WWW-Authenticate header as per RFC 6750. Returns: Scope string if found in WWW-Authenticate header, None otherwise @@ -47,7 +47,7 @@ def extract_scope_from_www_auth(response: Response) -> str | None: def extract_resource_metadata_from_www_auth(response: Response) -> str | None: - """Extract protected resource metadata URL from WWW-Authenticate header as per RFC9728. + """Extract protected resource metadata URL from WWW-Authenticate header as per RFC 9728. Returns: Resource metadata URL if found in WWW-Authenticate header, None otherwise @@ -67,8 +67,8 @@ def build_protected_resource_metadata_discovery_urls(www_auth_url: str | None, s 3. Fall back to root-based well-known URI: /.well-known/oauth-protected-resource Args: - www_auth_url: optional resource_metadata url extracted from the WWW-Authenticate header - server_url: server url + www_auth_url: Optional resource_metadata URL extracted from the WWW-Authenticate header + server_url: Server URL Returns: Ordered list of URLs to try for discovery @@ -120,10 +120,10 @@ def get_client_metadata_scopes( def build_oauth_authorization_server_metadata_discovery_urls(auth_server_url: str | None, server_url: str) -> list[str]: - """Generate ordered list of (url, type) tuples for discovery attempts. + """Generate an ordered list of URLs for authorization server metadata discovery. Args: - auth_server_url: URL for the OAuth Authorization Metadata URL if found, otherwise None + auth_server_url: OAuth Authorization Server Metadata URL if found, otherwise None server_url: URL for the MCP server, used as a fallback if auth_server_url is None """ @@ -170,7 +170,7 @@ async def handle_protected_resource_response( Per SEP-985, supports fallback when discovery fails at one URL. Returns: - True if metadata was successfully discovered, False if we should try next URL + ProtectedResourceMetadata if successfully discovered, None if we should try next URL """ if response.status_code == 200: try: @@ -206,7 +206,7 @@ def create_oauth_metadata_request(url: str) -> Request: def create_client_registration_request( auth_server_metadata: OAuthMetadata | None, client_metadata: OAuthClientMetadata, auth_base_url: str ) -> Request: - """Build registration request or skip if already registered.""" + """Build a client registration request.""" if auth_server_metadata and auth_server_metadata.registration_endpoint: registration_url = str(auth_server_metadata.registration_endpoint) @@ -261,7 +261,7 @@ def should_use_client_metadata_url( """Determine if URL-based client ID (CIMD) should be used instead of DCR. URL-based client IDs should be used when: - 1. The server advertises client_id_metadata_document_supported=true + 1. The server advertises client_id_metadata_document_supported=True 2. The client has a valid client_metadata_url configured Args: @@ -306,7 +306,7 @@ def create_client_info_from_metadata_url( async def handle_token_response_scopes( response: Response, ) -> OAuthToken: - """Parse and validate token response with optional scope validation. + """Parse and validate a token response. Parses token response JSON. Callers should check response.status_code before calling. diff --git a/src/mcp/client/client.py b/src/mcp/client/client.py index 29d4a7035..7dc67c584 100644 --- a/src/mcp/client/client.py +++ b/src/mcp/client/client.py @@ -37,8 +37,8 @@ class Client: """A high-level MCP client for connecting to MCP servers. - Currently supports in-memory transport for testing. Pass a Server or - MCPServer instance directly to the constructor. + Supports in-memory transport for testing (pass a Server or MCPServer instance), + Streamable HTTP transport (pass a URL string), or a custom Transport instance. Example: ```python @@ -205,7 +205,7 @@ async def read_resource(self, uri: str, *, meta: RequestParamsMeta | None = None Args: uri: The URI of the resource to read. - meta: Additional metadata for the request + meta: Additional metadata for the request. Returns: The resource content. @@ -239,7 +239,7 @@ async def call_tool( meta: Additional metadata for the request Returns: - The tool result + The tool result. """ return await self.session.call_tool( name=name, diff --git a/src/mcp/client/experimental/tasks.py b/src/mcp/client/experimental/tasks.py index 8ddc4face..2e2fdf735 100644 --- a/src/mcp/client/experimental/tasks.py +++ b/src/mcp/client/experimental/tasks.py @@ -83,7 +83,7 @@ async def call_tool_as_task( status = await session.experimental.get_task(task_id) if status.status == "completed": break - await asyncio.sleep(0.5) + await anyio.sleep(0.5) # Get result final = await session.experimental.get_task_result(task_id, CallToolResult) @@ -177,7 +177,7 @@ async def poll_task(self, task_id: str) -> AsyncIterator[types.GetTaskResult]: """Poll a task until it reaches a terminal status. Yields GetTaskResult for each poll, allowing the caller to react to - status changes (e.g., handle input_required). Exits when task reaches + status changes (e.g., handle input_required). Exits when the task reaches a terminal status (completed, failed, cancelled). Respects the pollInterval hint from the server. diff --git a/src/mcp/client/session_group.py b/src/mcp/client/session_group.py index f4e6293b7..17f41025b 100644 --- a/src/mcp/client/session_group.py +++ b/src/mcp/client/session_group.py @@ -3,7 +3,7 @@ Tools, resources, and prompts are aggregated across servers. Servers may be connected to or disconnected from at any point after initialization. -This abstractions can handle naming collisions using a custom user-provided hook. +This abstraction can handle naming collisions using a custom user-provided hook. """ import contextlib @@ -30,7 +30,7 @@ class SseServerParameters(BaseModel): - """Parameters for initializing a sse_client.""" + """Parameters for initializing an sse_client.""" # The endpoint URL. url: str @@ -67,8 +67,8 @@ class StreamableHttpParameters(BaseModel): ServerParameters: TypeAlias = StdioServerParameters | SseServerParameters | StreamableHttpParameters -# Use dataclass instead of pydantic BaseModel -# because pydantic BaseModel cannot handle Protocol fields. +# Use dataclass instead of Pydantic BaseModel +# because Pydantic BaseModel cannot handle Protocol fields. @dataclass class ClientSessionParameters: """Parameters for establishing a client session to an MCP server.""" @@ -119,7 +119,7 @@ class _ComponentNames(BaseModel): _session_exit_stacks: dict[mcp.ClientSession, contextlib.AsyncExitStack] # Optional fn consuming (component_name, server_info) for custom names. - # This is provide a means to mitigate naming conflicts across servers. + # This is to provide a means to mitigate naming conflicts across servers. # Example: (tool_name, server_info) => "{result.server_info.name}.{tool_name}" _ComponentNameHook: TypeAlias = Callable[[str, types.Implementation], str] _component_name_hook: _ComponentNameHook | None diff --git a/src/mcp/client/sse.py b/src/mcp/client/sse.py index 7c309ecb5..61026aa0c 100644 --- a/src/mcp/client/sse.py +++ b/src/mcp/client/sse.py @@ -47,6 +47,7 @@ async def sse_client( headers: Optional headers to include in requests. timeout: HTTP timeout for regular operations (in seconds). sse_read_timeout: Timeout for SSE read operations (in seconds). + httpx_client_factory: Factory function for creating the HTTPX client. auth: Optional HTTPX authentication handler. on_session_created: Optional callback invoked with the session ID when received. """ diff --git a/src/mcp/client/stdio.py b/src/mcp/client/stdio.py index 5b8209eeb..902dc8576 100644 --- a/src/mcp/client/stdio.py +++ b/src/mcp/client/stdio.py @@ -87,9 +87,9 @@ class StdioServerParameters(BaseModel): encoding: str = "utf-8" """ - The text encoding used when sending/receiving messages to the server + The text encoding used when sending/receiving messages to the server. - defaults to utf-8 + Defaults to utf-8. """ encoding_error_handler: Literal["strict", "ignore", "replace"] = "strict" @@ -97,7 +97,7 @@ class StdioServerParameters(BaseModel): The text encoding error handler. See https://docs.python.org/3/library/codecs.html#codec-base-classes for - explanations of possible values + explanations of possible values. """ diff --git a/src/mcp/client/streamable_http.py b/src/mcp/client/streamable_http.py index d161e3c2a..9f3dd5e0b 100644 --- a/src/mcp/client/streamable_http.py +++ b/src/mcp/client/streamable_http.py @@ -358,7 +358,7 @@ async def _handle_sse_response( resumption_callback=(ctx.metadata.on_resumption_token_update if ctx.metadata else None), is_initialization=is_initialization, ) - # If the SSE event indicates completion, like returning respose/error + # If the SSE event indicates completion, like returning response/error # break the loop if is_complete: await response.aclose() diff --git a/src/mcp/client/websocket.py b/src/mcp/client/websocket.py index bda199f36..79e75fad1 100644 --- a/src/mcp/client/websocket.py +++ b/src/mcp/client/websocket.py @@ -25,8 +25,8 @@ async def websocket_client( (read_stream, write_stream) - read_stream: As you read from this stream, you'll receive either valid - JSONRPCMessage objects or Exception objects (when validation fails). - - write_stream: Write JSONRPCMessage objects to this stream to send them + SessionMessage objects or Exception objects (when validation fails). + - write_stream: Write SessionMessage objects to this stream to send them over the WebSocket to the server. """ diff --git a/src/mcp/os/win32/utilities.py b/src/mcp/os/win32/utilities.py index fa4e4b399..0e188691f 100644 --- a/src/mcp/os/win32/utilities.py +++ b/src/mcp/os/win32/utilities.py @@ -138,9 +138,9 @@ async def create_windows_process( ) -> Process | FallbackProcess: """Creates a subprocess in a Windows-compatible way with Job Object support. - Attempt to use anyio's open_process for async subprocess creation. - In some cases this will throw NotImplementedError on Windows, e.g. - when using the SelectorEventLoop which does not support async subprocesses. + Attempts to use anyio's open_process for async subprocess creation. + In some cases this will throw NotImplementedError on Windows, e.g., + when using the SelectorEventLoop, which does not support async subprocesses. In that case, we fall back to using subprocess.Popen. The process is automatically added to a Job Object to ensure all child @@ -242,8 +242,9 @@ def _create_job_object() -> int | None: def _maybe_assign_process_to_job(process: Process | FallbackProcess, job: JobHandle | None) -> None: - """Try to assign a process to a job object. If assignment fails - for any reason, the job handle is closed. + """Try to assign a process to a job object. + + If assignment fails for any reason, the job handle is closed. """ if not job: return @@ -312,8 +313,8 @@ async def terminate_windows_process(process: Process | FallbackProcess): Note: On Windows, terminating a process with process.terminate() doesn't always guarantee immediate process termination. - So we give it 2s to exit, or we call process.kill() - which sends a SIGKILL equivalent signal. + If the process does not exit within 2 seconds, process.kill() is called + to send a SIGKILL-equivalent signal. Args: process: The process to terminate diff --git a/src/mcp/server/auth/handlers/revoke.py b/src/mcp/server/auth/handlers/revoke.py index 68a3392b4..4efd15400 100644 --- a/src/mcp/server/auth/handlers/revoke.py +++ b/src/mcp/server/auth/handlers/revoke.py @@ -15,7 +15,7 @@ class RevocationRequest(BaseModel): - """# See https://datatracker.ietf.org/doc/html/rfc7009#section-2.1""" + """See https://datatracker.ietf.org/doc/html/rfc7009#section-2.1""" token: str token_type_hint: Literal["access_token", "refresh_token"] | None = None diff --git a/src/mcp/server/auth/middleware/client_auth.py b/src/mcp/server/auth/middleware/client_auth.py index 8a6a1b518..2832f8352 100644 --- a/src/mcp/server/auth/middleware/client_auth.py +++ b/src/mcp/server/auth/middleware/client_auth.py @@ -19,15 +19,17 @@ def __init__(self, message: str): class ClientAuthenticator: """ClientAuthenticator is a callable which validates requests from a client application, used to verify /token calls. + If, during registration, the client requested to be issued a secret, the authenticator asserts that /token calls must be authenticated with - that same token. + that same secret. + NOTE: clients can opt for no authentication during registration, in which case this logic is skipped. """ def __init__(self, provider: OAuthAuthorizationServerProvider[Any, Any, Any]): - """Initialize the dependency. + """Initialize the authenticator. Args: provider: Provider to look up client information @@ -83,7 +85,7 @@ async def authenticate_request(self, request: Request) -> OAuthClientInformation elif client.token_endpoint_auth_method == "client_secret_post": raw_form_data = form_data.get("client_secret") - # form_data.get() can return a UploadFile or None, so we need to check if it's a string + # form_data.get() can return an UploadFile or None, so we need to check if it's a string if isinstance(raw_form_data, str): request_client_secret = str(raw_form_data) diff --git a/src/mcp/server/auth/provider.py b/src/mcp/server/auth/provider.py index 5eb577fd4..957082a85 100644 --- a/src/mcp/server/auth/provider.py +++ b/src/mcp/server/auth/provider.py @@ -131,8 +131,9 @@ async def register_client(self, client_info: OAuthClientInformationFull) -> None """ async def authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str: - """Called as part of the /authorize endpoint, and returns a URL that the client + """Handle the /authorize endpoint and return a URL that the client will be redirected to. + Many MCP implementations will redirect to a third-party provider to perform a second OAuth exchange with that provider. In this sort of setup, the client has an OAuth connection with the MCP server, and the MCP server has an OAuth @@ -151,7 +152,7 @@ async def authorize(self, client: OAuthClientInformationFull, params: Authorizat | | +------------+ - Implementations will need to define another handler on the MCP server return + Implementations will need to define another handler on the MCP server's return flow to perform the second redirect, and generate and store an authorization code as part of completing the OAuth authorization step. @@ -182,7 +183,7 @@ async def load_authorization_code( authorization_code: The authorization code to get the challenge for. Returns: - The AuthorizationCode, or None if not found + The AuthorizationCode, or None if not found. """ ... @@ -199,7 +200,7 @@ async def exchange_authorization_code( The OAuth token, containing access and refresh tokens. Raises: - TokenError: If the request is invalid + TokenError: If the request is invalid. """ ... @@ -234,18 +235,18 @@ async def exchange_refresh_token( The OAuth token, containing access and refresh tokens. Raises: - TokenError: If the request is invalid + TokenError: If the request is invalid. """ ... async def load_access_token(self, token: str) -> AccessTokenT | None: - """Loads an access token by its token. + """Loads an access token by its token string. Args: token: The access token to verify. Returns: - The AuthInfo, or None if the token is invalid. + The access token, or None if the token is invalid. """ async def revoke_token( @@ -261,7 +262,7 @@ async def revoke_token( provided. Args: - token: the token to revoke + token: The token to revoke. """ diff --git a/src/mcp/server/auth/routes.py b/src/mcp/server/auth/routes.py index 08f735f36..f922d41df 100644 --- a/src/mcp/server/auth/routes.py +++ b/src/mcp/server/auth/routes.py @@ -25,10 +25,10 @@ def validate_issuer_url(url: AnyHttpUrl): """Validate that the issuer URL meets OAuth 2.0 requirements. Args: - url: The issuer URL to validate + url: The issuer URL to validate. Raises: - ValueError: If the issuer URL is invalid + ValueError: If the issuer URL is invalid. """ # RFC 8414 requires HTTPS, but we allow localhost HTTP for testing @@ -217,6 +217,8 @@ def create_protected_resource_routes( resource_url: The URL of this resource server authorization_servers: List of authorization servers that can issue tokens scopes_supported: Optional list of scopes supported by this resource + resource_name: Optional human-readable name for this resource + resource_documentation: Optional URL to documentation for this resource Returns: List of Starlette routes for protected resource metadata diff --git a/src/mcp/server/elicitation.py b/src/mcp/server/elicitation.py index 58e9fe448..731c914ed 100644 --- a/src/mcp/server/elicitation.py +++ b/src/mcp/server/elicitation.py @@ -112,8 +112,8 @@ async def elicit_with_validation( This method can be used to interactively ask for additional information from the client within a tool's execution. The client might display the message to the - user and collect a response according to the provided schema. Or in case a - client is an agent, it might decide how to handle the elicitation -- either by asking + user and collect a response according to the provided schema. If the client + is an agent, it might decide how to handle the elicitation -- either by asking the user or automatically generating a response. For sensitive data like credentials or OAuth flows, use elicit_url() instead. diff --git a/src/mcp/server/experimental/request_context.py b/src/mcp/server/experimental/request_context.py index 91aa9a645..138c02110 100644 --- a/src/mcp/server/experimental/request_context.py +++ b/src/mcp/server/experimental/request_context.py @@ -62,8 +62,8 @@ def validate_task_mode( """Validate that the request is compatible with the tool's task execution mode. Per MCP spec: - - "required": Clients MUST invoke as task. Server returns -32601 if not. - - "forbidden" (or None): Clients MUST NOT invoke as task. Server returns -32601 if they do. + - "required": Clients MUST invoke as a task. Server returns -32601 if not. + - "forbidden" (or None): Clients MUST NOT invoke as a task. Server returns -32601 if they do. - "optional": Either is acceptable. Args: @@ -111,7 +111,7 @@ def can_use_tool(self, tool_task_mode: TaskExecutionMode | None) -> bool: """Check if this client can use a tool with the given task mode. Useful for filtering tool lists or providing warnings. - Returns False if tool requires "required" but client doesn't support tasks. + Returns False if the tool's task mode is "required" but the client doesn't support tasks. Args: tool_task_mode: The tool's execution.taskSupport value @@ -164,7 +164,7 @@ async def handle_tool(ctx: RequestContext, params: CallToolRequestParams) -> Cal async def work(task: ServerTaskContext) -> CallToolResult: result = await task.elicit( message="Are you sure?", - requestedSchema={"type": "object", ...} + requested_schema={"type": "object", ...} ) confirmed = result.content.get("confirm", False) return CallToolResult(content=[TextContent(text="Done" if confirmed else "Cancelled")]) diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py index 9ca5ac4fc..aee644040 100644 --- a/src/mcp/server/lowlevel/server.py +++ b/src/mcp/server/lowlevel/server.py @@ -88,9 +88,6 @@ def __init__(self, prompts_changed: bool = False, resources_changed: bool = Fals async def lifespan(_: Server[LifespanResultT]) -> AsyncIterator[dict[str, Any]]: """Default lifespan context manager that does nothing. - Args: - server: The server instance this lifespan is managing - Returns: An empty context object """ diff --git a/src/mcp/server/mcpserver/resources/types.py b/src/mcp/server/mcpserver/resources/types.py index 64e633806..42aecd6e3 100644 --- a/src/mcp/server/mcpserver/resources/types.py +++ b/src/mcp/server/mcpserver/resources/types.py @@ -109,7 +109,7 @@ def from_function( class FileResource(Resource): """A resource that reads from a file. - Set is_binary=True to read file as binary data instead of text. + Set is_binary=True to read the file as binary data instead of text. """ path: Path = Field(description="Path to the file") diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index cd459589a..21b6af7b9 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -108,7 +108,7 @@ class Settings(BaseSettings, Generic[LifespanResultT]): warn_on_duplicate_prompts: bool lifespan: Callable[[MCPServer[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None - """A async context manager that will be called when the server is started.""" + """An async context manager that will be called when the server is started.""" auth: AuthSettings | None @@ -388,8 +388,10 @@ async def list_tools(self) -> list[MCPTool]: ] def get_context(self) -> Context[LifespanResultT, Request]: - """Returns a Context object. Note that the context will only be valid - during a request; outside a request, most methods will error. + """Return a Context object. + + Note that the context will only be valid during a request; outside a + request, most methods will error. """ try: request_context = request_ctx.get() @@ -475,6 +477,8 @@ def add_tool( title: Optional human-readable title for the tool description: Optional description of what the tool does annotations: Optional ToolAnnotations providing additional tool information + icons: Optional list of icons for the tool + meta: Optional metadata dictionary for the tool structured_output: Controls whether the tool's output is structured or unstructured - If None, auto-detects based on the function's return type annotation - If True, creates a structured tool (return type annotation permitting) @@ -523,6 +527,8 @@ def tool( title: Optional human-readable title for the tool description: Optional description of what the tool does annotations: Optional ToolAnnotations providing additional tool information + icons: Optional list of icons for the tool + meta: Optional metadata dictionary for the tool structured_output: Controls whether the tool's output is structured or unstructured - If None, auto-detects based on the function's return type annotation - If True, creates a structured tool (return type annotation permitting) @@ -534,8 +540,8 @@ def my_tool(x: int) -> str: return str(x) @server.tool() - def tool_with_context(x: int, ctx: Context) -> str: - ctx.info(f"Processing {x}") + async def tool_with_context(x: int, ctx: Context) -> str: + await ctx.info(f"Processing {x}") return str(x) @server.tool() @@ -636,6 +642,8 @@ def resource( title: Optional human-readable title for the resource description: Optional description of the resource mime_type: Optional MIME type for the resource + icons: Optional list of icons for the resource + annotations: Optional annotations for the resource meta: Optional metadata dictionary for the resource Example: @@ -644,7 +652,7 @@ def get_data() -> str: return "Hello, world!" @server.resource("resource://my-resource") - async get_data() -> str: + async def get_data() -> str: data = await fetch_data() return f"Hello, world! {data}" @@ -736,6 +744,7 @@ def prompt( name: Optional name for the prompt (defaults to function name) title: Optional human-readable title for the prompt description: Optional description of what the prompt does + icons: Optional list of icons for the prompt Example: @server.prompt() @@ -1092,18 +1101,18 @@ class Context(BaseModel, Generic[LifespanContextT, RequestT]): ```python @server.tool() - def my_tool(x: int, ctx: Context) -> str: + async def my_tool(x: int, ctx: Context) -> str: # Log messages to the client - ctx.info(f"Processing {x}") - ctx.debug("Debug info") - ctx.warning("Warning message") - ctx.error("Error message") + await ctx.info(f"Processing {x}") + await ctx.debug("Debug info") + await ctx.warning("Warning message") + await ctx.error("Error message") # Report progress - ctx.report_progress(50, 100) + await ctx.report_progress(50, 100) # Access resources - data = ctx.read_resource("resource://data") + data = await ctx.read_resource("resource://data") # Get request info request_id = ctx.request_id @@ -1149,9 +1158,9 @@ async def report_progress(self, progress: float, total: float | None = None, mes """Report progress for the current operation. Args: - progress: Current progress value e.g. 24 - total: Optional total value e.g. 100 - message: Optional message e.g. Starting render... + progress: Current progress value (e.g., 24) + total: Optional total value (e.g., 100) + message: Optional message (e.g., "Starting render...") """ progress_token = self.request_context.meta.get("progress_token") if self.request_context.meta else None @@ -1187,15 +1196,14 @@ async def elicit( This method can be used to interactively ask for additional information from the client within a tool's execution. The client might display the message to the - user and collect a response according to the provided schema. Or in case a - client is an agent, it might decide how to handle the elicitation -- either by asking + user and collect a response according to the provided schema. If the client + is an agent, it might decide how to handle the elicitation -- either by asking the user or automatically generating a response. Args: - schema: A Pydantic model class defining the expected response structure, according to the specification, - only primitive types are allowed. - message: Optional message to present to the user. If not provided, will use - a default message based on the schema + message: Message to present to the user + schema: A Pydantic model class defining the expected response structure. + According to the specification, only primitive types are allowed. Returns: An ElicitationResult containing the action taken and the data if accepted @@ -1229,7 +1237,7 @@ async def elicit_url( The response indicates whether the user consented to navigate to the URL. The actual interaction happens out-of-band. When the elicitation completes, - call `self.session.send_elicit_complete(elicitation_id)` to notify the client. + call `ctx.session.send_elicit_complete(elicitation_id)` to notify the client. Args: message: Human-readable explanation of why the interaction is needed @@ -1299,7 +1307,7 @@ async def close_sse_stream(self) -> None: be replayed when the client reconnects with Last-Event-ID. Use this to implement polling behavior during long-running operations - - client will reconnect after the retry interval specified in the priming event. + the client will reconnect after the retry interval specified in the priming event. Note: This is a no-op if not using StreamableHTTP transport with event_store. diff --git a/src/mcp/server/mcpserver/utilities/func_metadata.py b/src/mcp/server/mcpserver/utilities/func_metadata.py index 4b539ce1f..062b47d0f 100644 --- a/src/mcp/server/mcpserver/utilities/func_metadata.py +++ b/src/mcp/server/mcpserver/utilities/func_metadata.py @@ -46,7 +46,7 @@ class ArgModelBase(BaseModel): def model_dump_one_level(self) -> dict[str, Any]: """Return a dict of the model's fields, one level deep. - That is, sub-models etc are not dumped - they are kept as pydantic models. + That is, sub-models etc are not dumped - they are kept as Pydantic models. """ kwargs: dict[str, Any] = {} for field_name, field_info in self.__class__.model_fields.items(): @@ -89,8 +89,7 @@ async def call_fn_with_arg_validation( return await anyio.to_thread.run_sync(functools.partial(fn, **arguments_parsed_dict)) def convert_result(self, result: Any) -> Any: - """Convert the result of a function call to the appropriate format for - the lowlevel server tool call handler: + """Convert a function call result to the format for the lowlevel tool call handler. - If output_model is None, return the unstructured content directly. - If output_model is not None, convert the result to structured output format @@ -126,11 +125,11 @@ def convert_result(self, result: Any) -> Any: def pre_parse_json(self, data: dict[str, Any]) -> dict[str, Any]: """Pre-parse data from JSON. - Return a dict with same keys as input but with values parsed from JSON + Return a dict with the same keys as input but with values parsed from JSON if appropriate. This is to handle cases like `["a", "b", "c"]` being passed in as JSON inside - a string rather than an actual list. Claude desktop is prone to this - in fact + a string rather than an actual list. Claude Desktop is prone to this - in fact it seems incapable of NOT doing this. For sub-models, it tends to pass dicts (JSON objects) as JSON strings, which can be pre-parsed here. """ @@ -173,8 +172,7 @@ def func_metadata( skip_names: Sequence[str] = (), structured_output: bool | None = None, ) -> FuncMetadata: - """Given a function, return metadata including a pydantic model representing its - signature. + """Given a function, return metadata including a Pydantic model representing its signature. The use case for this is ``` @@ -183,11 +181,11 @@ def func_metadata( return func(**validated_args.model_dump_one_level()) ``` - **critically** it also provides pre-parse helper to attempt to parse things from + **critically** it also provides a pre-parse helper to attempt to parse things from JSON. Args: - func: The function to convert to a pydantic model + func: The function to convert to a Pydantic model skip_names: A list of parameter names to skip. These will not be included in the model. structured_output: Controls whether the tool's output is structured or unstructured @@ -195,8 +193,8 @@ def func_metadata( - If True, creates a structured tool (return type annotation permitting) - If False, unconditionally creates an unstructured tool - If structured, creates a Pydantic model for the function's result based on its annotation. - Supports various return types: + If structured, creates a Pydantic model for the function's result based on its annotation. + Supports various return types: - BaseModel subclasses (used directly) - Primitive types (str, int, float, bool, bytes, None) - wrapped in a model with a 'result' field @@ -206,9 +204,9 @@ def func_metadata( Returns: A FuncMetadata object containing: - - arg_model: A pydantic model representing the function's arguments - - output_model: A pydantic model for the return type if output is structured - - output_conversion: Records how function output should be converted before returning. + - arg_model: A Pydantic model representing the function's arguments + - output_model: A Pydantic model for the return type if the output is structured + - wrap_output: Whether the function result needs to be wrapped in `{"result": ...}` for structured output. """ try: sig = inspect.signature(func, eval_str=True) @@ -296,7 +294,7 @@ def func_metadata( ] # pragma: no cover else: # We only had `Annotated[CallToolResult, ReturnType]`, treat the original annotation - # as beging `ReturnType`: + # as being `ReturnType`: original_annotation = return_type_expr else: return FuncMetadata(arg_model=arguments_model) @@ -355,7 +353,7 @@ def _try_create_model_and_schema( if origin is dict: args = get_args(type_expr) if len(args) == 2 and args[0] is str: - # TODO: should we use the original annotation? We are loosing any potential `Annotated` + # TODO: should we use the original annotation? We are losing any potential `Annotated` # metadata for Pydantic here: model = _create_dict_model(func_name, type_expr) else: diff --git a/src/mcp/server/mcpserver/utilities/logging.py b/src/mcp/server/mcpserver/utilities/logging.py index c394f2bfa..04ca38853 100644 --- a/src/mcp/server/mcpserver/utilities/logging.py +++ b/src/mcp/server/mcpserver/utilities/logging.py @@ -8,10 +8,10 @@ def get_logger(name: str) -> logging.Logger: """Get a logger nested under MCP namespace. Args: - name: the name of the logger + name: The name of the logger. Returns: - a configured logger instance + A configured logger instance. """ return logging.getLogger(name) @@ -22,7 +22,7 @@ def configure_logging( """Configure logging for MCP. Args: - level: the log level to use + level: The log level to use. """ handlers: list[logging.Handler] = [] try: diff --git a/src/mcp/server/models.py b/src/mcp/server/models.py index 41b9224c1..3861f42a7 100644 --- a/src/mcp/server/models.py +++ b/src/mcp/server/models.py @@ -1,4 +1,4 @@ -"""This module provides simpler types to use with the server for managing prompts +"""This module provides simplified types to use with the server for managing prompts and tools. """ diff --git a/src/mcp/server/session.py b/src/mcp/server/session.py index 6925aa556..759d2131a 100644 --- a/src/mcp/server/session.py +++ b/src/mcp/server/session.py @@ -363,12 +363,12 @@ async def elicit( """Send a form mode elicitation/create request. Args: - message: The message to present to the user - requested_schema: Schema defining the expected response structure - related_request_id: Optional ID of the request that triggered this elicitation + message: The message to present to the user. + requested_schema: Schema defining the expected response structure. + related_request_id: Optional ID of the request that triggered this elicitation. Returns: - The client's response + The client's response. Note: This method is deprecated in favor of elicit_form(). It remains for @@ -385,12 +385,12 @@ async def elicit_form( """Send a form mode elicitation/create request. Args: - message: The message to present to the user - requested_schema: Schema defining the expected response structure - related_request_id: Optional ID of the request that triggered this elicitation + message: The message to present to the user. + requested_schema: Schema defining the expected response structure. + related_request_id: Optional ID of the request that triggered this elicitation. Returns: - The client's response with form data + The client's response with form data. Raises: StatelessModeNotSupported: If called in stateless HTTP mode. @@ -421,13 +421,13 @@ async def elicit_url( like OAuth flows, credential collection, or payment processing. Args: - message: Human-readable explanation of why the interaction is needed - url: The URL the user should navigate to - elicitation_id: Unique identifier for tracking this elicitation - related_request_id: Optional ID of the request that triggered this elicitation + message: Human-readable explanation of why the interaction is needed. + url: The URL the user should navigate to. + elicitation_id: Unique identifier for tracking this elicitation. + related_request_id: Optional ID of the request that triggered this elicitation. Returns: - The client's response indicating acceptance, decline, or cancellation + The client's response indicating acceptance, decline, or cancellation. Raises: StatelessModeNotSupported: If called in stateless HTTP mode. @@ -499,7 +499,7 @@ async def send_elicit_complete( Args: elicitation_id: The unique identifier of the completed elicitation - related_request_id: Optional ID of the request that triggered this + related_request_id: Optional ID of the request that triggered this notification """ await self.send_notification( types.ElicitCompleteNotification( diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index 674294c5c..827ec3591 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -29,8 +29,8 @@ async def handle_sse(request): 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 +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 an empty Response() after the SSE connection ends to fix this. See SseServerTransport class documentation for more details. @@ -61,8 +61,8 @@ async def handle_sse(request): class SseServerTransport: - """SSE server transport for MCP. This class provides _two_ ASGI applications, - suitable to be used with a framework like Starlette and a server like Hypercorn: + """SSE server transport for MCP. This class provides two ASGI applications, + suitable for use with a framework like Starlette and a server like Hypercorn: 1. connect_sse() is an ASGI application which receives incoming GET requests, and sets up a new SSE stream to send server messages to the client. diff --git a/src/mcp/server/streamable_http.py b/src/mcp/server/streamable_http.py index bcee3a474..04aed345e 100644 --- a/src/mcp/server/streamable_http.py +++ b/src/mcp/server/streamable_http.py @@ -89,7 +89,7 @@ async def store_event(self, stream_id: StreamId, message: JSONRPCMessage | None) message: The JSON-RPC message to store, or None for priming events Returns: - The generated event ID for the stored event + The generated event ID for the stored event. """ pass # pragma: no cover @@ -106,7 +106,7 @@ async def replay_events_after( send_callback: A callback function to send events to the client Returns: - The stream ID of the replayed events + The stream ID of the replayed events, or None if no events were found. """ pass # pragma: no cover @@ -185,7 +185,7 @@ def close_sse_stream(self, request_id: RequestId) -> None: # pragma: no cover be replayed when the client reconnects with Last-Event-ID. Use this to implement polling behavior during long-running operations - - client will reconnect after the retry interval specified in the priming event. + the client will reconnect after the retry interval specified in the priming event. Args: request_id: The request ID whose SSE stream should be closed. @@ -213,7 +213,7 @@ def close_standalone_sse_stream(self) -> None: # pragma: no cover with Last-Event-ID to resume receiving notifications. Use this to implement polling behavior for the notification stream - - client will reconnect after the retry interval specified in the priming event. + the client will reconnect after the retry interval specified in the priming event. Note: This is a no-op if there is no active standalone SSE stream. @@ -316,7 +316,7 @@ def _create_json_response( status_code: HTTPStatus = HTTPStatus.OK, headers: dict[str, str] | None = None, ) -> Response: - """Create a JSON response from a JSONRPCMessage""" + """Create a JSON response from a JSONRPCMessage.""" response_headers = {"Content-Type": CONTENT_TYPE_JSON} if headers: # pragma: lax no cover response_headers.update(headers) @@ -362,7 +362,7 @@ async def _clean_up_memory_streams(self, request_id: RequestId) -> None: self._request_streams.pop(request_id, None) async def handle_request(self, scope: Scope, receive: Receive, send: Send) -> None: - """Application entry point that handles all HTTP requests""" + """Application entry point that handles all HTTP requests.""" request = Request(scope, receive) # Validate request headers for DNS rebinding protection @@ -536,7 +536,7 @@ async def _handle_post_request(self, scope: Scope, request: Request, receive: Re if isinstance(event_message.message, JSONRPCResponse | JSONRPCError): response_message = event_message.message break - # For notifications and request, keep waiting + # For notifications and requests, keep waiting else: # pragma: no cover logger.debug(f"received: {event_message.message.method}") @@ -860,6 +860,7 @@ async def _validate_protocol_version(self, request: Request, send: Send) -> bool async def _replay_events(self, last_event_id: str, request: Request, send: Send) -> None: # pragma: no cover """Replays events that would have been sent after the specified event ID. + Only used when resumability is enabled. """ event_store = self._event_store diff --git a/src/mcp/server/websocket.py b/src/mcp/server/websocket.py index 7b00f7905..3e675da5f 100644 --- a/src/mcp/server/websocket.py +++ b/src/mcp/server/websocket.py @@ -12,8 +12,8 @@ @asynccontextmanager # pragma: no cover async def websocket_server(scope: Scope, receive: Receive, send: Send): - """WebSocket server transport for MCP. This is an ASGI application, suitable to be - used with a framework like Starlette and a server like Hypercorn. + """WebSocket server transport for MCP. This is an ASGI application, suitable for use + with a framework like Starlette and a server like Hypercorn. """ websocket = WebSocket(scope, receive, send) diff --git a/src/mcp/shared/auth.py b/src/mcp/shared/auth.py index bf03a8b8d..ca5b7b45a 100644 --- a/src/mcp/shared/auth.py +++ b/src/mcp/shared/auth.py @@ -33,9 +33,8 @@ def __init__(self, message: str): class OAuthClientMetadata(BaseModel): - """RFC 7591 OAuth 2.0 Dynamic Client Registration metadata. + """RFC 7591 OAuth 2.0 Dynamic Client Registration Metadata. See https://datatracker.ietf.org/doc/html/rfc7591#section-2 - for the full specification. """ redirect_uris: list[AnyUrl] | None = Field(..., min_length=1) @@ -145,9 +144,9 @@ class ProtectedResourceMetadata(BaseModel): resource_documentation: AnyHttpUrl | None = None resource_policy_uri: AnyHttpUrl | None = None resource_tos_uri: AnyHttpUrl | None = None - # tls_client_certificate_bound_access_tokens default is False, but ommited here for clarity + # tls_client_certificate_bound_access_tokens default is False, but omitted here for clarity tls_client_certificate_bound_access_tokens: bool | None = None authorization_details_types_supported: list[str] | None = None dpop_signing_alg_values_supported: list[str] | None = None - # dpop_bound_access_tokens_required default is False, but ommited here for clarity + # dpop_bound_access_tokens_required default is False, but omitted here for clarity dpop_bound_access_tokens_required: bool | None = None diff --git a/src/mcp/shared/memory.py b/src/mcp/shared/memory.py index d01d28b80..f2d5e2b9a 100644 --- a/src/mcp/shared/memory.py +++ b/src/mcp/shared/memory.py @@ -17,7 +17,7 @@ async def create_client_server_memory_streams() -> AsyncGenerator[tuple[MessageStream, MessageStream], None]: """Creates a pair of bidirectional memory streams for client-server communication. - Returns: + Yields: A tuple of (client_streams, server_streams) where each is a tuple of (read_stream, write_stream) """ diff --git a/src/mcp/shared/metadata_utils.py b/src/mcp/shared/metadata_utils.py index 2b66996bd..3b6b27dfe 100644 --- a/src/mcp/shared/metadata_utils.py +++ b/src/mcp/shared/metadata_utils.py @@ -1,7 +1,7 @@ """Utility functions for working with metadata in MCP types. These utilities are primarily intended for client-side usage to properly display -human-readable names in user interfaces in a spec compliant way. +human-readable names in user interfaces in a spec-compliant way. """ from mcp.types import Implementation, Prompt, Resource, ResourceTemplate, Tool diff --git a/src/mcp/shared/session.py b/src/mcp/shared/session.py index 5ee8f3baa..d3f36ded4 100644 --- a/src/mcp/shared/session.py +++ b/src/mcp/shared/session.py @@ -115,6 +115,7 @@ async def respond(self, response: SendResultT | ErrorData) -> None: """Send a response for this request. Must be called within a context manager block. + Raises: RuntimeError: If not used within a context manager AssertionError: If request was already responded to @@ -235,7 +236,7 @@ async def send_request( metadata: MessageMetadata = None, progress_callback: ProgressFnT | None = None, ) -> ReceiveResultT: - """Sends a request and wait for a response. + """Sends a request and waits for a response. Raises an MCPError if the response contains an error. If a request read timeout is provided, it will take precedence over the session read timeout. @@ -512,4 +513,4 @@ async def send_progress_notification( async def _handle_incoming( self, req: RequestResponder[ReceiveRequestT, SendResultT] | ReceiveNotificationT | Exception ) -> None: - """A generic handler for incoming messages. Overwritten by subclasses.""" + """A generic handler for incoming messages. Overridden by subclasses.""" diff --git a/src/mcp/types/_types.py b/src/mcp/types/_types.py index 320422636..9005d253a 100644 --- a/src/mcp/types/_types.py +++ b/src/mcp/types/_types.py @@ -22,7 +22,7 @@ provided by the client. See the "Protocol Version Header" at -https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#protocol-version-header). +https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#protocol-version-header. """ ProgressToken = str | int @@ -108,8 +108,7 @@ class Request(MCPModel, Generic[RequestParamsT, MethodT]): class PaginatedRequest(Request[PaginatedRequestParams | None, MethodT], Generic[MethodT]): - """Base class for paginated requests, - matching the schema's PaginatedRequest interface.""" + """Base class for paginated requests, matching the schema's PaginatedRequest interface.""" params: PaginatedRequestParams | None = None @@ -174,10 +173,10 @@ class Icon(MCPModel): theme: IconTheme | None = None """Optional theme specifier. - - `"light"` indicates the icon is designed for a light background, `"dark"` indicates the icon + + `"light"` indicates the icon is designed for a light background, `"dark"` indicates the icon is designed for a dark background. - + See https://modelcontextprotocol.io/specification/2025-11-25/schema#icon for more details. """ @@ -536,7 +535,7 @@ class TaskStatusNotificationParams(NotificationParams, Task): class TaskStatusNotification(Notification[TaskStatusNotificationParams, Literal["notifications/tasks/status"]]): """An optional notification from the receiver to the requestor, informing them that a task's status has changed. - Receivers are not required to send these notifications + Receivers are not required to send these notifications. """ method: Literal["notifications/tasks/status"] = "notifications/tasks/status" @@ -608,7 +607,7 @@ class ProgressNotificationParams(NotificationParams): message: str | None = None """Message related to progress. - This should provide relevant human readable progress information. + This should provide relevant human-readable progress information. """ @@ -999,7 +998,9 @@ class ToolResultContent(MCPModel): SamplingContent: TypeAlias = TextContent | ImageContent | AudioContent """Basic content types for sampling responses (without tool use). -Used for backwards-compatible CreateMessageResult when tools are not used.""" + +Used for backwards-compatible CreateMessageResult when tools are not used. +""" class SamplingMessage(MCPModel): @@ -1117,7 +1118,7 @@ class ToolAnnotations(MCPModel): idempotent_hint: bool | None = None """ If true, calling the tool repeatedly with the same arguments - will have no additional effect on the its environment. + will have no additional effect on its environment. (This property is meaningful only when `read_only_hint == false`) Default: false """ @@ -1265,7 +1266,7 @@ class ModelPreferences(MCPModel): sampling. Because LLMs can vary along multiple dimensions, choosing the "best" model is - rarely straightforward. Different models excel in different areas—some are + rarely straightforward. Different models excel in different areas—some are faster but less capable, others are more capable but more expensive, and so on. This interface allows servers to express their priorities across multiple dimensions to help clients make an appropriate selection for their use case. @@ -1369,7 +1370,7 @@ class CreateMessageRequest(Request[CreateMessageRequestParams, Literal["sampling class CreateMessageResult(Result): - """The client's response to a sampling/create_message request from the server. + """The client's response to a sampling/createMessage request from the server. This is the backwards-compatible version that returns single content (no arrays). Used when the request does not include tools. @@ -1386,7 +1387,7 @@ class CreateMessageResult(Result): class CreateMessageResultWithTools(Result): - """The client's response to a sampling/create_message request when tools were provided. + """The client's response to a sampling/createMessage request when tools were provided. This version supports array content for tool use flows. """ @@ -1426,14 +1427,14 @@ class PromptReference(MCPModel): type: Literal["ref/prompt"] = "ref/prompt" name: str - """The name of the prompt or prompt template""" + """The name of the prompt or prompt template.""" class CompletionArgument(MCPModel): """The argument's information for completion requests.""" name: str - """The name of the argument""" + """The name of the argument.""" value: str """The value of the argument to use for completion matching.""" @@ -1451,7 +1452,7 @@ class CompleteRequestParams(RequestParams): ref: ResourceTemplateReference | PromptReference argument: CompletionArgument context: CompletionContext | None = None - """Additional, optional context for completions""" + """Additional, optional context for completions.""" class CompleteRequest(Request[CompleteRequestParams, Literal["completion/complete"]]): @@ -1479,7 +1480,7 @@ class Completion(MCPModel): class CompleteResult(Result): - """The server's response to a completion/complete request""" + """The server's response to a completion/complete request.""" completion: Completion @@ -1522,6 +1523,7 @@ class Root(MCPModel): class ListRootsResult(Result): """The client's response to a roots/list request from the server. + This result contains an array of Root objects, each representing a root directory or file that the server can operate on. """ @@ -1643,7 +1645,7 @@ class ElicitRequestFormParams(RequestParams): requested_schema: ElicitRequestedSchema """ - A restricted subset of JSON Schema defining the structure of expected response. + A restricted subset of JSON Schema defining the structure of the expected response. Only top-level properties are allowed, without nesting. """ @@ -1697,8 +1699,8 @@ class ElicitResult(Result): content: dict[str, str | int | float | bool | list[str] | None] | None = None """ The submitted form data, only present when action is "accept" in form mode. - Contains values matching the requested schema. Values can be strings, integers, - booleans, or arrays of strings. + Contains values matching the requested schema. Values can be strings, integers, floats, + booleans, arrays of strings, or null. For URL mode, this field is omitted. """