feat: log MCP tool calls and agent messages to per-run JSONL#1499
feat: log MCP tool calls and agent messages to per-run JSONL#1499spomichter merged 2 commits intodevfrom
Conversation
MCP server: log every tool call (name, args), response (truncated to 200 chars), duration, and errors. Previously only errors were logged. Agent: pretty_print_langchain_message now also writes to structlog so agent conversation (human, AI, tool calls, tool responses) appears in per-run JSONL logs alongside module-level events. Both are now visible via dimos log.
Greptile SummaryThis PR adds structured per-run JSONL logging to two previously silent code paths: MCP tool call dispatch in Key changes:
Issue found:
Confidence Score: 3/5
Sequence DiagramsequenceDiagram
participant Client as MCP Client
participant Server as mcp_server.py
participant Logger as structlog (JSONL)
participant Tool as RPC Tool
Client->>Server: POST /mcp tools/call {name, args}
alt Tool not found
Server->>Logger: warning("MCP tool not found", tool=name)
Server-->>Client: Tool not found error
else Tool found
Server->>Logger: info("MCP tool call", tool=name, args=args)
Server->>Tool: rpc_call(**args)
alt Exception raised
Tool-->>Server: raises Exception
Server->>Logger: exception("MCP tool error", tool=name, duration=...)
Server-->>Client: Error response
else result is None (async)
Tool-->>Server: None
Server->>Logger: info("MCP tool done (async)", tool=name, duration=...)
Server-->>Client: "It has started..."
else result has agent_encode
Tool-->>Server: result
Server->>Logger: info("MCP tool done", tool=name, duration=..., response=str(result)[:200])
Server-->>Client: result.agent_encode()
else result is text
Tool-->>Server: result
Server->>Logger: info("MCP tool done", tool=name, duration=..., response=str(result)[:200])
Server-->>Client: str(result)
end
end
Note over Server,Logger: Agent message flow (utils.py)
participant Agent as LangChain Agent
participant Utils as utils.py
Agent->>Utils: pretty_print_langchain_message(msg)
Utils->>Utils: _try_to_remove_url_data(content) [console only]
Utils->>Logger: info("Agent message", msg_type=..., content=..., tool_calls=...)
Last reviewed commit: ca796e3 |
| print(f"{time_str} {type_str}") | ||
|
|
||
| # Also log to structlog so agent messages appear in per-run JSONL logs. | ||
| _log_message(msg_type, content, tool_calls) |
There was a problem hiding this comment.
Raw content bypasses URL filtering before JSONL logging
The _log_message call at line 86 passes raw content without filtering through _try_to_remove_url_data. However, the console output (line 70) explicitly filters via _try_to_remove_url_data to strip base64 image URLs. For multimodal messages like [{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,..."}}], this means up to 500 characters of base64 image data will be written to the JSONL log file—inconsistent with the filtering applied to console output.
Pass the filtered content to _log_message instead:
| _log_message(msg_type, content, tool_calls) | |
| _log_message(msg_type, _try_to_remove_url_data(content), tool_calls) |
496451f to
fd30ed2
Compare
- Filter base64 image data once at content extraction instead of at each usage site (console output and JSONL logging). - Simplify _log_message: replace if/elif/else branches with a single logger.info call using conditional kwargs dict.
fd30ed2 to
b5e5e84
Compare
Problem
MCP tool calls and agent messages are invisible in
dimos log:pretty_print_langchain_messagewrites to stdout only. Nothing in per-run JSONL.So when an agent calls a skill, there's no persistent record of what happened.
Solution
MCP server (
mcp_server.py)Log every tool call with name, args, response (truncated 200 chars), duration:
Agent (
utils.py)pretty_print_langchain_messagenow also writes to structlog:Both now visible via
dimos log.Changes
dimos/agents/mcp/mcp_server.py: +12 lines (4 log calls in_handle_tools_call)dimos/agents/utils.py: +19 lines (structlog logger +_log_messagehelper)Contributor License Agreement