chore: add D212 lint rule to enforce Google-style docstrings (#1892)
This commit is contained in:
parent
df039bf97c
commit
d41d0c0128
10 changed files with 452 additions and 925 deletions
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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().
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue