Search before asking
Description
Background
Java and Python currently expose different Tool result contracts:
- Java
Tool.call returns ToolResponse, which can explicitly represent success or failure without throwing an exception.
- Python
Tool.call returns Any. An exception represents failure, while every normal return is treated as success because Python has no equivalent explicit Tool result type.
PR #926 has introduced parallel Tool calls and the cross-language Outcome abstraction for durable batch execution. The resulting model has two distinct outcome layers:
Outcome<T> = whether the durable invocation returned or raised
ToolResponse = whether the Tool operation itself succeeded or failed
Java handles both layers for sequential and parallel calls: a successful Outcome<ToolResponse> may still contain an unsuccessful ToolResponse. Python can currently represent only the invocation layer, so every Outcome.success(value) is treated as a successful Tool operation.
This difference is now visible through several framework surfaces:
ToolResponseEvent.success and ToolResponseEvent.error;
- Tool Execution Events and Agent Trace;
- Tool and MCP operational metrics proposed in PR #955;
- Java/Python Tool bridges;
- MCP protocol-level error results.
There are also concrete built-in cases that cannot be represented consistently today. For example, Java and Python load_skill return normal success values when the Skill manager is unavailable, a Skill does not exist, or a requested Skill resource does not exist. These responses contain useful guidance for the model, but Trace and metrics classify them as successful Tool calls.
Proposed direction
Define one Tool outcome contract across Java and Python while preserving compatibility for existing Python Tools:
- A normal raw Python return remains a successful Tool result.
- Both languages can explicitly return a successful or failed Tool result; Python exposes
ToolResponse.success(...) and ToolResponse.error(...) for this purpose.
- An invocation exception remains a failed Tool result.
- Generic durable
Outcome keeps its invocation-level meaning and does not absorb Tool-specific semantics.
- Sequential and parallel Tool-call paths normalize and record results through the same logic.
- Java/Python bridges preserve explicit Tool success, failure, and error details.
ToolResponseEvent, Execution Events, and operational metrics consume the same normalized Tool outcome.
load_skill missing-manager, missing-Skill, and missing-resource responses become explicit Tool failures while retaining their current model-facing messages.
- MCP protocol-level error results map to failed Tool outcomes instead of successful content.
The runtime must not infer failure by inspecting arbitrary Tool payloads such as strings or dictionaries. Aligning the wire representation of all existing ToolResponseEvent.responses payloads is not required for this change; existing event payload compatibility should be preserved.
Expected validation
Cover native Java, native Python, and both cross-language directions for:
- normal raw success;
- explicit returned success and failure;
- thrown exception;
- sequential and parallel execution;
load_skill failure responses;
- MCP protocol-level failure;
- consistent
ToolResponseEvent and Execution Event outcomes.
Are you willing to submit a PR?
Search before asking
Description
Background
Java and Python currently expose different Tool result contracts:
Tool.callreturnsToolResponse, which can explicitly represent success or failure without throwing an exception.Tool.callreturnsAny. An exception represents failure, while every normal return is treated as success because Python has no equivalent explicit Tool result type.PR #926 has introduced parallel Tool calls and the cross-language
Outcomeabstraction for durable batch execution. The resulting model has two distinct outcome layers:Java handles both layers for sequential and parallel calls: a successful
Outcome<ToolResponse>may still contain an unsuccessfulToolResponse. Python can currently represent only the invocation layer, so everyOutcome.success(value)is treated as a successful Tool operation.This difference is now visible through several framework surfaces:
ToolResponseEvent.successandToolResponseEvent.error;There are also concrete built-in cases that cannot be represented consistently today. For example, Java and Python
load_skillreturn normal success values when the Skill manager is unavailable, a Skill does not exist, or a requested Skill resource does not exist. These responses contain useful guidance for the model, but Trace and metrics classify them as successful Tool calls.Proposed direction
Define one Tool outcome contract across Java and Python while preserving compatibility for existing Python Tools:
ToolResponse.success(...)andToolResponse.error(...)for this purpose.Outcomekeeps its invocation-level meaning and does not absorb Tool-specific semantics.ToolResponseEvent, Execution Events, and operational metrics consume the same normalized Tool outcome.load_skillmissing-manager, missing-Skill, and missing-resource responses become explicit Tool failures while retaining their current model-facing messages.The runtime must not infer failure by inspecting arbitrary Tool payloads such as strings or dictionaries. Aligning the wire representation of all existing
ToolResponseEvent.responsespayloads is not required for this change; existing event payload compatibility should be preserved.Expected validation
Cover native Java, native Python, and both cross-language directions for:
load_skillfailure responses;ToolResponseEventand Execution Event outcomes.Are you willing to submit a PR?