chore: add D212 lint rule to enforce Google-style docstrings (#1892)
This commit is contained in:
parent
df039bf97c
commit
d41d0c0128
18 changed files with 452 additions and 925 deletions
|
|
@ -4,9 +4,7 @@ from pydantic import AnyHttpUrl, AnyUrl, BaseModel, Field, field_validator
|
|||
|
||||
|
||||
class OAuthToken(BaseModel):
|
||||
"""
|
||||
See https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
|
||||
"""
|
||||
"""See https://datatracker.ietf.org/doc/html/rfc6749#section-5.1"""
|
||||
|
||||
access_token: str
|
||||
token_type: Literal["Bearer"] = "Bearer"
|
||||
|
|
@ -35,8 +33,7 @@ class InvalidRedirectUriError(Exception):
|
|||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
|
@ -94,8 +91,7 @@ class OAuthClientMetadata(BaseModel):
|
|||
|
||||
|
||||
class OAuthClientInformationFull(OAuthClientMetadata):
|
||||
"""
|
||||
RFC 7591 OAuth 2.0 Dynamic Client Registration full response
|
||||
"""RFC 7591 OAuth 2.0 Dynamic Client Registration full response
|
||||
(client information plus metadata).
|
||||
"""
|
||||
|
||||
|
|
@ -106,8 +102,7 @@ class OAuthClientInformationFull(OAuthClientMetadata):
|
|||
|
||||
|
||||
class OAuthMetadata(BaseModel):
|
||||
"""
|
||||
RFC 8414 OAuth 2.0 Authorization Server Metadata.
|
||||
"""RFC 8414 OAuth 2.0 Authorization Server Metadata.
|
||||
See https://datatracker.ietf.org/doc/html/rfc8414#section-2
|
||||
"""
|
||||
|
||||
|
|
@ -136,8 +131,7 @@ class OAuthMetadata(BaseModel):
|
|||
|
||||
|
||||
class ProtectedResourceMetadata(BaseModel):
|
||||
"""
|
||||
RFC 9728 OAuth 2.0 Protected Resource Metadata.
|
||||
"""RFC 9728 OAuth 2.0 Protected Resource Metadata.
|
||||
See https://datatracker.ietf.org/doc/html/rfc9728#section-2
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
"""
|
||||
Request context for MCP handlers.
|
||||
"""
|
||||
"""Request context for MCP handlers."""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Generic
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ from mcp.types import URL_ELICITATION_REQUIRED, ElicitRequestURLParams, ErrorDat
|
|||
|
||||
|
||||
class McpError(Exception):
|
||||
"""
|
||||
Exception type raised when an error arrives over an MCP connection.
|
||||
"""
|
||||
"""Exception type raised when an error arrives over an MCP connection."""
|
||||
|
||||
error: ErrorData
|
||||
|
||||
|
|
@ -19,8 +17,7 @@ class McpError(Exception):
|
|||
|
||||
|
||||
class StatelessModeNotSupported(RuntimeError):
|
||||
"""
|
||||
Raised when attempting to use a method that is not supported in stateless mode.
|
||||
"""Raised when attempting to use a method that is not supported in stateless mode.
|
||||
|
||||
Server-to-client requests (sampling, elicitation, list_roots) are not
|
||||
supported in stateless HTTP mode because there is no persistent connection
|
||||
|
|
@ -37,8 +34,7 @@ class StatelessModeNotSupported(RuntimeError):
|
|||
|
||||
|
||||
class UrlElicitationRequiredError(McpError):
|
||||
"""
|
||||
Specialized error for when a tool requires URL mode elicitation(s) before proceeding.
|
||||
"""Specialized error for when a tool requires URL mode elicitation(s) before proceeding.
|
||||
|
||||
Servers can raise this error from tool handlers to indicate that the client
|
||||
must complete one or more URL elicitations before the request can be processed.
|
||||
|
|
|
|||
|
|
@ -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().
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
"""
|
||||
In-memory transports
|
||||
"""
|
||||
"""In-memory transports"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -17,8 +15,7 @@ MessageStream = tuple[MemoryObjectReceiveStream[SessionMessage | Exception], Mem
|
|||
|
||||
@asynccontextmanager
|
||||
async def create_client_server_memory_streams() -> AsyncGenerator[tuple[MessageStream, MessageStream], None]:
|
||||
"""
|
||||
Creates a pair of bidirectional memory streams for client-server communication.
|
||||
"""Creates a pair of bidirectional memory streams for client-server communication.
|
||||
|
||||
Returns:
|
||||
A tuple of (client_streams, server_streams) where each is a tuple of
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"""
|
||||
Message wrapper with metadata support.
|
||||
"""Message wrapper with metadata support.
|
||||
|
||||
This module defines a wrapper type that combines JSONRPCMessage with metadata
|
||||
to support transport-specific features like resumability.
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ from mcp.types import Implementation, Prompt, Resource, ResourceTemplate, Tool
|
|||
|
||||
|
||||
def get_display_name(obj: Tool | Resource | Prompt | ResourceTemplate | Implementation) -> str:
|
||||
"""
|
||||
Get the display name for an MCP object with proper precedence.
|
||||
"""Get the display name for an MCP object with proper precedence.
|
||||
|
||||
This is a client-side utility function designed to help MCP clients display
|
||||
human-readable names in their user interfaces. When servers provide a 'title'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
"""
|
||||
ResponseRouter - Protocol for pluggable response routing.
|
||||
"""ResponseRouter - Protocol for pluggable response routing.
|
||||
|
||||
This module defines a protocol for routing JSON-RPC responses to alternative
|
||||
handlers before falling back to the default response stream mechanism.
|
||||
|
|
@ -20,8 +19,7 @@ from mcp.types import ErrorData, RequestId
|
|||
|
||||
|
||||
class ResponseRouter(Protocol):
|
||||
"""
|
||||
Protocol for routing responses to alternative handlers.
|
||||
"""Protocol for routing responses to alternative handlers.
|
||||
|
||||
Implementations check if they have a pending request for the given ID
|
||||
and deliver the response/error to the appropriate handler.
|
||||
|
|
@ -37,8 +35,7 @@ class ResponseRouter(Protocol):
|
|||
"""
|
||||
|
||||
def route_response(self, request_id: RequestId, response: dict[str, Any]) -> bool:
|
||||
"""
|
||||
Try to route a response to a pending request handler.
|
||||
"""Try to route a response to a pending request handler.
|
||||
|
||||
Args:
|
||||
request_id: The JSON-RPC request ID from the response
|
||||
|
|
@ -50,8 +47,7 @@ class ResponseRouter(Protocol):
|
|||
... # pragma: no cover
|
||||
|
||||
def route_error(self, request_id: RequestId, error: ErrorData) -> bool:
|
||||
"""
|
||||
Try to route an error to a pending request handler.
|
||||
"""Try to route an error to a pending request handler.
|
||||
|
||||
Args:
|
||||
request_id: The JSON-RPC request ID from the error response
|
||||
|
|
|
|||
|
|
@ -163,8 +163,7 @@ class BaseSession(
|
|||
ReceiveNotificationT,
|
||||
],
|
||||
):
|
||||
"""
|
||||
Implements an MCP "session" on top of read/write streams, including features
|
||||
"""Implements an MCP "session" on top of read/write streams, including features
|
||||
like request/response linking, notifications, and progress.
|
||||
|
||||
This class is an async context manager that automatically starts processing
|
||||
|
|
@ -199,8 +198,7 @@ class BaseSession(
|
|||
self._exit_stack = AsyncExitStack()
|
||||
|
||||
def add_response_router(self, router: ResponseRouter) -> None:
|
||||
"""
|
||||
Register a response router to handle responses for non-standard requests.
|
||||
"""Register a response router to handle responses for non-standard requests.
|
||||
|
||||
Response routers are checked in order before falling back to the default
|
||||
response stream mechanism. This is used by TaskResultHandler to route
|
||||
|
|
@ -241,8 +239,7 @@ class BaseSession(
|
|||
metadata: MessageMetadata = None,
|
||||
progress_callback: ProgressFnT | None = None,
|
||||
) -> ReceiveResultT:
|
||||
"""
|
||||
Sends a request and wait for a response. Raises an McpError if the
|
||||
"""Sends a request and wait 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.
|
||||
|
||||
|
|
@ -314,8 +311,7 @@ class BaseSession(
|
|||
notification: SendNotificationT,
|
||||
related_request_id: RequestId | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Emits a notification, which is a one-way message that does not expect
|
||||
"""Emits a notification, which is a one-way message that does not expect
|
||||
a response.
|
||||
"""
|
||||
# Some transport implementations may need to set the related_request_id
|
||||
|
|
@ -454,8 +450,7 @@ class BaseSession(
|
|||
self._response_streams.clear()
|
||||
|
||||
def _normalize_request_id(self, response_id: RequestId) -> RequestId:
|
||||
"""
|
||||
Normalize a response ID to match how request IDs are stored.
|
||||
"""Normalize a response ID to match how request IDs are stored.
|
||||
|
||||
Since the client always sends integer IDs, we normalize string IDs
|
||||
to integers when possible. This matches the TypeScript SDK approach:
|
||||
|
|
@ -475,8 +470,7 @@ class BaseSession(
|
|||
return response_id
|
||||
|
||||
async def _handle_response(self, message: SessionMessage) -> None:
|
||||
"""
|
||||
Handle an incoming response or error message.
|
||||
"""Handle an incoming response or error message.
|
||||
|
||||
Checks response routers first (e.g., for task-related responses),
|
||||
then falls back to the normal response stream mechanism.
|
||||
|
|
@ -514,8 +508,7 @@ class BaseSession(
|
|||
await self._handle_incoming(RuntimeError(f"Received response with an unknown request ID: {message}"))
|
||||
|
||||
async def _received_request(self, responder: RequestResponder[ReceiveRequestT, SendResultT]) -> None:
|
||||
"""
|
||||
Can be overridden by subclasses to handle a request without needing to
|
||||
"""Can be overridden by subclasses to handle a request without needing to
|
||||
listen on the message stream.
|
||||
|
||||
If the request is responded to within this method, it will not be
|
||||
|
|
@ -523,8 +516,7 @@ class BaseSession(
|
|||
"""
|
||||
|
||||
async def _received_notification(self, notification: ReceiveNotificationT) -> None:
|
||||
"""
|
||||
Can be overridden by subclasses to handle a notification without needing
|
||||
"""Can be overridden by subclasses to handle a notification without needing
|
||||
to listen on the message stream.
|
||||
"""
|
||||
|
||||
|
|
@ -535,8 +527,7 @@ class BaseSession(
|
|||
total: float | None = None,
|
||||
message: str | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Sends a progress notification for a request that is currently being
|
||||
"""Sends a progress notification for a request that is currently being
|
||||
processed.
|
||||
"""
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue