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
10 changed files with 452 additions and 925 deletions

View file

@ -1,5 +1,4 @@
"""
Pure experimental MCP features (no server dependencies).
"""Pure experimental MCP features (no server dependencies).
WARNING: These APIs are experimental and may change without notice.

View file

@ -1,5 +1,4 @@
"""
Pure task state management for MCP.
"""Pure task state management for MCP.
WARNING: These APIs are experimental and may change without notice.

View file

@ -1,5 +1,4 @@
"""
Tasks capability checking utilities.
"""Tasks capability checking utilities.
This module provides functions for checking and requiring task-related
capabilities. All tasks capability logic is centralized here to keep
@ -21,8 +20,7 @@ def check_tasks_capability(
required: ClientTasksCapability,
client: ClientTasksCapability,
) -> bool:
"""
Check if client's tasks capability matches the required capability.
"""Check if client's tasks capability matches the required capability.
Args:
required: The capability being checked for
@ -78,8 +76,7 @@ def has_task_augmented_sampling(caps: ClientCapabilities) -> bool:
def require_task_augmented_elicitation(client_caps: ClientCapabilities | None) -> None:
"""
Raise McpError if client doesn't support task-augmented elicitation.
"""Raise McpError if client doesn't support task-augmented elicitation.
Args:
client_caps: The client's declared capabilities, or None if not initialized
@ -97,8 +94,7 @@ def require_task_augmented_elicitation(client_caps: ClientCapabilities | None) -
def require_task_augmented_sampling(client_caps: ClientCapabilities | None) -> None:
"""
Raise McpError if client doesn't support task-augmented sampling.
"""Raise McpError if client doesn't support task-augmented sampling.
Args:
client_caps: The client's declared capabilities, or None if not initialized

View file

@ -1,5 +1,4 @@
"""
TaskContext - Pure task state management.
"""TaskContext - Pure task state management.
This module provides TaskContext, which manages task state without any
server/session dependencies. It can be used standalone for distributed
@ -11,8 +10,7 @@ from mcp.types import TASK_STATUS_COMPLETED, TASK_STATUS_FAILED, Result, Task
class TaskContext:
"""
Pure task state management - no session dependencies.
"""Pure task state management - no session dependencies.
This class handles:
- Task state (status, result)
@ -54,8 +52,7 @@ class TaskContext:
return self._cancelled
def request_cancellation(self) -> None:
"""
Request cancellation of this task.
"""Request cancellation of this task.
This sets is_cancelled=True. Task work should check this
periodically and exit gracefully if set.
@ -63,8 +60,7 @@ class TaskContext:
self._cancelled = True
async def update_status(self, message: str) -> None:
"""
Update the task's status message.
"""Update the task's status message.
Args:
message: The new status message
@ -75,8 +71,7 @@ class TaskContext:
)
async def complete(self, result: Result) -> None:
"""
Mark the task as completed with the given result.
"""Mark the task as completed with the given result.
Args:
result: The task result
@ -88,8 +83,7 @@ class TaskContext:
)
async def fail(self, error: str) -> None:
"""
Mark the task as failed with an error message.
"""Mark the task as failed with an error message.
Args:
error: The error message

View file

@ -1,5 +1,4 @@
"""
Helper functions for pure task management.
"""Helper functions for pure task management.
These helpers work with pure TaskContext and don't require server dependencies.
For server-integrated task helpers, use mcp.server.experimental.
@ -36,8 +35,7 @@ RELATED_TASK_METADATA_KEY = "io.modelcontextprotocol/related-task"
def is_terminal(status: TaskStatus) -> bool:
"""
Check if a task status represents a terminal state.
"""Check if a task status represents a terminal state.
Terminal states are those where the task has finished and will not change.
@ -54,8 +52,7 @@ async def cancel_task(
store: TaskStore,
task_id: str,
) -> CancelTaskResult:
"""
Cancel a task with spec-compliant validation.
"""Cancel a task with spec-compliant validation.
Per spec: "Receivers MUST reject cancellation of terminal status tasks
with -32602 (Invalid params)"
@ -111,8 +108,7 @@ def create_task_state(
metadata: TaskMetadata,
task_id: str | None = None,
) -> Task:
"""
Create a Task object with initial state.
"""Create a Task object with initial state.
This is a helper for TaskStore implementations.
@ -139,8 +135,7 @@ async def task_execution(
task_id: str,
store: TaskStore,
) -> AsyncIterator[TaskContext]:
"""
Context manager for safe task execution (pure, no server dependencies).
"""Context manager for safe task execution (pure, no server dependencies).
Loads a task from the store and provides a TaskContext for the work.
If an unhandled exception occurs, the task is automatically marked as failed

View file

@ -1,5 +1,4 @@
"""
In-memory implementation of TaskStore for demonstration purposes.
"""In-memory implementation of TaskStore for demonstration purposes.
This implementation stores all tasks in memory and provides automatic cleanup
based on the TTL duration specified in the task metadata using lazy expiration.
@ -29,8 +28,7 @@ class StoredTask:
class InMemoryTaskStore(TaskStore):
"""
A simple in-memory implementation of TaskStore.
"""A simple in-memory implementation of TaskStore.
Features:
- Automatic TTL-based cleanup (lazy expiration)

View file

@ -1,5 +1,4 @@
"""
TaskMessageQueue - FIFO queue for task-related messages.
"""TaskMessageQueue - FIFO queue for task-related messages.
This implements the core message queue pattern from the MCP Tasks spec.
When a handler needs to send a request (like elicitation) during a task-augmented
@ -25,8 +24,7 @@ from mcp.types import JSONRPCNotification, JSONRPCRequest, RequestId
@dataclass
class QueuedMessage:
"""
A message queued for delivery via tasks/result.
"""A message queued for delivery via tasks/result.
Messages are stored with their type and a resolver for requests
that expect responses.
@ -49,8 +47,7 @@ class QueuedMessage:
class TaskMessageQueue(ABC):
"""
Abstract interface for task message queuing.
"""Abstract interface for task message queuing.
This is a FIFO queue that stores messages to be delivered via `tasks/result`.
When a task-augmented handler calls elicit() or sends a notification, the
@ -65,8 +62,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def enqueue(self, task_id: str, message: QueuedMessage) -> None:
"""
Add a message to the queue for a task.
"""Add a message to the queue for a task.
Args:
task_id: The task identifier
@ -75,8 +71,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def dequeue(self, task_id: str) -> QueuedMessage | None:
"""
Remove and return the next message from the queue.
"""Remove and return the next message from the queue.
Args:
task_id: The task identifier
@ -87,8 +82,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def peek(self, task_id: str) -> QueuedMessage | None:
"""
Return the next message without removing it.
"""Return the next message without removing it.
Args:
task_id: The task identifier
@ -99,8 +93,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def is_empty(self, task_id: str) -> bool:
"""
Check if the queue is empty for a task.
"""Check if the queue is empty for a task.
Args:
task_id: The task identifier
@ -111,8 +104,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def clear(self, task_id: str) -> list[QueuedMessage]:
"""
Remove and return all messages from the queue.
"""Remove and return all messages from the queue.
This is useful for cleanup when a task is cancelled or completed.
@ -125,8 +117,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def wait_for_message(self, task_id: str) -> None:
"""
Wait until a message is available in the queue.
"""Wait until a message is available in the queue.
This blocks until either:
1. A message is enqueued for this task
@ -138,8 +129,7 @@ class TaskMessageQueue(ABC):
@abstractmethod
async def notify_message_available(self, task_id: str) -> None:
"""
Signal that a message is available for a task.
"""Signal that a message is available for a task.
This wakes up any coroutines waiting in wait_for_message().
@ -149,8 +139,7 @@ class TaskMessageQueue(ABC):
class InMemoryTaskMessageQueue(TaskMessageQueue):
"""
In-memory implementation of TaskMessageQueue.
"""In-memory implementation of TaskMessageQueue.
This is suitable for single-process servers. For distributed systems,
implement TaskMessageQueue with Redis, RabbitMQ, etc.
@ -227,8 +216,7 @@ class InMemoryTaskMessageQueue(TaskMessageQueue):
self._events[task_id].set()
def cleanup(self, task_id: str | None = None) -> None:
"""
Clean up queues and events.
"""Clean up queues and events.
Args:
task_id: If provided, clean up only this task. Otherwise clean up all.

View file

@ -1,5 +1,4 @@
"""
Shared polling utilities for task operations.
"""Shared polling utilities for task operations.
This module provides generic polling logic that works for both client→server
and server→client task polling.
@ -20,8 +19,7 @@ async def poll_until_terminal(
task_id: str,
default_interval_ms: int = 500,
) -> AsyncIterator[GetTaskResult]:
"""
Poll a task until it reaches terminal status.
"""Poll a task until it reaches terminal status.
This is a generic utility that works for both client→server and server→client
polling. The caller provides the get_task function appropriate for their direction.

View file

@ -1,5 +1,4 @@
"""
Resolver - An anyio-compatible future-like object for async result passing.
"""Resolver - An anyio-compatible future-like object for async result passing.
This provides a simple way to pass a result (or exception) from one coroutine
to another without depending on asyncio.Future.
@ -13,8 +12,7 @@ T = TypeVar("T")
class Resolver(Generic[T]):
"""
A simple resolver for passing results between coroutines.
"""A simple resolver for passing results between coroutines.
Unlike asyncio.Future, this works with any anyio-compatible async backend.

View file

@ -1,6 +1,4 @@
"""
TaskStore - Abstract interface for task state storage.
"""
"""TaskStore - Abstract interface for task state storage."""
from abc import ABC, abstractmethod
@ -8,8 +6,7 @@ from mcp.types import Result, Task, TaskMetadata, TaskStatus
class TaskStore(ABC):
"""
Abstract interface for task state storage.
"""Abstract interface for task state storage.
This is a pure storage interface - it doesn't manage execution.
Implementations can use in-memory storage, databases, Redis, etc.
@ -23,8 +20,7 @@ class TaskStore(ABC):
metadata: TaskMetadata,
task_id: str | None = None,
) -> Task:
"""
Create a new task.
"""Create a new task.
Args:
metadata: Task metadata (ttl, etc.)
@ -39,8 +35,7 @@ class TaskStore(ABC):
@abstractmethod
async def get_task(self, task_id: str) -> Task | None:
"""
Get a task by ID.
"""Get a task by ID.
Args:
task_id: The task identifier
@ -56,8 +51,7 @@ class TaskStore(ABC):
status: TaskStatus | None = None,
status_message: str | None = None,
) -> Task:
"""
Update a task's status and/or message.
"""Update a task's status and/or message.
Args:
task_id: The task identifier
@ -76,8 +70,7 @@ class TaskStore(ABC):
@abstractmethod
async def store_result(self, task_id: str, result: Result) -> None:
"""
Store the result for a task.
"""Store the result for a task.
Args:
task_id: The task identifier
@ -89,8 +82,7 @@ class TaskStore(ABC):
@abstractmethod
async def get_result(self, task_id: str) -> Result | None:
"""
Get the stored result for a task.
"""Get the stored result for a task.
Args:
task_id: The task identifier
@ -104,8 +96,7 @@ class TaskStore(ABC):
self,
cursor: str | None = None,
) -> tuple[list[Task], str | None]:
"""
List tasks with pagination.
"""List tasks with pagination.
Args:
cursor: Optional cursor for pagination
@ -116,8 +107,7 @@ class TaskStore(ABC):
@abstractmethod
async def delete_task(self, task_id: str) -> bool:
"""
Delete a task.
"""Delete a task.
Args:
task_id: The task identifier
@ -128,8 +118,7 @@ class TaskStore(ABC):
@abstractmethod
async def wait_for_update(self, task_id: str) -> None:
"""
Wait until the task status changes.
"""Wait until the task status changes.
This blocks until either:
1. The task status changes
@ -146,8 +135,7 @@ class TaskStore(ABC):
@abstractmethod
async def notify_update(self, task_id: str) -> None:
"""
Signal that a task has been updated.
"""Signal that a task has been updated.
This wakes up any coroutines waiting in wait_for_update().