Rename McpError to MCPError and convert construction sites

This commit is contained in:
Jeremiah Lowin 2026-07-05 18:07:02 -04:00
commit fdac9f9f2e
No known key found for this signature in database
39 changed files with 217 additions and 281 deletions

View file

@ -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)

View file

@ -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

View file

@ -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 {}

View file

@ -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 {}

View file

@ -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(

View file

@ -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)

View file

@ -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."""

View file

@ -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,
)

View file

@ -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."""

View file

@ -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:

View file

@ -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

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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",
{

View file

@ -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(

View file

@ -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)

View file

@ -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(

View file

@ -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)

View file

@ -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(

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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())

View file

@ -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")

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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")