chore: add D212 lint rule to enforce Google-style docstrings (#1892)

This commit is contained in:
Felix Weinberger 2026-01-16 16:10:52 +00:00 • committed by GitHub
commit d41d0c0128
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
64 changed files with 452 additions and 925 deletions

View file

@ -16,8 +16,7 @@ from mcp.shared.message import SessionMessage
class InMemoryTransport:
"""
In-memory transport for testing MCP servers without network overhead.
"""In-memory transport for testing MCP servers without network overhead.
This transport starts the server in a background task and provides
streams for client-side communication. The server is automatically
@ -43,8 +42,7 @@ class InMemoryTransport:
*,
raise_exceptions: bool = False,
) -> None:
"""
Initialize the in-memory transport.
"""Initialize the in-memory transport.
Args:
server: The MCP server to connect to (Server or FastMCP instance)
@ -63,8 +61,7 @@ class InMemoryTransport:
],
None,
]:
"""
Connect to the server and return streams for communication.
"""Connect to the server and return streams for communication.
Yields:
A tuple of (read_stream, write_stream) for bidirectional communication

View file

@ -1,5 +1,4 @@
"""
OAuth2 Authentication implementation for HTTPX.
"""OAuth2 Authentication implementation for HTTPX.
Implements authorization code flow with PKCE and automatic token refresh.
"""

View file

@ -1,5 +1,4 @@
"""
OAuth client credential extensions for MCP.
"""OAuth client credential extensions for MCP.
Provides OAuth providers for machine-to-machine authentication flows:
- ClientCredentialsOAuthProvider: For client_credentials with client_id + client_secret

View file

@ -1,5 +1,4 @@
"""
OAuth2 Authentication implementation for HTTPX.
"""OAuth2 Authentication implementation for HTTPX.
Implements authorization code flow with PKCE and automatic token refresh.
"""
@ -215,8 +214,7 @@ class OAuthContext:
class OAuthClientProvider(httpx.Auth):
"""
OAuth2 authentication for httpx.
"""OAuth2 authentication for httpx.
Handles OAuth flow with automatic client registration and token storage.
"""
@ -268,8 +266,7 @@ class OAuthClientProvider(httpx.Auth):
self._initialized = False
async def _handle_protected_resource_response(self, response: httpx.Response) -> bool:
"""
Handle protected resource metadata discovery response.
"""Handle protected resource metadata discovery response.
Per SEP-985, supports fallback when discovery fails at one URL.

View file

@ -20,8 +20,7 @@ logger = logging.getLogger(__name__)
def extract_field_from_www_auth(response: Response, field_name: str) -> str | None:
"""
Extract field from WWW-Authenticate header.
"""Extract field from WWW-Authenticate header.
Returns:
Field value if found in WWW-Authenticate header, None otherwise
@ -42,8 +41,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 RFC6750.
Returns:
Scope string if found in WWW-Authenticate header, None otherwise
@ -52,8 +50,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 RFC9728.
Returns:
Resource metadata URL if found in WWW-Authenticate header, None otherwise
@ -65,8 +62,7 @@ def extract_resource_metadata_from_www_auth(response: Response) -> str | None:
def build_protected_resource_metadata_discovery_urls(www_auth_url: str | None, server_url: str) -> list[str]:
"""
Build ordered list of URLs to try for protected resource metadata discovery.
"""Build ordered list of URLs to try for protected resource metadata discovery.
Per SEP-985, the client MUST:
1. Try resource_metadata from WWW-Authenticate header (if present)
@ -127,8 +123,7 @@ 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 ordered list of (url, type) tuples for discovery attempts.
Args:
auth_server_url: URL for the OAuth Authorization Metadata URL if found, otherwise None
@ -173,8 +168,7 @@ def build_oauth_authorization_server_metadata_discovery_urls(auth_server_url: st
async def handle_protected_resource_response(
response: Response,
) -> ProtectedResourceMetadata | None:
"""
Handle protected resource metadata discovery response.
"""Handle protected resource metadata discovery response.
Per SEP-985, supports fallback when discovery fails at one URL.

View file

@ -67,8 +67,7 @@ class Client:
client_info: types.Implementation | None = None,
elicitation_callback: ElicitationFnT | None = None,
) -> None:
"""
Initialize the client with a server.
"""Initialize the client with a server.
Args:
server: The MCP server to connect to (Server or FastMCP instance)
@ -139,8 +138,7 @@ class Client:
@property
def session(self) -> ClientSession:
"""
Get the underlying ClientSession.
"""Get the underlying ClientSession.
This provides access to the full ClientSession API for advanced use cases.
@ -194,8 +192,7 @@ class Client:
return await self.session.list_resource_templates(params=params)
async def read_resource(self, uri: str | AnyUrl) -> types.ReadResourceResult:
"""
Read a resource from the server.
"""Read a resource from the server.
Args:
uri: The URI of the resource to read
@ -222,8 +219,7 @@ class Client:
*,
meta: dict[str, Any] | None = None,
) -> types.CallToolResult:
"""
Call a tool on the server.
"""Call a tool on the server.
Args:
name: The name of the tool to call
@ -255,8 +251,7 @@ class Client:
name: str,
arguments: dict[str, str] | None = None,
) -> types.GetPromptResult:
"""
Get a prompt from the server.
"""Get a prompt from the server.
Args:
name: The name of the prompt
@ -273,8 +268,7 @@ class Client:
argument: dict[str, str],
context_arguments: dict[str, str] | None = None,
) -> types.CompleteResult:
"""
Get completions for a prompt or resource template argument.
"""Get completions for a prompt or resource template argument.
Args:
ref: Reference to the prompt or resource template

View file

@ -1,5 +1,4 @@
"""
Experimental client features.
"""Experimental client features.
WARNING: These APIs are experimental and may change without notice.
"""

View file

@ -1,5 +1,4 @@
"""
Experimental task handler protocols for server -> client requests.
"""Experimental task handler protocols for server -> client requests.
This module provides Protocol types and default handlers for when servers
send task-related requests to clients (the reverse of normal client -> server flow).

View file

@ -1,5 +1,4 @@
"""
Experimental client-side task support.
"""Experimental client-side task support.
This module provides client methods for interacting with MCP tasks.
@ -37,8 +36,7 @@ ResultT = TypeVar("ResultT", bound=types.Result)
class ExperimentalClientFeatures:
"""
Experimental client features for tasks and other experimental APIs.
"""Experimental client features for tasks and other experimental APIs.
WARNING: These APIs are experimental and may change without notice.
@ -108,8 +106,7 @@ class ExperimentalClientFeatures:
)
async def get_task(self, task_id: str) -> types.GetTaskResult:
"""
Get the current status of a task.
"""Get the current status of a task.
Args:
task_id: The task identifier
@ -131,8 +128,7 @@ class ExperimentalClientFeatures:
task_id: str,
result_type: type[ResultT],
) -> ResultT:
"""
Get the result of a completed task.
"""Get the result of a completed task.
The result type depends on the original request type:
- tools/call tasks return CallToolResult
@ -158,8 +154,7 @@ class ExperimentalClientFeatures:
self,
cursor: str | None = None,
) -> types.ListTasksResult:
"""
List all tasks.
"""List all tasks.
Args:
cursor: Optional pagination cursor
@ -176,8 +171,7 @@ class ExperimentalClientFeatures:
)
async def cancel_task(self, task_id: str) -> types.CancelTaskResult:
"""
Cancel a running task.
"""Cancel a running task.
Args:
task_id: The task identifier
@ -195,8 +189,7 @@ class ExperimentalClientFeatures:
)
async def poll_task(self, task_id: str) -> AsyncIterator[types.GetTaskResult]:
"""
Poll a task until it reaches a terminal status.
"""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

View file

@ -1,5 +1,4 @@
"""
SessionGroup concurrently manages multiple MCP session connections.
"""SessionGroup concurrently manages multiple MCP session connections.
Tools, resources, and prompts are aggregated across servers. Servers may
be connected to or disconnected from at any point after initialization.

View file

@ -37,8 +37,7 @@ async def sse_client(
auth: httpx.Auth | None = None,
on_session_created: Callable[[str], None] | None = None,
):
"""
Client transport for SSE.
"""Client transport for SSE.
`sse_read_timeout` determines how long (in seconds) the client will wait for a new
event before disconnecting. All other HTTP operations are controlled by `timeout`.

View file

@ -49,8 +49,7 @@ PROCESS_TERMINATION_TIMEOUT = 2.0
def get_default_environment() -> dict[str, str]:
"""
Returns a default environment object including only environment variables deemed
"""Returns a default environment object including only environment variables deemed
safe to inherit.
"""
env: dict[str, str] = {}
@ -104,8 +103,7 @@ class StdioServerParameters(BaseModel):
@asynccontextmanager
async def stdio_client(server: StdioServerParameters, errlog: TextIO = sys.stderr):
"""
Client transport for stdio: this will connect to a server by spawning a
"""Client transport for stdio: this will connect to a server by spawning a
process and communicating with it over stdin/stdout.
"""
read_stream: MemoryObjectReceiveStream[SessionMessage | Exception]
@ -217,8 +215,7 @@ async def stdio_client(server: StdioServerParameters, errlog: TextIO = sys.stder
def _get_executable_command(command: str) -> str:
"""
Get the correct executable command normalized for the current platform.
"""Get the correct executable command normalized for the current platform.
Args:
command: Base command (e.g., 'uvx', 'npx')
@ -239,8 +236,7 @@ async def _create_platform_compatible_process(
errlog: TextIO = sys.stderr,
cwd: Path | str | None = None,
):
"""
Creates a subprocess in a platform-compatible way.
"""Creates a subprocess in a platform-compatible way.
Unix: Creates process in a new session/process group for killpg support
Windows: Creates process in a Job Object for reliable child termination
@ -260,8 +256,7 @@ async def _create_platform_compatible_process(
async def _terminate_process_tree(process: Process | FallbackProcess, timeout_seconds: float = 2.0) -> None:
"""
Terminate a process and all its children using platform-specific methods.
"""Terminate a process and all its children using platform-specific methods.
Unix: Uses os.killpg() for atomic process group termination
Windows: Uses Job Objects via pywin32 for reliable child process cleanup

View file

@ -509,8 +509,7 @@ async def streamable_http_client(
],
None,
]:
"""
Client transport for StreamableHTTP.
"""Client transport for StreamableHTTP.
Args:
url: The MCP server endpoint URL.

View file

@ -19,8 +19,7 @@ async def websocket_client(
tuple[MemoryObjectReceiveStream[SessionMessage | Exception], MemoryObjectSendStream[SessionMessage]],
None,
]:
"""
WebSocket client transport for MCP, symmetrical to the server version.
"""WebSocket client transport for MCP, symmetrical to the server version.
Connects to 'url' using the 'mcp' subprotocol, then yields:
(read_stream, write_stream)
@ -46,8 +45,7 @@ async def websocket_client(
async with ws_connect(url, subprotocols=[Subprotocol("mcp")]) as ws:
async def ws_reader():
"""
Reads text messages from the WebSocket, parses them as JSON-RPC messages,
"""Reads text messages from the WebSocket, parses them as JSON-RPC messages,
and sends them into read_stream_writer.
"""
async with read_stream_writer:
@ -61,8 +59,7 @@ async def websocket_client(
await read_stream_writer.send(exc)
async def ws_writer():
"""
Reads JSON-RPC messages from write_stream_reader and
"""Reads JSON-RPC messages from write_stream_reader and
sends them to the server.
"""
async with write_stream_reader:

View file

@ -1,6 +1,4 @@
"""
POSIX-specific functionality for stdio client operations.
"""
"""POSIX-specific functionality for stdio client operations."""
import logging
import os
@ -13,8 +11,7 @@ logger = logging.getLogger(__name__)
async def terminate_posix_process_tree(process: Process, timeout_seconds: float = 2.0) -> None:
"""
Terminate a process and all its children on POSIX systems.
"""Terminate a process and all its children on POSIX systems.
Uses os.killpg() for atomic process group termination.

View file

@ -1,6 +1,4 @@
"""
Windows-specific functionality for stdio client operations.
"""
"""Windows-specific functionality for stdio client operations."""
import logging
import shutil
@ -34,8 +32,7 @@ JobHandle = int
def get_windows_executable_command(command: str) -> str:
"""
Get the correct executable command normalized for Windows.
"""Get the correct executable command normalized for Windows.
On Windows, commands might exist with specific extensions (.exe, .cmd, etc.)
that need to be located for proper execution.
@ -66,8 +63,7 @@ def get_windows_executable_command(command: str) -> str:
class FallbackProcess:
"""
A fallback process wrapper for Windows to handle async I/O
"""A fallback process wrapper for Windows to handle async I/O
when using subprocess.Popen, which provides sync-only FileIO objects.
This wraps stdin and stdout into async-compatible
@ -140,8 +136,7 @@ async def create_windows_process(
errlog: TextIO | None = sys.stderr,
cwd: Path | str | None = None,
) -> Process | FallbackProcess:
"""
Creates a subprocess in a Windows-compatible way with Job Object support.
"""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.
@ -199,8 +194,7 @@ async def _create_windows_fallback_process(
errlog: TextIO | None = sys.stderr,
cwd: Path | str | None = None,
) -> FallbackProcess:
"""
Create a subprocess using subprocess.Popen as a fallback when anyio fails.
"""Create a subprocess using subprocess.Popen as a fallback when anyio fails.
This function wraps the sync subprocess.Popen in an async-compatible interface.
"""
@ -231,9 +225,7 @@ async def _create_windows_fallback_process(
def _create_job_object() -> int | None:
"""
Create a Windows Job Object configured to terminate all processes when closed.
"""
"""Create a Windows Job Object configured to terminate all processes when closed."""
if sys.platform != "win32" or not win32job:
return None
@ -250,8 +242,7 @@ 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
"""Try to assign a process to a job object. If assignment fails
for any reason, the job handle is closed.
"""
if not job:
@ -279,8 +270,7 @@ def _maybe_assign_process_to_job(process: Process | FallbackProcess, job: JobHan
async def terminate_windows_process_tree(process: Process | FallbackProcess, timeout_seconds: float = 2.0) -> None:
"""
Terminate a process and all its children on Windows.
"""Terminate a process and all its children on Windows.
If the process has an associated job object, it will be terminated.
Otherwise, falls back to basic process termination.
@ -318,8 +308,7 @@ async def terminate_windows_process_tree(process: Process | FallbackProcess, tim
"Process termination is now handled internally by the stdio_client context manager."
)
async def terminate_windows_process(process: Process | FallbackProcess):
"""
Terminate a Windows process.
"""Terminate a Windows process.
Note: On Windows, terminating a process with process.terminate() doesn't
always guarantee immediate process termination.

View file

@ -1,3 +1 @@
"""
MCP OAuth server authorization components.
"""
"""MCP OAuth server authorization components."""

View file

@ -1,3 +1 @@
"""
Request handlers for MCP authorization endpoints.
"""
"""Request handlers for MCP authorization endpoints."""

View file

@ -15,9 +15,7 @@ from mcp.server.auth.provider import AccessToken, OAuthAuthorizationServerProvid
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
@ -36,9 +34,7 @@ class RevocationHandler:
client_authenticator: ClientAuthenticator
async def handle(self, request: Request) -> Response:
"""
Handler for the OAuth 2.0 Token Revocation endpoint.
"""
"""Handler for the OAuth 2.0 Token Revocation endpoint."""
try:
client = await self.client_authenticator.authenticate_request(request)
except AuthenticationError as e: # pragma: no cover

View file

@ -55,9 +55,7 @@ class TokenRequest(
class TokenErrorResponse(BaseModel):
"""
See https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
"""
"""See https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"""
error: TokenErrorCode
error_description: str | None = None

Some files were not shown because too many files have changed in this diff Show more