Skip to content

Commit 4fee123

Browse files
authored
clean up log.error (#1109)
1 parent bd84329 commit 4fee123

File tree

11 files changed

+69
-65
lines changed

11 files changed

+69
-65
lines changed

CLAUDE.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This document contains critical information about working with this codebase. Fo
1616
- Public APIs must have docstrings
1717
- Functions must be focused and small
1818
- Follow existing patterns exactly
19-
- Line length: 88 chars maximum
19+
- Line length: 120 chars maximum
2020

2121
3. Testing Requirements
2222
- Framework: `uv run --frozen pytest`
@@ -116,3 +116,15 @@ This document contains critical information about working with this codebase. Fo
116116
- Follow existing patterns
117117
- Document public APIs
118118
- Test thoroughly
119+
120+
## Exception Handling
121+
122+
- **Always use `logger.exception()` instead of `logger.error()` when catching exceptions**
123+
- Don't include the exception in the message: `logger.exception("Failed")` not `logger.exception(f"Failed: {e}")`
124+
- **Catch specific exceptions** where possible:
125+
- File ops: `except (OSError, PermissionError):`
126+
- JSON: `except json.JSONDecodeError:`
127+
- Network: `except (ConnectionError, TimeoutError):`
128+
- **Only catch `Exception` for**:
129+
- Top-level handlers that must not crash
130+
- Cleanup blocks (log at debug level)

examples/servers/simple-auth/mcp_simple_auth/server.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,8 @@ def main(port: int, auth_server: str, transport: Literal["sse", "streamable-http
160160
mcp_server.run(transport=transport)
161161
logger.info("Server stopped")
162162
return 0
163-
except Exception as e:
164-
logger.error(f"Server error: {e}")
165-
logger.exception("Exception details:")
163+
except Exception:
164+
logger.exception("Server error")
166165
return 1
167166

168167

src/mcp/cli/claude.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ def update_claude_config(
7575
if not config_file.exists():
7676
try:
7777
config_file.write_text("{}")
78-
except Exception as e:
79-
logger.error(
78+
except Exception:
79+
logger.exception(
8080
"Failed to create Claude config file",
8181
extra={
82-
"error": str(e),
8382
"config_file": str(config_file),
8483
},
8584
)
@@ -139,11 +138,10 @@ def update_claude_config(
139138
extra={"config_file": str(config_file)},
140139
)
141140
return True
142-
except Exception as e:
143-
logger.error(
141+
except Exception:
142+
logger.exception(
144143
"Failed to update Claude config",
145144
extra={
146-
"error": str(e),
147145
"config_file": str(config_file),
148146
},
149147
)

src/mcp/cli/cli.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -349,12 +349,11 @@ def run(
349349

350350
server.run(**kwargs)
351351

352-
except Exception as e:
353-
logger.error(
354-
f"Failed to run server: {e}",
352+
except Exception:
353+
logger.exception(
354+
"Failed to run server",
355355
extra={
356356
"file": str(file),
357-
"error": str(e),
358357
},
359358
)
360359
sys.exit(1)
@@ -464,8 +463,8 @@ def install(
464463
if dotenv:
465464
try:
466465
env_dict |= {k: v for k, v in dotenv.dotenv_values(env_file).items() if v is not None}
467-
except Exception as e:
468-
logger.error(f"Failed to load .env file: {e}")
466+
except (OSError, ValueError):
467+
logger.exception("Failed to load .env file")
469468
sys.exit(1)
470469
else:
471470
logger.error("python-dotenv is not installed. Cannot load .env file.")

src/mcp/client/auth.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,8 @@ async def _handle_refresh_response(self, response: httpx.Response) -> bool:
464464
await self.context.storage.set_tokens(token_response)
465465

466466
return True
467-
except ValidationError as e:
468-
logger.error(f"Invalid refresh response: {e}")
467+
except ValidationError:
468+
logger.exception("Invalid refresh response")
469469
self.context.clear_tokens()
470470
return False
471471

@@ -522,8 +522,8 @@ async def async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.
522522
token_request = await self._exchange_token(auth_code, code_verifier)
523523
token_response = yield token_request
524524
await self._handle_token_response(token_response)
525-
except Exception as e:
526-
logger.error(f"OAuth flow error: {e}")
525+
except Exception:
526+
logger.exception("OAuth flow error")
527527
raise
528528

529529
# Add authorization header and make request

src/mcp/client/sse.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ async def sse_reader(
9797
)
9898
logger.debug(f"Received server message: {message}")
9999
except Exception as exc:
100-
logger.error(f"Error parsing server message: {exc}")
100+
logger.exception("Error parsing server message")
101101
await read_stream_writer.send(exc)
102102
continue
103103

@@ -106,7 +106,7 @@ async def sse_reader(
106106
case _:
107107
logger.warning(f"Unknown SSE event: {sse.event}")
108108
except Exception as exc:
109-
logger.error(f"Error in sse_reader: {exc}")
109+
logger.exception("Error in sse_reader")
110110
await read_stream_writer.send(exc)
111111
finally:
112112
await read_stream_writer.aclose()
@@ -126,8 +126,8 @@ async def post_writer(endpoint_url: str):
126126
)
127127
response.raise_for_status()
128128
logger.debug(f"Client message sent successfully: {response.status_code}")
129-
except Exception as exc:
130-
logger.error(f"Error in post_writer: {exc}")
129+
except Exception:
130+
logger.exception("Error in post_writer")
131131
finally:
132132
await write_stream.aclose()
133133

src/mcp/client/streamable_http.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ async def _handle_json_response(
308308
session_message = SessionMessage(message)
309309
await read_stream_writer.send(session_message)
310310
except Exception as exc:
311-
logger.error(f"Error parsing JSON response: {exc}")
311+
logger.exception("Error parsing JSON response")
312312
await read_stream_writer.send(exc)
313313

314314
async def _handle_sse_response(
@@ -410,8 +410,8 @@ async def handle_request_async():
410410
else:
411411
await handle_request_async()
412412

413-
except Exception as exc:
414-
logger.error(f"Error in post_writer: {exc}")
413+
except Exception:
414+
logger.exception("Error in post_writer")
415415
finally:
416416
await read_stream_writer.aclose()
417417
await write_stream.aclose()

src/mcp/os/posix/utilities.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ async def terminate_posix_process_tree(process: Process, timeout_seconds: float
5252
process.terminate()
5353
with anyio.fail_after(timeout_seconds):
5454
await process.wait()
55-
except Exception as term_error:
56-
logger.warning(f"Process termination failed for PID {pid}: {term_error}, attempting force kill")
55+
except Exception:
56+
logger.warning(f"Process termination failed for PID {pid}, attempting force kill")
5757
try:
5858
process.kill()
59-
except Exception as kill_error:
60-
logger.error(f"Failed to kill process {pid}: {kill_error}")
59+
except Exception:
60+
logger.exception(f"Failed to kill process {pid}")

src/mcp/server/fastmcp/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ async def read_resource(self, uri: AnyUrl | str) -> Iterable[ReadResourceContent
311311
content = await resource.read()
312312
return [ReadResourceContents(content=content, mime_type=resource.mime_type)]
313313
except Exception as e:
314-
logger.error(f"Error reading resource {uri}: {e}")
314+
logger.exception(f"Error reading resource {uri}")
315315
raise ResourceError(str(e))
316316

317317
def add_tool(
@@ -961,7 +961,7 @@ async def get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -
961961

962962
return GetPromptResult(messages=pydantic_core.to_jsonable_python(messages))
963963
except Exception as e:
964-
logger.error(f"Error getting prompt {name}: {e}")
964+
logger.exception(f"Error getting prompt {name}")
965965
raise ValueError(str(e))
966966

967967

src/mcp/server/sse.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ def __init__(self, endpoint: str, security_settings: TransportSecuritySettings |
105105
# Validate that endpoint is a relative path and not a full URL
106106
if "://" in endpoint or endpoint.startswith("//") or "?" in endpoint or "#" in endpoint:
107107
raise ValueError(
108-
f"Given endpoint: {endpoint} is not a relative path (e.g., '/messages/'), \
109-
expecting a relative path(e.g., '/messages/')."
108+
f"Given endpoint: {endpoint} is not a relative path (e.g., '/messages/'), "
109+
"expecting a relative path (e.g., '/messages/')."
110110
)
111111

112112
# Ensure endpoint starts with a forward slash
@@ -234,7 +234,7 @@ async def handle_post_message(self, scope: Scope, receive: Receive, send: Send)
234234
message = types.JSONRPCMessage.model_validate_json(body)
235235
logger.debug(f"Validated client message: {message}")
236236
except ValidationError as err:
237-
logger.error(f"Failed to parse message: {err}")
237+
logger.exception("Failed to parse message")
238238
response = Response("Could not parse message", status_code=400)
239239
await response(scope, receive, send)
240240
await writer.send(err)

0 commit comments

Comments
 (0)