Conversation
get_zoneinfo raises McpError(INVALID_PARAMS) so a caller can tell a bad
timezone argument from a server fault. The handler's blanket
`except Exception` catches that McpError too and re-raises it as a plain
ValueError, so the code is dropped and the message is prefixed twice:
get_current_time(timezone="Invalid/Zone")
before: "Error processing mcp-server-time query: Invalid timezone:
'No time zone found with key Invalid/Zone'"
after: "Invalid timezone: 'No time zone found with key Invalid/Zone'"
The fetch server, the other Python server here, already lets McpError
propagate. Re-raise it before the blanket handler; everything else keeps
the existing prefix.
The handler had no tests because it was only reachable through serve(),
which binds stdio. Extract build_server() so a test can drive it over an
in-memory session; serve() is otherwise unchanged.
Adds three handler tests: the McpError path (fails without the fix), the
generic-error path, and a success path.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
MCP SDK 1.29.0 still converts the re-raised error into an untyped tool result, so INVALID_PARAMS is not preserved.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Fixes time-server error handling and makes the server testable in memory.
Changes:
- Re-raises
McpErrorbefore generic exception wrapping. - Extracts
build_server(). - Adds handler-level tests.
File summaries
| File | Description |
|---|---|
src/time/src/mcp_server_time/server.py |
Refactors server creation and error handling. |
src/time/test/time_server_test.py |
Tests success and error paths through an in-memory session. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+215
to
+218
| except McpError: | ||
| # Already carries a specific code and message (e.g. INVALID_PARAMS | ||
| # from get_zoneinfo); re-wrapping would discard both. | ||
| raise |
CallToolResult.content is a union; only TextContent carries .text, so accessing it directly failed pyright with 12 errors across three call sites (ImageContent, AudioContent, ResourceLink, EmbeddedResource). Add a first_text() helper that asserts the block is TextContent and returns its text. Both time tools only ever emit text content, so the assertion documents that rather than widening the tests. Also lifts the per-test imports to module scope, matching the rest of the file.
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
MCP 1.29 still converts the re-raised error into an untyped tool-error result.
Review details
Suppressed comments (1)
src/time/src/mcp_server_time/server.py:218
- Re-raising here still does not preserve
INVALID_PARAMSon the wire. With the locked MCP 1.29 SDK, the wrapper installed by@server.call_tool()catches everyExceptionfrom this handler and converts it toCallToolResult(isError=True), so the outer JSON-RPC handler never receives thisMcpError. The new test'sassert result.isErrorconfirms that the typed protocol error is still flattened; only the duplicate prefix is removed. Please route this through a request handler/API that can emit the originalErrorData(or first fix/use an SDK API that letsMcpErrorescape), and assert thatsession.call_tool(...)raises anMcpErrorwhose code isINVALID_PARAMS.
except McpError:
# Already carries a specific code and message (e.g. INVALID_PARAMS
# from get_zoneinfo); re-wrapping would discard both.
raise
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Balanced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
get_zoneinforaises a typed error so a caller can tell a bad argument from a server fault:The
call_toolhandler then catches it with a blanketexcept Exceptionand re-raises it as a plainValueError(src/time/src/mcp_server_time/server.py:215). The code is discarded and the message is prefixed a second time:The
fetchserver, the other Python server in this repo, already letsMcpErrorpropagate untouched.Changes
McpErrorahead of the blanket handler. Every other exception keeps the existing prefix, so nothing else changes on the wire.build_server()fromserve(). The handler previously had no test coverage because it was only reachable throughserve(), which binds stdio; the extraction lets a test drive it over an in-memory session.serve()is otherwise unchanged and still does exactly what it did.Testing
Three tests added against the handler: the
McpErrorpath, the generic-error path, and a success path. The first was confirmed red with only theexcept McpError: raiseremoved:The new tests pin
anyio_backendto asyncio via a fixture, sincetriois not a dev dependency here and the anyio plugin would otherwise parametrize over both.Motivation and Context
The typed error exists so a client can distinguish a correctable argument from a server fault. Flattening it to
ValueErrorremoves that distinction at the only point where it reaches the caller.Types of changes