Skip to content

Commit b83a222

Browse files
committed
Split ValueError from unexpected errors during logs.
ValueError only needs a warning. Unexpected errors need a full traceback.
1 parent 57394b0 commit b83a222

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

src/mcp/server/mcpserver/server.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,7 @@ async def get_prompt(
12831283
"""
12841284
if context is None:
12851285
context = Context(mcp_server=self, subscriptions=self._subscriptions)
1286+
12861287
try:
12871288
prompt = self._prompt_manager.get_prompt(name)
12881289
if not prompt:
@@ -1296,8 +1297,17 @@ async def get_prompt(
12961297
description=prompt.description,
12971298
messages=pydantic_core.to_jsonable_python(rendered),
12981299
)
1300+
12991301
except MCPError:
13001302
raise
1303+
1304+
except ValueError as e:
1305+
# Expected user-input validation failures, like missing required
1306+
# arguments, don't need a full traceback for unexpected errors.
1307+
# Instead, log a concise warning without exc_info.
1308+
logger.warning(f"Error getting prompt {name}: {e}")
1309+
raise ValueError(str(e)) from e
1310+
13011311
except Exception as e:
13021312
logger.exception(f"Error getting prompt {name}")
13031313
raise ValueError(str(e)) from e

tests/server/mcpserver/test_server.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import base64
2+
import logging
23
from pathlib import Path
34
from types import SimpleNamespace
45
from typing import Any
@@ -1527,6 +1528,35 @@ def prompt_fn(name: str) -> str: ... # pragma: no branch
15271528
with pytest.raises(MCPError, match="Missing required arguments"):
15281529
await client.get_prompt("prompt_fn")
15291530

1531+
async def test_get_prompt_missing_args_logs_warning_without_traceback(
1532+
self, caplog: pytest.LogCaptureFixture
1533+
) -> None:
1534+
"""Regression for issue #3342: missing-argument ValueErrors are an expected
1535+
validation failure, so `MCPServer.get_prompt`'s own logger should emit
1536+
a plain warning without exc_info, instead of a full traceback.
1537+
1538+
Note: `jsonrpc_dispatcher` has a separate, intentional catch-all that
1539+
logs a traceback for any handler exception it doesn't recognize as
1540+
`MCPError`/`ValidationError`. However, that generic safety net is out of
1541+
scope here and unaffected by this fix PR #3347.
1542+
"""
1543+
mcp = MCPServer()
1544+
1545+
@mcp.prompt()
1546+
def prompt_fn(name: str) -> str: ... # Pragma: no branch.
1547+
1548+
with caplog.at_level(logging.WARNING, logger="mcp.server.mcpserver.server"):
1549+
async with Client(mcp, mode="legacy") as client:
1550+
with pytest.raises(MCPError, match="Missing required arguments"):
1551+
await client.get_prompt("prompt_fn")
1552+
1553+
server_records = [r for r in caplog.records if r.name == "mcp.server.mcpserver.server"]
1554+
assert len(server_records) == 1
1555+
1556+
# ValueError should have warning log without exc_info.
1557+
assert server_records[0].levelno == logging.WARNING
1558+
assert not server_records[0].exc_info
1559+
15301560

15311561
async def test_resource_decorator_rfc6570_reserved_expansion():
15321562
# Regression: old regex-based param extraction couldn't see `path`

0 commit comments

Comments
 (0)