Skip to content

Commit acba547

Browse files
Kludexmaxisbey
andauthored
refactor: McpError renamed to MCPError and flatten parameters (#1956)
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
1 parent 65c614e commit acba547

38 files changed

+253
-326
lines changed

docs/experimental/tasks-client.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ if __name__ == "__main__":
337337
Handle task errors gracefully:
338338

339339
```python
340-
from mcp.shared.exceptions import McpError
340+
from mcp.shared.exceptions import MCPError
341341

342342
try:
343343
result = await session.experimental.call_tool_as_task("my_tool", args)
@@ -349,8 +349,8 @@ try:
349349

350350
final = await session.experimental.get_task_result(task_id, CallToolResult)
351351

352-
except McpError as e:
353-
print(f"MCP error: {e.error.message}")
352+
except MCPError as e:
353+
print(f"MCP error: {e.message}")
354354
except Exception as e:
355355
print(f"Error: {e}")
356356
```

docs/migration.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,38 @@ result = await session.list_resources(params=PaginatedRequestParams(cursor="next
121121
result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token"))
122122
```
123123

124+
### `McpError` renamed to `MCPError`
125+
126+
The `McpError` exception class has been renamed to `MCPError` for consistent naming with the MCP acronym style used throughout the SDK.
127+
128+
**Before (v1):**
129+
130+
```python
131+
from mcp.shared.exceptions import McpError
132+
133+
try:
134+
result = await session.call_tool("my_tool")
135+
except McpError as e:
136+
print(f"Error: {e.error.message}")
137+
```
138+
139+
**After (v2):**
140+
141+
```python
142+
from mcp.shared.exceptions import MCPError
143+
144+
try:
145+
result = await session.call_tool("my_tool")
146+
except MCPError as e:
147+
print(f"Error: {e.message}")
148+
```
149+
150+
`MCPError` is also exported from the top-level `mcp` package:
151+
152+
```python
153+
from mcp import MCPError
154+
```
155+
124156
### `FastMCP` renamed to `MCPServer`
125157

126158
The `FastMCP` class has been renamed to `MCPServer` to better reflect its role as the main server class in the SDK. This is a simple rename with no functional changes to the class itself.

examples/snippets/clients/url_elicitation_client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from mcp import ClientSession, types
3434
from mcp.client.sse import sse_client
3535
from mcp.shared.context import RequestContext
36-
from mcp.shared.exceptions import McpError, UrlElicitationRequiredError
36+
from mcp.shared.exceptions import MCPError, UrlElicitationRequiredError
3737
from mcp.types import URL_ELICITATION_REQUIRED
3838

3939

@@ -160,9 +160,9 @@ async def call_tool_with_error_handling(
160160

161161
return result
162162

163-
except McpError as e:
163+
except MCPError as e:
164164
# Check if this is a URL elicitation required error
165-
if e.error.code == URL_ELICITATION_REQUIRED:
165+
if e.code == URL_ELICITATION_REQUIRED:
166166
print("\n[Tool requires URL elicitation to proceed]")
167167

168168
# Convert to typed error to access elicitations

src/mcp/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .client.stdio import StdioServerParameters, stdio_client
55
from .server.session import ServerSession
66
from .server.stdio import stdio_server
7-
from .shared.exceptions import McpError, UrlElicitationRequiredError
7+
from .shared.exceptions import MCPError, UrlElicitationRequiredError
88
from .types import (
99
CallToolRequest,
1010
ClientCapabilities,
@@ -96,7 +96,7 @@
9696
"ListToolsResult",
9797
"LoggingLevel",
9898
"LoggingMessageNotification",
99-
"McpError",
99+
"MCPError",
100100
"Notification",
101101
"PingRequest",
102102
"ProgressNotification",

src/mcp/client/session_group.py

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from mcp.client.stdio import StdioServerParameters
2626
from mcp.client.streamable_http import streamable_http_client
2727
from mcp.shared._httpx_utils import create_mcp_http_client
28-
from mcp.shared.exceptions import McpError
28+
from mcp.shared.exceptions import MCPError
2929
from mcp.shared.session import ProgressFnT
3030

3131

@@ -216,11 +216,9 @@ async def disconnect_from_server(self, session: mcp.ClientSession) -> None:
216216
session_known_for_stack = session in self._session_exit_stacks
217217

218218
if not session_known_for_components and not session_known_for_stack:
219-
raise McpError(
220-
types.ErrorData(
221-
code=types.INVALID_PARAMS,
222-
message="Provided session is not managed or already disconnected.",
223-
)
219+
raise MCPError(
220+
code=types.INVALID_PARAMS,
221+
message="Provided session is not managed or already disconnected.",
224222
)
225223

226224
if session_known_for_components: # pragma: no branch
@@ -352,7 +350,7 @@ async def _aggregate_components(self, server_info: types.Implementation, session
352350
name = self._component_name(prompt.name, server_info)
353351
prompts_temp[name] = prompt
354352
component_names.prompts.add(name)
355-
except McpError as err: # pragma: no cover
353+
except MCPError as err: # pragma: no cover
356354
logging.warning(f"Could not fetch prompts: {err}")
357355

358356
# Query the server for its resources and aggregate to list.
@@ -362,7 +360,7 @@ async def _aggregate_components(self, server_info: types.Implementation, session
362360
name = self._component_name(resource.name, server_info)
363361
resources_temp[name] = resource
364362
component_names.resources.add(name)
365-
except McpError as err: # pragma: no cover
363+
except MCPError as err: # pragma: no cover
366364
logging.warning(f"Could not fetch resources: {err}")
367365

368366
# Query the server for its tools and aggregate to list.
@@ -373,7 +371,7 @@ async def _aggregate_components(self, server_info: types.Implementation, session
373371
tools_temp[name] = tool
374372
tool_to_session_temp[name] = session
375373
component_names.tools.add(name)
376-
except McpError as err: # pragma: no cover
374+
except MCPError as err: # pragma: no cover
377375
logging.warning(f"Could not fetch tools: {err}")
378376

379377
# Clean up exit stack for session if we couldn't retrieve anything
@@ -384,28 +382,19 @@ async def _aggregate_components(self, server_info: types.Implementation, session
384382
# Check for duplicates.
385383
matching_prompts = prompts_temp.keys() & self._prompts.keys()
386384
if matching_prompts:
387-
raise McpError( # pragma: no cover
388-
types.ErrorData(
389-
code=types.INVALID_PARAMS,
390-
message=f"{matching_prompts} already exist in group prompts.",
391-
)
385+
raise MCPError( # pragma: no cover
386+
code=types.INVALID_PARAMS,
387+
message=f"{matching_prompts} already exist in group prompts.",
392388
)
393389
matching_resources = resources_temp.keys() & self._resources.keys()
394390
if matching_resources:
395-
raise McpError( # pragma: no cover
396-
types.ErrorData(
397-
code=types.INVALID_PARAMS,
398-
message=f"{matching_resources} already exist in group resources.",
399-
)
391+
raise MCPError( # pragma: no cover
392+
code=types.INVALID_PARAMS,
393+
message=f"{matching_resources} already exist in group resources.",
400394
)
401395
matching_tools = tools_temp.keys() & self._tools.keys()
402396
if matching_tools:
403-
raise McpError(
404-
types.ErrorData(
405-
code=types.INVALID_PARAMS,
406-
message=f"{matching_tools} already exist in group tools.",
407-
)
408-
)
397+
raise MCPError(code=types.INVALID_PARAMS, message=f"{matching_tools} already exist in group tools.")
409398

410399
# Aggregate components.
411400
self._sessions[session] = component_names

src/mcp/server/experimental/request_context.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from mcp.server.experimental.task_context import ServerTaskContext
1414
from mcp.server.experimental.task_support import TaskSupport
1515
from mcp.server.session import ServerSession
16-
from mcp.shared.exceptions import McpError
16+
from mcp.shared.exceptions import MCPError
1717
from mcp.shared.experimental.tasks.helpers import MODEL_IMMEDIATE_RESPONSE_KEY, is_terminal
1818
from mcp.types import (
1919
METHOD_NOT_FOUND,
@@ -72,32 +72,26 @@ def validate_task_mode(
7272
Args:
7373
tool_task_mode: The tool's execution.taskSupport value
7474
("forbidden", "optional", "required", or None)
75-
raise_error: If True, raises McpError on validation failure. If False, returns ErrorData.
75+
raise_error: If True, raises MCPError on validation failure. If False, returns ErrorData.
7676
7777
Returns:
7878
None if valid, ErrorData if invalid and raise_error=False
7979
8080
Raises:
81-
McpError: If invalid and raise_error=True
81+
MCPError: If invalid and raise_error=True
8282
"""
8383

8484
mode = tool_task_mode or TASK_FORBIDDEN
8585

8686
error: ErrorData | None = None
8787

8888
if mode == TASK_REQUIRED and not self.is_task:
89-
error = ErrorData(
90-
code=METHOD_NOT_FOUND,
91-
message="This tool requires task-augmented invocation",
92-
)
89+
error = ErrorData(code=METHOD_NOT_FOUND, message="This tool requires task-augmented invocation")
9390
elif mode == TASK_FORBIDDEN and self.is_task:
94-
error = ErrorData(
95-
code=METHOD_NOT_FOUND,
96-
message="This tool does not support task-augmented invocation",
97-
)
91+
error = ErrorData(code=METHOD_NOT_FOUND, message="This tool does not support task-augmented invocation")
9892

9993
if error is not None and raise_error:
100-
raise McpError(error)
94+
raise MCPError(code=error.code, message=error.message)
10195

10296
return error
10397

@@ -113,7 +107,7 @@ def validate_for_tool(
113107
114108
Args:
115109
tool: The Tool definition
116-
raise_error: If True, raises McpError on validation failure.
110+
raise_error: If True, raises MCPError on validation failure.
117111
118112
Returns:
119113
None if valid, ErrorData if invalid and raise_error=False

src/mcp/server/experimental/session_features.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ async def elicit_as_task(
114114
The client's elicitation response
115115
116116
Raises:
117-
McpError: If client doesn't support task-augmented elicitation
117+
MCPError: If client doesn't support task-augmented elicitation
118118
"""
119119
client_caps = self._session.client_params.capabilities if self._session.client_params else None
120120
require_task_augmented_elicitation(client_caps)
@@ -174,7 +174,7 @@ async def create_message_as_task(
174174
The sampling result from the client
175175
176176
Raises:
177-
McpError: If client doesn't support task-augmented sampling or tools
177+
MCPError: If client doesn't support task-augmented sampling or tools
178178
ValueError: If tool_use or tool_result message structure is invalid
179179
"""
180180
client_caps = self._session.client_params.capabilities if self._session.client_params else None

src/mcp/server/experimental/task_context.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from mcp.server.experimental.task_result_handler import TaskResultHandler
1414
from mcp.server.session import ServerSession
1515
from mcp.server.validation import validate_sampling_tools, validate_tool_use_result_messages
16-
from mcp.shared.exceptions import McpError
16+
from mcp.shared.exceptions import MCPError
1717
from mcp.shared.experimental.tasks.capabilities import (
1818
require_task_augmented_elicitation,
1919
require_task_augmented_sampling,
@@ -32,7 +32,6 @@
3232
ElicitationCapability,
3333
ElicitRequestedSchema,
3434
ElicitResult,
35-
ErrorData,
3635
IncludeContext,
3736
ModelPreferences,
3837
RequestId,
@@ -173,22 +172,12 @@ async def _send_notification(self) -> None:
173172
def _check_elicitation_capability(self) -> None:
174173
"""Check if the client supports elicitation."""
175174
if not self._session.check_client_capability(ClientCapabilities(elicitation=ElicitationCapability())):
176-
raise McpError(
177-
ErrorData(
178-
code=INVALID_REQUEST,
179-
message="Client does not support elicitation capability",
180-
)
181-
)
175+
raise MCPError(code=INVALID_REQUEST, message="Client does not support elicitation capability")
182176

183177
def _check_sampling_capability(self) -> None:
184178
"""Check if the client supports sampling."""
185179
if not self._session.check_client_capability(ClientCapabilities(sampling=SamplingCapability())):
186-
raise McpError(
187-
ErrorData(
188-
code=INVALID_REQUEST,
189-
message="Client does not support sampling capability",
190-
)
191-
)
180+
raise MCPError(code=INVALID_REQUEST, message="Client does not support sampling capability")
192181

193182
async def elicit(
194183
self,
@@ -213,7 +202,7 @@ async def elicit(
213202
The client's response
214203
215204
Raises:
216-
McpError: If client doesn't support elicitation capability
205+
MCPError: If client doesn't support elicitation capability
217206
"""
218207
self._check_elicitation_capability()
219208

@@ -281,7 +270,7 @@ async def elicit_url(
281270
The client's response indicating acceptance, decline, or cancellation
282271
283272
Raises:
284-
McpError: If client doesn't support elicitation capability
273+
MCPError: If client doesn't support elicitation capability
285274
RuntimeError: If handler is not configured
286275
"""
287276
self._check_elicitation_capability()
@@ -361,7 +350,7 @@ async def create_message(
361350
The sampling result from the client
362351
363352
Raises:
364-
McpError: If client doesn't support sampling capability or tools
353+
MCPError: If client doesn't support sampling capability or tools
365354
ValueError: If tool_use or tool_result message structure is invalid
366355
"""
367356
self._check_sampling_capability()
@@ -436,7 +425,7 @@ async def elicit_as_task(
436425
The client's elicitation response
437426
438427
Raises:
439-
McpError: If client doesn't support task-augmented elicitation
428+
MCPError: If client doesn't support task-augmented elicitation
440429
RuntimeError: If handler is not configured
441430
"""
442431
client_caps = self._session.client_params.capabilities if self._session.client_params else None
@@ -529,7 +518,7 @@ async def create_message_as_task(
529518
The sampling result from the client
530519
531520
Raises:
532-
McpError: If client doesn't support task-augmented sampling or tools
521+
MCPError: If client doesn't support task-augmented sampling or tools
533522
ValueError: If tool_use or tool_result message structure is invalid
534523
RuntimeError: If handler is not configured
535524
"""

src/mcp/server/experimental/task_result_handler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import anyio
1616

1717
from mcp.server.session import ServerSession
18-
from mcp.shared.exceptions import McpError
18+
from mcp.shared.exceptions import MCPError
1919
from mcp.shared.experimental.tasks.helpers import RELATED_TASK_METADATA_KEY, is_terminal
2020
from mcp.shared.experimental.tasks.message_queue import TaskMessageQueue
2121
from mcp.shared.experimental.tasks.resolver import Resolver
@@ -106,7 +106,7 @@ async def handle(
106106
while True:
107107
task = await self._store.get_task(task_id)
108108
if task is None:
109-
raise McpError(ErrorData(code=INVALID_PARAMS, message=f"Task not found: {task_id}"))
109+
raise MCPError(code=INVALID_PARAMS, message=f"Task not found: {task_id}")
110110

111111
await self._deliver_queued_messages(task_id, session, request_id)
112112

@@ -216,6 +216,6 @@ def route_error(self, request_id: RequestId, error: ErrorData) -> bool:
216216
"""
217217
resolver = self._pending_requests.pop(request_id, None)
218218
if resolver is not None and not resolver.done():
219-
resolver.set_exception(McpError(error))
219+
resolver.set_exception(MCPError.from_error_data(error))
220220
return True
221221
return False

0 commit comments

Comments
 (0)