Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions claude_sdk/observability.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
policy:
id: claude_sdk_observability
name: Claude Agent SDK tool observability hygiene
category: claude_sdk
description: >
Rules covering how a Claude Agent SDK tool emits diagnostics. A tool body
that prints to stdout writes somewhere neither the model nor the
application's log sink reads, and can corrupt a transport that uses stdout
as a protocol channel.

rules:
- id: CSDK-019
title: Tool prints to stdout for diagnostics
severity: low
confidence: 0.65
language: python
applies_to:
- claude_sdk_tool
scope: tool
match:
has_print_call: true
explanation: >
The tool body calls print(), which writes diagnostics to the process's
stdout. The model never sees that output — only the tool's return value
flows back into the agent loop — so the print silently disappears in any
deployment that captures structured records rather than raw stdout. Two
Claude-SDK-specific consequences make it worse than a lost log line. A
tool defined in a Python SDK server is commonly served to the agent over
an MCP stdio transport, where stdout carries the JSON-RPC frames: a loose
print interleaves with them and the client hits a parse error on a line
that is not JSON. And when the SDK is driven programmatically, the host
application is reading the SDK's own message stream, so tool prints land
interleaved with it rather than in the application's logs, attributable to
no particular turn or tool call.
fix: >
Remove the print(). For operator diagnostics, emit through a module logger
(logging.getLogger(__name__).info(...)) so the record carries its module
and level and lands in the application's log sink; where the tool may be
served over a stdio transport, make sure that handler writes to stderr,
which the transport leaves alone. If the information needs to reach the
model, include it in the tool's return value instead.