diff --git a/fastmcp_slim/fastmcp/cli/generate.py b/fastmcp_slim/fastmcp/cli/generate.py index dcf8f1267..0530e13a8 100644 --- a/fastmcp_slim/fastmcp/cli/generate.py +++ b/fastmcp_slim/fastmcp/cli/generate.py @@ -11,7 +11,7 @@ from urllib.parse import urlparse import cyclopts import mcp_types import pydantic_core -from mcp import McpError +from mcp import MCPError from rich.console import Console from fastmcp.cli.client import _build_client, resolve_server_spec @@ -754,7 +754,7 @@ async def generate_cli_command( f"[dim]Discovered {len(tools)} tool(s) from {server_spec}[/dim]" ) - except (RuntimeError, TimeoutError, McpError, OSError) as exc: + except (RuntimeError, TimeoutError, MCPError, OSError) as exc: console.print(f"[bold red]Error:[/bold red] Could not connect: {exc}") sys.exit(1) diff --git a/fastmcp_slim/fastmcp/client/client.py b/fastmcp_slim/fastmcp/client/client.py index 718e4de8e..98847dd62 100644 --- a/fastmcp_slim/fastmcp/client/client.py +++ b/fastmcp_slim/fastmcp/client/client.py @@ -16,7 +16,7 @@ import anyio import httpx import mcp_types from exceptiongroup import catch -from mcp import ClientSession, McpError +from mcp import ClientSession, MCPError from mcp_types import GetTaskResult, TaskStatusNotification from pydantic import AnyUrl @@ -617,7 +617,7 @@ class Client( "Session task completed without exception but connection failed" ) # Preserve specific exception types that clients may want to handle - if isinstance(exception, httpx.HTTPStatusError | McpError): + if isinstance(exception, httpx.HTTPStatusError | MCPError): raise exception raise RuntimeError( f"Client failed to connect: {exception}" @@ -865,7 +865,7 @@ class Client( Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ logger.debug(f"[{self.name}] called complete: {ref}") @@ -895,7 +895,7 @@ class Client( Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ result = await self.complete_mcp( ref=ref, argument=argument, context_arguments=context_arguments diff --git a/fastmcp_slim/fastmcp/client/mixins/prompts.py b/fastmcp_slim/fastmcp/client/mixins/prompts.py index 6e2741793..c790e033c 100644 --- a/fastmcp_slim/fastmcp/client/mixins/prompts.py +++ b/fastmcp_slim/fastmcp/client/mixins/prompts.py @@ -47,7 +47,7 @@ class ClientPromptsMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ with client_span( "prompts/list", @@ -80,7 +80,7 @@ class ClientPromptsMixin: Raises: RuntimeError: If the page limit is reached before pagination completes. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ all_prompts: list[mcp_types.Prompt] = [] cursor: str | None = None @@ -129,7 +129,7 @@ class ClientPromptsMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ with client_span( f"prompts/get {name}", @@ -232,7 +232,7 @@ class ClientPromptsMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ # Merge version into request-level meta (not arguments) request_meta = dict(meta) if meta else {} diff --git a/fastmcp_slim/fastmcp/client/mixins/resources.py b/fastmcp_slim/fastmcp/client/mixins/resources.py index 07dcd7a43..58445206d 100644 --- a/fastmcp_slim/fastmcp/client/mixins/resources.py +++ b/fastmcp_slim/fastmcp/client/mixins/resources.py @@ -46,7 +46,7 @@ class ClientResourcesMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ with client_span( "resources/list", @@ -79,7 +79,7 @@ class ClientResourcesMixin: Raises: RuntimeError: If the page limit is reached before pagination completes. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ all_resources: list[mcp_types.Resource] = [] cursor: str | None = None @@ -122,7 +122,7 @@ class ClientResourcesMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ with client_span( "resources/templates/list", @@ -156,7 +156,7 @@ class ClientResourcesMixin: Raises: RuntimeError: If the page limit is reached before pagination completes. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ all_templates: list[mcp_types.ResourceTemplate] = [] cursor: str | None = None @@ -201,7 +201,7 @@ class ClientResourcesMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ uri_str = str(uri) with client_span( @@ -293,7 +293,7 @@ class ClientResourcesMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ # Merge version into request-level meta (not arguments) request_meta = dict(meta) if meta else {} diff --git a/fastmcp_slim/fastmcp/client/mixins/task_management.py b/fastmcp_slim/fastmcp/client/mixins/task_management.py index 9e339113c..9ca9057ed 100644 --- a/fastmcp_slim/fastmcp/client/mixins/task_management.py +++ b/fastmcp_slim/fastmcp/client/mixins/task_management.py @@ -5,7 +5,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any import mcp_types -from mcp import McpError +from mcp import MCPError if TYPE_CHECKING: from fastmcp.client.client import Client @@ -43,7 +43,7 @@ class ClientTaskManagementMixin: Raises: RuntimeError: If client not connected - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ request = GetTaskRequest(params=GetTaskRequestParams(taskId=task_id)) return await self._await_with_session_monitoring( @@ -67,7 +67,7 @@ class ClientTaskManagementMixin: Raises: RuntimeError: If client not connected, task not found, or task failed - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ request = GetTaskPayloadRequest( params=GetTaskPayloadRequestParams(taskId=task_id) @@ -104,7 +104,7 @@ class ClientTaskManagementMixin: Raises: RuntimeError: If client not connected - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ # Send protocol request params = PaginatedRequestParams(cursor=cursor, limit=limit) # type: ignore[call-arg] # Optional field in MCP SDK # ty:ignore[unknown-argument] @@ -126,7 +126,7 @@ class ClientTaskManagementMixin: try: status = await self.get_task_status(task_id) tasks.append(status.model_dump(by_alias=True)) - except McpError: + except MCPError: # Task may have expired or been deleted, skip it continue @@ -146,7 +146,7 @@ class ClientTaskManagementMixin: Raises: RuntimeError: If task doesn't exist - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ request = CancelTaskRequest(params=CancelTaskRequestParams(taskId=task_id)) return await self._await_with_session_monitoring( diff --git a/fastmcp_slim/fastmcp/client/mixins/tools.py b/fastmcp_slim/fastmcp/client/mixins/tools.py index 9a1c52037..975f18f11 100644 --- a/fastmcp_slim/fastmcp/client/mixins/tools.py +++ b/fastmcp_slim/fastmcp/client/mixins/tools.py @@ -51,7 +51,7 @@ class ClientToolsMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ with client_span( "tools/list", @@ -84,7 +84,7 @@ class ClientToolsMixin: Raises: RuntimeError: If the page limit is reached before pagination completes. - McpError: If the request results in a TimeoutError | JSONRPCError + MCPError: If the request results in a TimeoutError | JSONRPCError """ all_tools: list[mcp_types.Tool] = [] cursor: str | None = None @@ -144,7 +144,7 @@ class ClientToolsMixin: Raises: RuntimeError: If called while the client is not connected. - McpError: If the tool call requests results in a TimeoutError | JSONRPCError + MCPError: If the tool call requests results in a TimeoutError | JSONRPCError """ with client_span( f"tools/call {name}", @@ -281,7 +281,7 @@ class ClientToolsMixin: Raises: ToolError: If the tool call results in an error. - McpError: If the tool call request results in a TimeoutError | JSONRPCError + MCPError: If the tool call request results in a TimeoutError | JSONRPCError RuntimeError: If called while the client is not connected. """ # Merge version into request-level meta (not arguments) diff --git a/fastmcp_slim/fastmcp/exceptions.py b/fastmcp_slim/fastmcp/exceptions.py index 947d9be13..b96ea223c 100644 --- a/fastmcp_slim/fastmcp/exceptions.py +++ b/fastmcp_slim/fastmcp/exceptions.py @@ -3,10 +3,10 @@ import logging try: - from mcp import McpError + from mcp import MCPError except ImportError: - class McpError(Exception): # type: ignore[no-redef] + class MCPError(Exception): # type: ignore[no-redef] """Fallback used when MCP dependencies are not installed.""" diff --git a/fastmcp_slim/fastmcp/server/low_level.py b/fastmcp_slim/fastmcp/server/low_level.py index 33393d6a2..efcdc8be2 100644 --- a/fastmcp_slim/fastmcp/server/low_level.py +++ b/fastmcp_slim/fastmcp/server/low_level.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING, Any, cast import anyio import mcp_types from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream -from mcp import LoggingLevel, McpError +from mcp import LoggingLevel, MCPError from mcp.server.lowlevel.server import ( LifespanResultT, NotificationOptions, @@ -135,8 +135,8 @@ class MiddlewareServerSession(ServerSession): mw_context, cast("CallNext[Any, Any]", call_original_handler), ) - except McpError as e: - # McpError can be thrown from middleware in `on_initialize` + except MCPError as e: + # MCPError can be thrown from middleware in `on_initialize` # send the error to responder. if not responder._completed: with responder: @@ -144,7 +144,7 @@ class MiddlewareServerSession(ServerSession): else: # Don't re-raise: prevents responding to initialize request twice logger.warning( - "Received McpError but responder is already completed. " + "Received MCPError but responder is already completed. " "Cannot send error response as response was already sent.", exc_info=e, ) diff --git a/fastmcp_slim/fastmcp/server/middleware/error_handling.py b/fastmcp_slim/fastmcp/server/middleware/error_handling.py index a1b124edf..449ee28f4 100644 --- a/fastmcp_slim/fastmcp/server/middleware/error_handling.py +++ b/fastmcp_slim/fastmcp/server/middleware/error_handling.py @@ -7,8 +7,7 @@ from collections.abc import Callable from typing import Any import anyio -from mcp import McpError -from mcp_types import ErrorData +from mcp import MCPError from fastmcp.exceptions import NotFoundError @@ -47,7 +46,7 @@ class ErrorHandlingMiddleware(Middleware): logger: Logger instance for error logging. If None, uses 'fastmcp.errors' include_traceback: Whether to include full traceback in error logs error_callback: Optional callback function called for each error - transform_errors: Whether to transform non-MCP errors to McpError + transform_errors: Whether to transform non-MCP errors to MCPError """ self.logger = logger or logging.getLogger("fastmcp.errors") self.include_traceback = include_traceback @@ -82,7 +81,7 @@ class ErrorHandlingMiddleware(Middleware): self, error: Exception, context: MiddlewareContext ) -> Exception: """Transform non-MCP errors to proper MCP errors.""" - if isinstance(error, McpError): + if isinstance(error, MCPError): return error if not self.transform_errors: @@ -92,30 +91,20 @@ class ErrorHandlingMiddleware(Middleware): error_type = type(error.__cause__) if error.__cause__ else type(error) if error_type in (ValueError, TypeError): - return McpError( - ErrorData(code=-32602, message=f"Invalid params: {error!s}") - ) + return MCPError(code=-32602, message=f"Invalid params: {error!s}") elif error_type in (FileNotFoundError, KeyError, NotFoundError): # MCP spec defines -32002 specifically for resource not found method = context.method or "" if method.startswith("resources/"): - return McpError( - ErrorData(code=-32002, message=f"Resource not found: {error!s}") - ) - return McpError(ErrorData(code=-32001, message=f"Not found: {error!s}")) + return MCPError(code=-32002, message=f"Resource not found: {error!s}") + return MCPError(code=-32001, message=f"Not found: {error!s}") elif error_type is PermissionError: - return McpError( - ErrorData(code=-32000, message=f"Permission denied: {error!s}") - ) + return MCPError(code=-32000, message=f"Permission denied: {error!s}") # asyncio.TimeoutError is a subclass of TimeoutError in Python 3.10, alias in 3.11+ elif error_type in (TimeoutError, asyncio.TimeoutError): - return McpError( - ErrorData(code=-32000, message=f"Request timeout: {error!s}") - ) + return MCPError(code=-32000, message=f"Request timeout: {error!s}") else: - return McpError( - ErrorData(code=-32603, message=f"Internal error: {error!s}") - ) + return MCPError(code=-32603, message=f"Internal error: {error!s}") async def on_message(self, context: MiddlewareContext, call_next: CallNext) -> Any: """Handle errors for all messages.""" diff --git a/fastmcp_slim/fastmcp/server/middleware/rate_limiting.py b/fastmcp_slim/fastmcp/server/middleware/rate_limiting.py index b767f3a35..a18ebbd48 100644 --- a/fastmcp_slim/fastmcp/server/middleware/rate_limiting.py +++ b/fastmcp_slim/fastmcp/server/middleware/rate_limiting.py @@ -7,17 +7,16 @@ from collections.abc import Awaitable, Callable from typing import Any, cast import anyio -from mcp import McpError -from mcp_types import ErrorData +from mcp import MCPError from .middleware import CallNext, Middleware, MiddlewareContext -class RateLimitError(McpError): +class RateLimitError(MCPError): """Error raised when rate limit is exceeded.""" def __init__(self, message: str = "Rate limit exceeded"): - super().__init__(ErrorData(code=-32000, message=message)) + super().__init__(code=-32000, message=message) class TokenBucketRateLimiter: diff --git a/fastmcp_slim/fastmcp/server/mixins/mcp_operations.py b/fastmcp_slim/fastmcp/server/mixins/mcp_operations.py index 5f183ccf1..b972ac7ff 100644 --- a/fastmcp_slim/fastmcp/server/mixins/mcp_operations.py +++ b/fastmcp_slim/fastmcp/server/mixins/mcp_operations.py @@ -6,7 +6,7 @@ from collections.abc import Awaitable, Callable, Sequence from typing import TYPE_CHECKING, Any, TypeVar, cast import mcp_types -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import ContentBlock from pydantic import AnyUrl @@ -29,7 +29,7 @@ def _apply_pagination( cursor: str | None, page_size: int | None, ) -> tuple[list[PaginateT], str | None]: - """Apply pagination to items, raising McpError for invalid cursors. + """Apply pagination to items, raising MCPError for invalid cursors. If page_size is None, returns all items without pagination. """ @@ -38,7 +38,7 @@ def _apply_pagination( try: return paginate_sequence(items, cursor, page_size) except ValueError as e: - raise McpError(mcp_types.ErrorData(code=-32602, message=str(e))) from e + raise MCPError(code=-32602, message=str(e)) from e class MCPOperationsMixin: @@ -291,15 +291,11 @@ class MCPOperationsMixin: return result return result.to_mcp_result(uri) except DisabledError as e: - raise McpError( - mcp_types.ErrorData( - code=-32002, message=f"Resource not found: {str(uri)!r}" - ) + raise MCPError( + code=-32002, message=f"Resource not found: {str(uri)!r}" ) from e except NotFoundError as e: - raise McpError( - mcp_types.ErrorData(code=-32002, message=f"Resource not found: {e}") - ) from e + raise MCPError(code=-32002, message=f"Resource not found: {e}") from e async def _get_prompt_mcp( self, name: str, arguments: dict[str, Any] | None diff --git a/fastmcp_slim/fastmcp/server/providers/proxy.py b/fastmcp_slim/fastmcp/server/providers/proxy.py index 27c796bca..71c7abc73 100644 --- a/fastmcp_slim/fastmcp/server/providers/proxy.py +++ b/fastmcp_slim/fastmcp/server/providers/proxy.py @@ -20,7 +20,7 @@ from mcp import ServerSession from mcp.client.session import ClientSession from mcp.server.lowlevel.server import request_ctx from mcp.shared.context import LifespanContextT, RequestContext -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import ( METHOD_NOT_FOUND, BlobResourceContents, @@ -66,12 +66,10 @@ logger = get_logger(__name__) ClientFactoryT = Callable[[], Client] | Callable[[], Awaitable[Client]] -def _proxy_upstream_error(error: Exception) -> McpError: - return McpError( - mcp_types.ErrorData( - code=mcp_types.INTERNAL_ERROR, - message=str(error), - ) +def _proxy_upstream_error(error: Exception) -> MCPError: + return MCPError( + code=mcp_types.INTERNAL_ERROR, + message=str(error), ) @@ -98,7 +96,7 @@ class ProxyInitializeMiddleware(Middleware): ) async with client: await client.initialize() - except McpError: + except MCPError: raise except ( RuntimeError, @@ -635,7 +633,7 @@ class ProxyProvider(Provider): tools = [ ProxyTool.from_mcp_tool(self.client_factory, t) for t in mcp_tools ] - except McpError as e: + except MCPError as e: if e.error.code == METHOD_NOT_FOUND: tools = [] else: @@ -672,7 +670,7 @@ class ProxyProvider(Provider): ProxyResource.from_mcp_resource(self.client_factory, r) for r in mcp_resources ] - except McpError as e: + except MCPError as e: if e.error.code == METHOD_NOT_FOUND: resources = [] else: @@ -709,7 +707,7 @@ class ProxyProvider(Provider): ProxyTemplate.from_mcp_template(self.client_factory, t) for t in mcp_templates ] - except McpError as e: + except MCPError as e: if e.error.code == METHOD_NOT_FOUND: templates = [] else: @@ -746,7 +744,7 @@ class ProxyProvider(Provider): ProxyPrompt.from_mcp_prompt(self.client_factory, p) for p in mcp_prompts ] - except McpError as e: + except MCPError as e: if e.error.code == METHOD_NOT_FOUND: prompts = [] else: diff --git a/fastmcp_slim/fastmcp/server/server.py b/fastmcp_slim/fastmcp/server/server.py index 0ee49ccea..7334115de 100644 --- a/fastmcp_slim/fastmcp/server/server.py +++ b/fastmcp_slim/fastmcp/server/server.py @@ -27,7 +27,7 @@ from key_value.aio.adapters.pydantic import PydanticAdapter from key_value.aio.protocols import AsyncKeyValue from key_value.aio.stores.memory import MemoryStore from mcp.server.lowlevel.server import LifespanResultT -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import ( Annotations, AnyFunction, @@ -1473,7 +1473,7 @@ class FastMCP( exc_info=True, ) raise - except McpError: + except MCPError: logger.exception(f"Error reading resource {uri!r}") raise except Exception as e: @@ -1517,7 +1517,7 @@ class FastMCP( e.log_level, f"Error reading resource {uri!r}", exc_info=True ) raise - except McpError: + except MCPError: logger.exception(f"Error reading resource {uri!r}") raise except Exception as e: @@ -1638,7 +1638,7 @@ class FastMCP( e.log_level, f"Error rendering prompt {name!r}", exc_info=True ) raise - except McpError: + except MCPError: logger.exception(f"Error rendering prompt {name!r}") raise except Exception as e: diff --git a/fastmcp_slim/fastmcp/server/tasks/elicitation.py b/fastmcp_slim/fastmcp/server/tasks/elicitation.py index e1597058b..dfafe940b 100644 --- a/fastmcp_slim/fastmcp/server/tasks/elicitation.py +++ b/fastmcp_slim/fastmcp/server/tasks/elicitation.py @@ -68,7 +68,7 @@ async def elicit_for_task( Raises: RuntimeError: If Docket is not available - McpError: If the elicitation request fails + MCPError: If the elicitation request fails """ docket = fastmcp._docket if docket is None: diff --git a/fastmcp_slim/fastmcp/server/tasks/handlers.py b/fastmcp_slim/fastmcp/server/tasks/handlers.py index 2c6988ee0..11e19b073 100644 --- a/fastmcp_slim/fastmcp/server/tasks/handlers.py +++ b/fastmcp_slim/fastmcp/server/tasks/handlers.py @@ -11,8 +11,8 @@ from datetime import datetime, timezone from typing import TYPE_CHECKING, Any, Literal import mcp_types -from mcp.shared.exceptions import McpError -from mcp_types import INTERNAL_ERROR, ErrorData +from mcp.shared.exceptions import MCPError +from mcp_types import INTERNAL_ERROR from fastmcp.server.dependencies import ( _current_docket, @@ -95,11 +95,9 @@ async def submit_to_docket( # mounted children (whose parent server owns the Docket instance). docket = ctx.fastmcp._docket or _current_docket.get() if docket is None: - raise McpError( - ErrorData( - code=INTERNAL_ERROR, - message="Background tasks require a running FastMCP server context", - ) + raise MCPError( + code=INTERNAL_ERROR, + message="Background tasks require a running FastMCP server context", ) # Register the current server so background workers resolve diff --git a/fastmcp_slim/fastmcp/server/tasks/requests.py b/fastmcp_slim/fastmcp/server/tasks/requests.py index 60c9a8977..112908796 100644 --- a/fastmcp_slim/fastmcp/server/tasks/requests.py +++ b/fastmcp_slim/fastmcp/server/tasks/requests.py @@ -13,12 +13,11 @@ from typing import TYPE_CHECKING, Any, Literal import mcp_types from docket.execution import ExecutionState -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import ( INTERNAL_ERROR, INVALID_PARAMS, CancelTaskResult, - ErrorData, GetTaskResult, ListTasksResult, ) @@ -88,7 +87,7 @@ async def _lookup_task_execution( Tuple of (execution, created_at, poll_interval_ms) Raises: - McpError: If task not found or execution not found + MCPError: If task not found or execution not found """ prefix = task_redis_prefix(task_scope) task_meta_key = docket.key(f"{prefix}:{client_task_id}") @@ -104,18 +103,14 @@ async def _lookup_task_execution( # Decode and validate task_key task_key = task_key_bytes.decode("utf-8") if task_key_bytes else None if not task_key: - raise McpError( - ErrorData(code=INVALID_PARAMS, message=f"Task {client_task_id} not found") - ) + raise MCPError(code=INVALID_PARAMS, message=f"Task {client_task_id} not found") # Get execution execution = await docket.get_execution(task_key) if not execution: - raise McpError( - ErrorData( - code=INVALID_PARAMS, - message=f"Task {client_task_id} execution not found", - ) + raise MCPError( + code=INVALID_PARAMS, + message=f"Task {client_task_id} execution not found", ) # Parse metadata with defaults @@ -145,10 +140,8 @@ async def tasks_get_handler(server: FastMCP, params: dict[str, Any]) -> GetTaskR async with fastmcp.server.context.Context(fastmcp=server): client_task_id = params.get("taskId") if not client_task_id: - raise McpError( - ErrorData( - code=INVALID_PARAMS, message="Missing required parameter: taskId" - ) + raise MCPError( + code=INVALID_PARAMS, message="Missing required parameter: taskId" ) # Get authorization scope for task lookup @@ -157,11 +150,9 @@ async def tasks_get_handler(server: FastMCP, params: dict[str, Any]) -> GetTaskR # Get Docket instance docket = server._docket if docket is None: - raise McpError( - ErrorData( - code=INTERNAL_ERROR, - message="Background tasks require Docket", - ) + raise MCPError( + code=INTERNAL_ERROR, + message="Background tasks require Docket", ) # Look up task execution and metadata @@ -232,10 +223,8 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: async with fastmcp.server.context.Context(fastmcp=server): client_task_id = params.get("taskId") if not client_task_id: - raise McpError( - ErrorData( - code=INVALID_PARAMS, message="Missing required parameter: taskId" - ) + raise MCPError( + code=INVALID_PARAMS, message="Missing required parameter: taskId" ) # Get authorization scope for task lookup @@ -244,11 +233,9 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: # Get execution from Docket (use instance attribute for cross-task access) docket = server._docket if docket is None: - raise McpError( - ErrorData( - code=INTERNAL_ERROR, - message="Background tasks require Docket", - ) + raise MCPError( + code=INTERNAL_ERROR, + message="Background tasks require Docket", ) # Look up full task key from Redis @@ -259,20 +246,16 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: task_key = None if task_key_bytes is None else task_key_bytes.decode("utf-8") if task_key is None: - raise McpError( - ErrorData( - code=INVALID_PARAMS, - message=f"Invalid taskId: {client_task_id} not found", - ) + raise MCPError( + code=INVALID_PARAMS, + message=f"Invalid taskId: {client_task_id} not found", ) execution = await docket.get_execution(task_key) if execution is None: - raise McpError( - ErrorData( - code=INVALID_PARAMS, - message=f"Invalid taskId: {client_task_id} not found", - ) + raise MCPError( + code=INVALID_PARAMS, + message=f"Invalid taskId: {client_task_id} not found", ) # Sync state from Redis @@ -282,11 +265,9 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: state_map = DOCKET_TO_MCP_STATE if execution.state not in (ExecutionState.COMPLETED, ExecutionState.FAILED): mcp_state = state_map.get(execution.state, "failed") - raise McpError( - ErrorData( - code=INVALID_PARAMS, - message=f"Task not completed yet (current state: {mcp_state})", - ) + raise MCPError( + code=INVALID_PARAMS, + message=f"Task not completed yet (current state: {mcp_state})", ) # Get result from Docket @@ -331,11 +312,9 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: component = None if component is None: - raise McpError( - ErrorData( - code=INTERNAL_ERROR, - message=f"Component not found for task: {component_key}", - ) + raise MCPError( + code=INTERNAL_ERROR, + message=f"Component not found for task: {component_key}", ) # Build related-task metadata @@ -390,11 +369,9 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any: return mcp_result else: - raise McpError( - ErrorData( - code=INTERNAL_ERROR, - message=f"Internal error: Unknown component type: {type(component).__name__}", - ) + raise MCPError( + code=INTERNAL_ERROR, + message=f"Internal error: Unknown component type: {type(component).__name__}", ) @@ -433,10 +410,8 @@ async def tasks_cancel_handler( async with fastmcp.server.context.Context(fastmcp=server): client_task_id = params.get("taskId") if not client_task_id: - raise McpError( - ErrorData( - code=INVALID_PARAMS, message="Missing required parameter: taskId" - ) + raise MCPError( + code=INVALID_PARAMS, message="Missing required parameter: taskId" ) # Get authorization scope for task lookup @@ -445,11 +420,9 @@ async def tasks_cancel_handler( # Get Docket instance docket = server._docket if docket is None: - raise McpError( - ErrorData( - code=INTERNAL_ERROR, - message="Background tasks require Docket", - ) + raise MCPError( + code=INTERNAL_ERROR, + message="Background tasks require Docket", ) # Look up task execution and metadata diff --git a/fastmcp_slim/fastmcp/server/tasks/routing.py b/fastmcp_slim/fastmcp/server/tasks/routing.py index 288fa2521..97839eff3 100644 --- a/fastmcp_slim/fastmcp/server/tasks/routing.py +++ b/fastmcp_slim/fastmcp/server/tasks/routing.py @@ -8,8 +8,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Literal import mcp_types -from mcp.shared.exceptions import McpError -from mcp_types import METHOD_NOT_FOUND, ErrorData +from mcp.shared.exceptions import MCPError +from mcp_types import METHOD_NOT_FOUND from fastmcp.server.tasks.config import TaskMeta from fastmcp.server.tasks.handlers import submit_to_docket @@ -41,7 +41,7 @@ async def check_background_task( CreateTaskResult if submitted to docket, None for sync execution Raises: - McpError: If mode="required" but no task metadata, or mode="forbidden" + MCPError: If mode="required" but no task metadata, or mode="forbidden" but task metadata is present """ task_config = component.task_config @@ -51,20 +51,16 @@ async def check_background_task( # Enforce mode="required" - must have task metadata if task_config.mode == "required" and not task_meta: - raise McpError( - ErrorData( - code=METHOD_NOT_FOUND, - message=f"{entity_label} requires task-augmented execution", - ) + raise MCPError( + code=METHOD_NOT_FOUND, + message=f"{entity_label} requires task-augmented execution", ) # Enforce mode="forbidden" - cannot be called with task metadata if not task_config.supports_tasks() and task_meta: - raise McpError( - ErrorData( - code=METHOD_NOT_FOUND, - message=f"{entity_label} does not support task-augmented execution", - ) + raise MCPError( + code=METHOD_NOT_FOUND, + message=f"{entity_label} does not support task-augmented execution", ) # No task metadata - synchronous execution diff --git a/fastmcp_slim/fastmcp/tools/function_tool.py b/fastmcp_slim/fastmcp/tools/function_tool.py index 8f4c1a17b..5e1d28cbe 100644 --- a/fastmcp_slim/fastmcp/tools/function_tool.py +++ b/fastmcp_slim/fastmcp/tools/function_tool.py @@ -24,8 +24,8 @@ from typing import ( ) import anyio -from mcp.shared.exceptions import McpError -from mcp_types import ErrorData, Icon, ToolAnnotations +from mcp.shared.exceptions import MCPError +from mcp_types import Icon, ToolAnnotations from pydantic import Field, TypeAdapter from pydantic import ValidationError as PydanticValidationError from pydantic.json_schema import SkipJsonSchema @@ -408,11 +408,9 @@ class FunctionTool(Tool): f"Consider using task=True for long-running operations. " f"See https://gofastmcp.com/servers/tasks" ) - raise McpError( - ErrorData( - code=-32000, - message=f"Tool '{self.name}' execution timed out after {self.timeout}s", - ) + raise MCPError( + code=-32000, + message=f"Tool '{self.name}' execution timed out after {self.timeout}s", ) from None else: result = await self._execute(type_adapter, exec_is_async, arguments) diff --git a/fastmcp_slim/fastmcp/utilities/exceptions.py b/fastmcp_slim/fastmcp/utilities/exceptions.py index cf2117891..dfdda672e 100644 --- a/fastmcp_slim/fastmcp/utilities/exceptions.py +++ b/fastmcp_slim/fastmcp/utilities/exceptions.py @@ -2,9 +2,8 @@ from collections.abc import Callable, Iterable, Mapping from typing import Any import httpx -import mcp_types from exceptiongroup import BaseExceptionGroup -from mcp import McpError +from mcp import MCPError import fastmcp @@ -20,11 +19,9 @@ def iter_exc(group: BaseExceptionGroup): def _exception_handler(group: BaseExceptionGroup): for leaf in iter_exc(group): if isinstance(leaf, httpx.ConnectTimeout): - raise McpError( - error=mcp_types.ErrorData( - code=httpx.codes.REQUEST_TIMEOUT, - message="Timed out while waiting for response.", - ) + raise MCPError( + code=httpx.codes.REQUEST_TIMEOUT, + message="Timed out while waiting for response.", ) raise leaf diff --git a/tests/client/client/test_client.py b/tests/client/client/test_client.py index 0fa02c562..3cccc8ba2 100644 --- a/tests/client/client/test_client.py +++ b/tests/client/client/test_client.py @@ -7,7 +7,7 @@ from typing import Any, cast import anyio import pytest -from mcp import ClientSession, McpError +from mcp import ClientSession, MCPError from mcp_types import TextContent from pydantic import AnyUrl @@ -284,7 +284,7 @@ async def test_server_deserialization_error(): client = Client(transport=FastMCPTransport(server)) async with client: - with pytest.raises(McpError, match="Could not convert argument"): + with pytest.raises(MCPError, match="Could not convert argument"): await client.get_prompt( "strict_typed_prompt", { diff --git a/tests/client/client/test_timeout.py b/tests/client/client/test_timeout.py index 5106e7cb6..0e83d0aed 100644 --- a/tests/client/client/test_timeout.py +++ b/tests/client/client/test_timeout.py @@ -1,7 +1,7 @@ """Client timeout tests.""" import pytest -from mcp import McpError +from mcp import MCPError from fastmcp.client import Client from fastmcp.client.transports import FastMCPTransport @@ -14,14 +14,14 @@ class TestTimeout: transport=FastMCPTransport(fastmcp_server), timeout=0.05 ) as client: with pytest.raises( - McpError, + MCPError, match="Timed out while waiting for response to ClientRequest. Waited 0.05 seconds", ): await client.call_tool("sleep", {"seconds": 0.1}) async def test_timeout_tool_call(self, fastmcp_server: FastMCP): async with Client(transport=FastMCPTransport(fastmcp_server)) as client: - with pytest.raises(McpError): + with pytest.raises(MCPError): await client.call_tool("sleep", {"seconds": 0.1}, timeout=0.01) async def test_timeout_tool_call_overrides_client_timeout( @@ -31,7 +31,7 @@ class TestTimeout: transport=FastMCPTransport(fastmcp_server), timeout=2, ) as client: - with pytest.raises(McpError): + with pytest.raises(MCPError): await client.call_tool("sleep", {"seconds": 0.1}, timeout=0.01) async def test_timeout_tool_call_overrides_client_timeout_even_if_lower( diff --git a/tests/client/tasks/test_task_result_caching.py b/tests/client/tasks/test_task_result_caching.py index ea7c8edaf..ae0ccd2da 100644 --- a/tests/client/tasks/test_task_result_caching.py +++ b/tests/client/tasks/test_task_result_caching.py @@ -162,7 +162,7 @@ async def test_forbidden_mode_tool_caches_error_result(): async def test_forbidden_mode_prompt_raises_error(): """Prompts with task=False (mode=forbidden) raise error.""" import pytest - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError mcp = FastMCP("test") @@ -171,15 +171,15 @@ async def test_forbidden_mode_prompt_raises_error(): return "Immediate" async with Client(mcp) as client: - # Prompts with mode="forbidden" raise McpError when called with task=True - with pytest.raises(McpError): + # Prompts with mode="forbidden" raise MCPError when called with task=True + with pytest.raises(MCPError): await client.get_prompt("non_task_prompt", task=True) async def test_forbidden_mode_resource_raises_error(): """Resources with task=False (mode=forbidden) raise error.""" import pytest - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError mcp = FastMCP("test") @@ -188,8 +188,8 @@ async def test_forbidden_mode_resource_raises_error(): return "Immediate" async with Client(mcp) as client: - # Resources with mode="forbidden" raise McpError when called with task=True - with pytest.raises(McpError): + # Resources with mode="forbidden" raise MCPError when called with task=True + with pytest.raises(MCPError): await client.read_resource("file://immediate.txt", task=True) diff --git a/tests/client/test_sse.py b/tests/client/test_sse.py index 4327124a3..beb5b857e 100644 --- a/tests/client/test_sse.py +++ b/tests/client/test_sse.py @@ -3,7 +3,7 @@ import json import sys import pytest -from mcp import McpError +from mcp import MCPError from mcp_types import TextResourceContents from fastmcp.client import Client @@ -161,7 +161,7 @@ async def test_nested_sse_server_resolves_correctly(nested_sse_server: str): class TestTimeout: async def test_timeout(self, sse_server: str): with pytest.raises( - McpError, + MCPError, match="Timed out while waiting for response to ClientRequest. Waited 0.03 seconds", ): async with Client( @@ -172,7 +172,7 @@ class TestTimeout: async def test_timeout_tool_call(self, sse_server: str): async with Client(transport=SSETransport(sse_server)) as client: - with pytest.raises(McpError, match="Timed out"): + with pytest.raises(MCPError, match="Timed out"): await client.call_tool("sleep", {"seconds": 0.1}, timeout=0.03) async def test_timeout_tool_call_overrides_client_timeout_if_lower( @@ -182,7 +182,7 @@ class TestTimeout: transport=SSETransport(sse_server), timeout=2, ) as client: - with pytest.raises(McpError, match="Timed out"): + with pytest.raises(MCPError, match="Timed out"): await client.call_tool("sleep", {"seconds": 0.1}, timeout=0.03) async def test_timeout_client_timeout_does_not_override_tool_call_timeout_if_lower( diff --git a/tests/client/test_streamable_http.py b/tests/client/test_streamable_http.py index 31706df24..85594ade9 100644 --- a/tests/client/test_streamable_http.py +++ b/tests/client/test_streamable_http.py @@ -5,7 +5,7 @@ from contextlib import suppress from unittest.mock import AsyncMock, call import pytest -from mcp import McpError +from mcp import MCPError from mcp_types import TextResourceContents from fastmcp import Context @@ -265,8 +265,8 @@ async def test_nested_streamable_http_server_resolves_correctly(nested_server: s class TestTimeout: async def test_timeout(self, streamable_http_server: str): # note this transport behaves differently than others and raises - # McpError from the *client* context - with pytest.raises(McpError, match="Timed out"): + # MCPError from the *client* context + with pytest.raises(MCPError, match="Timed out"): async with Client( transport=StreamableHttpTransport(streamable_http_server), timeout=0.02, @@ -277,7 +277,7 @@ class TestTimeout: async with Client( transport=StreamableHttpTransport(streamable_http_server), ) as client: - with pytest.raises(McpError): + with pytest.raises(MCPError): await client.call_tool("sleep", {"seconds": 0.2}, timeout=0.1) async def test_timeout_tool_call_overrides_client_timeout( @@ -287,5 +287,5 @@ class TestTimeout: transport=StreamableHttpTransport(streamable_http_server), timeout=2, ) as client: - with pytest.raises(McpError): + with pytest.raises(MCPError): await client.call_tool("sleep", {"seconds": 0.2}, timeout=0.1) diff --git a/tests/integration_tests/test_github_mcp_remote.py b/tests/integration_tests/test_github_mcp_remote.py index f07018c86..10d36efb5 100644 --- a/tests/integration_tests/test_github_mcp_remote.py +++ b/tests/integration_tests/test_github_mcp_remote.py @@ -2,7 +2,7 @@ import json import os import pytest -from mcp import McpError +from mcp import MCPError from mcp_types import Resource, TextContent, Tool from fastmcp import Client @@ -96,7 +96,7 @@ class TestGithubMCPRemote: """Test calling a non-existing tool""" async with streamable_http_client: assert streamable_http_client.is_connected() - with pytest.raises(McpError, match=r"unknown tool|tool not found"): + with pytest.raises(MCPError, match=r"unknown tool|tool not found"): await streamable_http_client.call_tool("foo") async def test_call_tool_list_commits( diff --git a/tests/server/middleware/test_error_handling.py b/tests/server/middleware/test_error_handling.py index b8ad07973..773bb7d0b 100644 --- a/tests/server/middleware/test_error_handling.py +++ b/tests/server/middleware/test_error_handling.py @@ -4,7 +4,7 @@ import logging from unittest.mock import AsyncMock, MagicMock import pytest -from mcp import McpError +from mcp import MCPError from fastmcp import FastMCP from fastmcp.client import Client @@ -106,9 +106,8 @@ class TestErrorHandlingMiddleware: def test_transform_error_mcp_error(self, mock_context): """Test that MCP errors are not transformed.""" middleware = ErrorHandlingMiddleware() - from mcp_types import ErrorData - error = McpError(ErrorData(code=-32001, message="test error")) + error = MCPError(code=-32001, message="test error") result = middleware._transform_error(error, mock_context) @@ -130,7 +129,7 @@ class TestErrorHandlingMiddleware: result = middleware._transform_error(error, mock_context) - assert isinstance(result, McpError) + assert isinstance(result, MCPError) assert result.error.code == -32602 assert "Invalid params: test error" in result.error.message @@ -146,7 +145,7 @@ class TestErrorHandlingMiddleware: ]: result = middleware._transform_error(error, resource_context) - assert isinstance(result, McpError) + assert isinstance(result, MCPError) assert result.error.code == -32002 assert "Resource not found: test error" in result.error.message @@ -160,7 +159,7 @@ class TestErrorHandlingMiddleware: ]: result = middleware._transform_error(error, mock_context) - assert isinstance(result, McpError) + assert isinstance(result, MCPError) assert result.error.code == -32001 assert "Not found: test error" in result.error.message @@ -171,7 +170,7 @@ class TestErrorHandlingMiddleware: result = middleware._transform_error(error, mock_context) - assert isinstance(result, McpError) + assert isinstance(result, MCPError) assert result.error.code == -32000 assert "Permission denied: test error" in result.error.message @@ -182,7 +181,7 @@ class TestErrorHandlingMiddleware: result = middleware._transform_error(error, mock_context) - assert isinstance(result, McpError) + assert isinstance(result, MCPError) assert result.error.code == -32000 assert "Request timeout: test error" in result.error.message @@ -193,7 +192,7 @@ class TestErrorHandlingMiddleware: result = middleware._transform_error(error, mock_context) - assert isinstance(result, McpError) + assert isinstance(result, MCPError) assert result.error.code == -32603 assert "Internal error: test error" in result.error.message @@ -212,10 +211,10 @@ class TestErrorHandlingMiddleware: mock_call_next = AsyncMock(side_effect=ValueError("test error")) with caplog.at_level(logging.ERROR): - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await middleware.on_message(mock_context, mock_call_next) - assert isinstance(exc_info.value, McpError) + assert isinstance(exc_info.value, MCPError) assert exc_info.value.error.code == -32602 assert "Invalid params: test error" in exc_info.value.error.message assert "Error in test_method: ValueError: test error" in caplog.text @@ -228,10 +227,10 @@ class TestErrorHandlingMiddleware: mock_call_next = AsyncMock(side_effect=tool_error) with caplog.at_level(logging.ERROR): - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await middleware.on_message(mock_context, mock_call_next) - assert isinstance(exc_info.value, McpError) + assert isinstance(exc_info.value, MCPError) assert exc_info.value.error.code == -32602 assert "Invalid params: test error" in exc_info.value.error.message assert "Error in test_method: ToolError: test error" in caplog.text diff --git a/tests/server/middleware/test_initialization_middleware.py b/tests/server/middleware/test_initialization_middleware.py index 4a06103dd..2e832f724 100644 --- a/tests/server/middleware/test_initialization_middleware.py +++ b/tests/server/middleware/test_initialization_middleware.py @@ -5,8 +5,8 @@ from typing import Any import mcp_types as mt import pytest -from mcp import McpError -from mcp_types import ErrorData, TextContent +from mcp import MCPError +from mcp_types import TextContent from fastmcp import Client, FastMCP from fastmcp.server.middleware import CallNext, Middleware, MiddlewareContext @@ -299,7 +299,7 @@ async def test_middleware_can_access_initialize_result(): async def test_middleware_mcp_error_during_initialization(): - """Test that McpError raised in middleware during initialization is sent to client.""" + """Test that MCPError raised in middleware during initialization is sent to client.""" server = FastMCP("TestServer") class ErrorThrowingMiddleware(Middleware): @@ -308,15 +308,13 @@ async def test_middleware_mcp_error_during_initialization(): context: MiddlewareContext[mt.InitializeRequest], call_next: CallNext[mt.InitializeRequest, mt.InitializeResult | None], ) -> mt.InitializeResult | None: - raise McpError( - ErrorData( - code=mt.INVALID_PARAMS, message="Invalid initialization parameters" - ) + raise MCPError( + code=mt.INVALID_PARAMS, message="Invalid initialization parameters" ) server.add_middleware(ErrorThrowingMiddleware()) - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: async with Client(server): pass @@ -325,7 +323,7 @@ async def test_middleware_mcp_error_during_initialization(): async def test_middleware_mcp_error_before_call_next(): - """Test McpError raised before calling next middleware.""" + """Test MCPError raised before calling next middleware.""" server = FastMCP("TestServer") class EarlyErrorMiddleware(Middleware): @@ -334,13 +332,11 @@ async def test_middleware_mcp_error_before_call_next(): context: MiddlewareContext[mt.InitializeRequest], call_next: CallNext[mt.InitializeRequest, mt.InitializeResult | None], ) -> mt.InitializeResult | None: - raise McpError( - ErrorData(code=mt.INVALID_REQUEST, message="Request validation failed") - ) + raise MCPError(code=mt.INVALID_REQUEST, message="Request validation failed") server.add_middleware(EarlyErrorMiddleware()) - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: async with Client(server): pass @@ -349,7 +345,7 @@ async def test_middleware_mcp_error_before_call_next(): async def test_middleware_mcp_error_after_call_next(): - """Test that McpError raised after call_next doesn't break the connection. + """Test that MCPError raised after call_next doesn't break the connection. When an error is raised after call_next, the responder has already completed, so the error is caught but not sent to the responder (checked via _completed flag). @@ -368,9 +364,7 @@ async def test_middleware_mcp_error_after_call_next(): ) -> mt.InitializeResult | None: await call_next(context) self.error_raised = True - raise McpError( - ErrorData(code=mt.INTERNAL_ERROR, message="Post-processing failed") - ) + raise MCPError(code=mt.INTERNAL_ERROR, message="Post-processing failed") middleware = PostProcessingErrorMiddleware() server.add_middleware(middleware) diff --git a/tests/server/middleware/test_logging.py b/tests/server/middleware/test_logging.py index d9dbb1f11..5c9360447 100644 --- a/tests/server/middleware/test_logging.py +++ b/tests/server/middleware/test_logging.py @@ -6,7 +6,6 @@ from collections.abc import Generator from typing import Any, Literal, TypeVar from unittest.mock import AsyncMock, MagicMock, patch -import mcp import mcp_types import pytest from inline_snapshot import snapshot diff --git a/tests/server/providers/proxy/test_proxy_server.py b/tests/server/providers/proxy/test_proxy_server.py index b25b2dfd3..8b19e546f 100644 --- a/tests/server/providers/proxy/test_proxy_server.py +++ b/tests/server/providers/proxy/test_proxy_server.py @@ -8,7 +8,7 @@ import mcp_types import pytest from anyio import create_task_group from dirty_equals import Contains -from mcp import McpError +from mcp import MCPError from mcp_types import Icon, TextContent, TextResourceContents from pydantic import AnyUrl @@ -259,7 +259,7 @@ async def test_proxy_ping_surfaces_wrong_remote_path(): async with run_server_async(remote, transport="http") as url: proxy = create_proxy(StreamableHttpTransport(url.removesuffix("/mcp"))) - with pytest.raises(McpError, match="Session terminated"): + with pytest.raises(MCPError, match="Session terminated"): async with Client(proxy): pass @@ -271,7 +271,7 @@ async def test_proxy_initialize_forwards_remote_connection_error(): provider_error_strategy="raise", ) - with pytest.raises(McpError, match="Client failed to connect"): + with pytest.raises(MCPError, match="Client failed to connect"): async with Client(proxy): pass @@ -294,7 +294,7 @@ async def test_proxy_list_tools_client_surfaces_remote_connection_error(): provider_error_strategy="raise", ) - with pytest.raises(McpError, match="Client failed to connect"): + with pytest.raises(MCPError, match="Client failed to connect"): async with Client(proxy) as client: await client.list_tools() @@ -561,7 +561,7 @@ class TestResources: async def test_read_resource_returns_none_if_not_found(self, proxy_server): with pytest.raises( - McpError, match="Unknown resource: 'resource://nonexistent'" + MCPError, match="Unknown resource: 'resource://nonexistent'" ): async with Client(proxy_server) as client: await client.read_resource("resource://nonexistent") diff --git a/tests/server/tasks/test_resource_task_meta_parameter.py b/tests/server/tasks/test_resource_task_meta_parameter.py index ec5137932..f22bbf92e 100644 --- a/tests/server/tasks/test_resource_task_meta_parameter.py +++ b/tests/server/tasks/test_resource_task_meta_parameter.py @@ -6,7 +6,7 @@ over sync vs task execution for resources and resource templates. """ import pytest -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from fastmcp import FastMCP from fastmcp.client import Client @@ -44,14 +44,14 @@ class TestResourceTaskMetaParameter: assert result.contents[0].content == "hello world" async def test_task_meta_on_forbidden_resource_raises_error(self): - """Providing task_meta to a task=False resource raises McpError.""" + """Providing task_meta to a task=False resource raises MCPError.""" server = FastMCP("test") @server.resource("data://test", task=False) async def sync_only_resource() -> str: return "hello" - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await server.read_resource("data://test", task_meta=TaskMeta()) assert "does not support task-augmented execution" in str(exc_info.value) @@ -100,14 +100,14 @@ class TestResourceTemplateTaslMeta: assert result.contents[0].content == "Item 42" async def test_template_task_meta_on_forbidden_template_raises_error(self): - """Providing task_meta to a task=False template raises McpError.""" + """Providing task_meta to a task=False template raises MCPError.""" server = FastMCP("test") @server.resource("item://{id}", task=False) async def sync_only_template(id: str) -> str: return f"Item {id}" - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await server.read_resource("item://42", task_meta=TaskMeta()) assert "does not support task-augmented execution" in str(exc_info.value) diff --git a/tests/server/tasks/test_server_tasks_parameter.py b/tests/server/tasks/test_server_tasks_parameter.py index 54b8a94cb..5e69c0c5a 100644 --- a/tests/server/tasks/test_server_tasks_parameter.py +++ b/tests/server/tasks/test_server_tasks_parameter.py @@ -54,7 +54,7 @@ async def test_server_tasks_true_defaults_all_components(): async def test_server_tasks_false_defaults_all_components(): """Server with tasks=False makes all components default to mode=forbidden.""" import pytest - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError mcp = FastMCP("test", tasks=False) @@ -78,12 +78,12 @@ async def test_server_tasks_false_defaults_all_components(): assert result.is_error assert "does not support task-augmented execution" in str(result) - # Prompt with mode="forbidden" raises McpError when called with task=True - with pytest.raises(McpError): + # Prompt with mode="forbidden" raises MCPError when called with task=True + with pytest.raises(MCPError): await client.get_prompt("my_prompt", task=True) - # Resource with mode="forbidden" raises McpError when called with task=True - with pytest.raises(McpError): + # Resource with mode="forbidden" raises MCPError when called with task=True + with pytest.raises(MCPError): await client.read_resource("test://resource", task=True) @@ -174,7 +174,7 @@ async def test_component_explicit_true_overrides_server_false(): async def test_mixed_explicit_and_inherited(): """Mix of explicit True/False/None on different components.""" import pytest - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError mcp = FastMCP("test", tasks=True) # Server default is True @@ -240,8 +240,8 @@ async def test_mixed_explicit_and_inherited(): inherited_prompt_task = await client.get_prompt("inherited_prompt", task=True) assert not inherited_prompt_task.returned_immediately - # Explicit False prompt (mode="forbidden") raises McpError - with pytest.raises(McpError): + # Explicit False prompt (mode="forbidden") raises MCPError + with pytest.raises(MCPError): await client.get_prompt("explicit_false_prompt", task=True) # Resources @@ -250,8 +250,8 @@ async def test_mixed_explicit_and_inherited(): ) assert not inherited_resource_task.returned_immediately - # Explicit False resource (mode="forbidden") raises McpError - with pytest.raises(McpError): + # Explicit False resource (mode="forbidden") raises MCPError + with pytest.raises(MCPError): await client.read_resource("test://explicit_false", task=True) @@ -303,7 +303,7 @@ async def test_resource_template_inherits_server_tasks_default(): async def test_multiple_components_same_name_different_tasks(): """Different component types with same name can have different task settings.""" import pytest - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError mcp = FastMCP("test", tasks=False) @@ -320,8 +320,8 @@ async def test_multiple_components_same_name_different_tasks(): tool_task = await client.call_tool("shared_name", task=True) assert not tool_task.returned_immediately - # Prompt inheriting False (mode="forbidden") raises McpError - with pytest.raises(McpError): + # Prompt inheriting False (mode="forbidden") raises MCPError + with pytest.raises(MCPError): await client.get_prompt("shared_name_prompt", task=True) diff --git a/tests/server/tasks/test_task_config.py b/tests/server/tasks/test_task_config.py index 2211e5667..72cdeec45 100644 --- a/tests/server/tasks/test_task_config.py +++ b/tests/server/tasks/test_task_config.py @@ -8,7 +8,7 @@ Tests for TaskConfig: from datetime import timedelta import pytest -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import TextContent, ToolExecution from mcp_types import Tool as MCPTool @@ -189,7 +189,7 @@ class TestResourceModeEnforcement: from mcp_types import METHOD_NOT_FOUND async with Client(server) as client: - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await client.read_resource("resource://required") assert exc_info.value.error.code == METHOD_NOT_FOUND @@ -241,7 +241,7 @@ class TestPromptModeEnforcement: from mcp_types import METHOD_NOT_FOUND async with Client(server) as client: - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await client.get_prompt("required_prompt") assert exc_info.value.error.code == METHOD_NOT_FOUND diff --git a/tests/server/tasks/test_task_meta_parameter.py b/tests/server/tasks/test_task_meta_parameter.py index db7e14b50..792a2c0f8 100644 --- a/tests/server/tasks/test_task_meta_parameter.py +++ b/tests/server/tasks/test_task_meta_parameter.py @@ -56,7 +56,7 @@ class TestTaskMetaParameter: async def sync_only_tool(x: int) -> int: return x * 2 - # Error is raised before docket is needed (McpError wrapped as ToolError) + # Error is raised before docket is needed (MCPError wrapped as ToolError) with pytest.raises(ToolError) as exc_info: await server.call_tool("sync_only_tool", {"x": 5}, task_meta=TaskMeta()) diff --git a/tests/server/tasks/test_task_methods.py b/tests/server/tasks/test_task_methods.py index 07bef01ce..f1ebfd5da 100644 --- a/tests/server/tasks/test_task_methods.py +++ b/tests/server/tasks/test_task_methods.py @@ -7,7 +7,7 @@ Tests the tasks/get, tasks/result, and tasks/list JSON-RPC protocol methods. import asyncio import pytest -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from fastmcp import FastMCP from fastmcp.client import Client @@ -150,7 +150,7 @@ async def test_get_status_nonexistent_task_raises_error(endpoint_server): async with Client(endpoint_server) as client: # Try to get status for task that was never created # Per SDK implementation: raises ValueError which becomes JSON-RPC error - with pytest.raises(McpError, match="Task nonexistent-task-id not found"): + with pytest.raises(MCPError, match="Task nonexistent-task-id not found"): await client.get_task_status("nonexistent-task-id") diff --git a/tests/server/tasks/test_task_prompts.py b/tests/server/tasks/test_task_prompts.py index df1e29a7b..a66167a7f 100644 --- a/tests/server/tasks/test_task_prompts.py +++ b/tests/server/tasks/test_task_prompts.py @@ -70,7 +70,7 @@ async def test_prompt_task_executes_in_background(prompt_server): async def test_forbidden_mode_prompt_rejects_task_calls(prompt_server): """Prompts with task=False (mode=forbidden) reject task-augmented calls.""" - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError from mcp_types import METHOD_NOT_FOUND @prompt_server.prompt(task=False) # Explicitly disable task support @@ -78,10 +78,10 @@ async def test_forbidden_mode_prompt_rejects_task_calls(prompt_server): return f"Sync prompt: {topic}" async with Client(prompt_server) as client: - # Calling with task=True when task=False should raise McpError + # Calling with task=True when task=False should raise MCPError import pytest - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await client.get_prompt("sync_only_prompt", {"topic": "test"}, task=True) # New behavior: mode="forbidden" returns METHOD_NOT_FOUND error diff --git a/tests/server/tasks/test_task_proxy.py b/tests/server/tasks/test_task_proxy.py index 4f7318a6d..582e5876e 100644 --- a/tests/server/tasks/test_task_proxy.py +++ b/tests/server/tasks/test_task_proxy.py @@ -7,11 +7,11 @@ Proxy servers explicitly forbid task-augmented execution. All proxy components Clients connecting through proxies can: - Execute tools/prompts/resources normally (sync execution) - NOT use task-augmented execution (task=True fails gracefully for tools, - raises McpError for prompts/resources) + raises MCPError for prompts/resources) """ import pytest -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import TextContent, TextResourceContents from fastmcp import FastMCP @@ -128,9 +128,9 @@ class TestProxyPromptsTaskForbidden: """Test that prompts with task=True are forbidden through proxy.""" async def test_prompt_task_raises_mcp_error(self, proxy_server: FastMCP): - """Prompt called with task=True through proxy raises McpError.""" + """Prompt called with task=True through proxy raises MCPError.""" async with Client(proxy_server) as client: - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await client.get_prompt("greeting_prompt", {"name": "Alice"}, task=True) assert "does not support task-augmented execution" in str(exc_info.value) @@ -158,17 +158,17 @@ class TestProxyResourcesTaskForbidden: """Test that resources with task=True are forbidden through proxy.""" async def test_resource_task_raises_mcp_error(self, proxy_server: FastMCP): - """Resource read with task=True through proxy raises McpError.""" + """Resource read with task=True through proxy raises MCPError.""" async with Client(proxy_server) as client: - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await client.read_resource("data://info.txt", task=True) assert "does not support task-augmented execution" in str(exc_info.value) async def test_resource_template_task_raises_mcp_error(self, proxy_server: FastMCP): - """Resource template with task=True through proxy raises McpError.""" + """Resource template with task=True through proxy raises MCPError.""" async with Client(proxy_server) as client: - with pytest.raises(McpError) as exc_info: + with pytest.raises(MCPError) as exc_info: await client.read_resource("data://user/42.json", task=True) assert "does not support task-augmented execution" in str(exc_info.value) diff --git a/tests/server/tasks/test_task_resources.py b/tests/server/tasks/test_task_resources.py index f75757d7a..e8b1e289e 100644 --- a/tests/server/tasks/test_task_resources.py +++ b/tests/server/tasks/test_task_resources.py @@ -86,7 +86,7 @@ async def test_resource_template_with_task(resource_server): async def test_forbidden_mode_resource_rejects_task_calls(resource_server): """Resources with task=False (mode=forbidden) reject task-augmented calls.""" import pytest - from mcp.shared.exceptions import McpError + from mcp.shared.exceptions import MCPError from mcp_types import METHOD_NOT_FOUND @resource_server.resource( @@ -96,8 +96,8 @@ async def test_forbidden_mode_resource_rejects_task_calls(resource_server): return "Sync content" async with Client(resource_server) as client: - # Calling with task=True when task=False should raise McpError - with pytest.raises(McpError) as exc_info: + # Calling with task=True when task=False should raise MCPError + with pytest.raises(MCPError) as exc_info: await client.read_resource("file://sync.txt", task=True) # New behavior: mode="forbidden" returns METHOD_NOT_FOUND error diff --git a/tests/server/test_pagination.py b/tests/server/test_pagination.py index efd0b7a2d..8a53e05d3 100644 --- a/tests/server/test_pagination.py +++ b/tests/server/test_pagination.py @@ -6,7 +6,7 @@ from unittest.mock import patch import mcp_types import pytest -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from fastmcp import Client, FastMCP from fastmcp.utilities.pagination import CursorState, paginate_sequence @@ -187,7 +187,7 @@ class TestServerPagination: return "ok" async with Client(server) as client: - with pytest.raises(McpError) as exc: + with pytest.raises(MCPError) as exc: await client.list_tools_mcp(cursor="invalid!") assert exc.value.error.code == -32602 diff --git a/tests/tools/test_tool_timeout.py b/tests/tools/test_tool_timeout.py index 541db271c..301986f92 100644 --- a/tests/tools/test_tool_timeout.py +++ b/tests/tools/test_tool_timeout.py @@ -4,7 +4,7 @@ import time import anyio import pytest -from mcp.shared.exceptions import McpError +from mcp.shared.exceptions import MCPError from mcp_types import TextContent from fastmcp import FastMCP @@ -188,5 +188,5 @@ class TestToolTimeout: return "never" # TimeoutError should be caught and converted to ToolError - with pytest.raises((ToolError, McpError)): + with pytest.raises((ToolError, MCPError)): await mcp.call_tool("times_out")