diff --git a/internal/runtime/executor/claude_executor.go b/internal/runtime/executor/claude_executor.go index 217d22ae..de270e5f 100644 --- a/internal/runtime/executor/claude_executor.go +++ b/internal/runtime/executor/claude_executor.go @@ -799,6 +799,21 @@ func applyClaudeToolPrefix(body []byte, prefix string) []byte { } path := fmt.Sprintf("messages.%d.content.%d.tool_name", msgIndex.Int(), contentIndex.Int()) body, _ = sjson.SetBytes(body, path, prefix+toolName) + } else if partType == "tool_result" { + // Handle nested tool_reference blocks inside tool_result.content[] + nestedContent := part.Get("content") + if nestedContent.Exists() && nestedContent.IsArray() { + nestedContent.ForEach(func(nestedIndex, nestedPart gjson.Result) bool { + if nestedPart.Get("type").String() == "tool_reference" { + nestedToolName := nestedPart.Get("tool_name").String() + if nestedToolName != "" && !strings.HasPrefix(nestedToolName, prefix) { + nestedPath := fmt.Sprintf("messages.%d.content.%d.content.%d.tool_name", msgIndex.Int(), contentIndex.Int(), nestedIndex.Int()) + body, _ = sjson.SetBytes(body, nestedPath, prefix+nestedToolName) + } + } + return true + }) + } } return true }) @@ -833,6 +848,21 @@ func stripClaudeToolPrefixFromResponse(body []byte, prefix string) []byte { } path := fmt.Sprintf("content.%d.tool_name", index.Int()) body, _ = sjson.SetBytes(body, path, strings.TrimPrefix(toolName, prefix)) + } else if partType == "tool_result" { + // Handle nested tool_reference blocks inside tool_result.content[] + nestedContent := part.Get("content") + if nestedContent.Exists() && nestedContent.IsArray() { + nestedContent.ForEach(func(nestedIndex, nestedPart gjson.Result) bool { + if nestedPart.Get("type").String() == "tool_reference" { + nestedToolName := nestedPart.Get("tool_name").String() + if strings.HasPrefix(nestedToolName, prefix) { + nestedPath := fmt.Sprintf("content.%d.content.%d.tool_name", index.Int(), nestedIndex.Int()) + body, _ = sjson.SetBytes(body, nestedPath, strings.TrimPrefix(nestedToolName, prefix)) + } + } + return true + }) + } } return true }) diff --git a/internal/runtime/executor/claude_executor_test.go b/internal/runtime/executor/claude_executor_test.go index cec9a3cd..a86b6f92 100644 --- a/internal/runtime/executor/claude_executor_test.go +++ b/internal/runtime/executor/claude_executor_test.go @@ -98,3 +98,31 @@ func TestStripClaudeToolPrefixFromStreamLine_WithToolReference(t *testing.T) { t.Fatalf("content_block.tool_name = %q, want %q", got, "beta") } } + +func TestApplyClaudeToolPrefix_NestedToolReference(t *testing.T) { + input := []byte(`{"messages":[{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_123","content":[{"type":"tool_reference","tool_name":"mcp__nia__manage_resource"}]}]}]}`) + out := applyClaudeToolPrefix(input, "proxy_") + got := gjson.GetBytes(out, "messages.0.content.0.content.0.tool_name").String() + if got != "proxy_mcp__nia__manage_resource" { + t.Fatalf("nested tool_reference tool_name = %q, want %q", got, "proxy_mcp__nia__manage_resource") + } +} + +func TestStripClaudeToolPrefixFromResponse_NestedToolReference(t *testing.T) { + input := []byte(`{"content":[{"type":"tool_result","tool_use_id":"toolu_123","content":[{"type":"tool_reference","tool_name":"proxy_mcp__nia__manage_resource"}]}]}`) + out := stripClaudeToolPrefixFromResponse(input, "proxy_") + got := gjson.GetBytes(out, "content.0.content.0.tool_name").String() + if got != "mcp__nia__manage_resource" { + t.Fatalf("nested tool_reference tool_name = %q, want %q", got, "mcp__nia__manage_resource") + } +} + +func TestApplyClaudeToolPrefix_NestedToolReferenceWithStringContent(t *testing.T) { + // tool_result.content can be a string - should not be processed + input := []byte(`{"messages":[{"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_123","content":"plain string result"}]}]}`) + out := applyClaudeToolPrefix(input, "proxy_") + got := gjson.GetBytes(out, "messages.0.content.0.content").String() + if got != "plain string result" { + t.Fatalf("string content should remain unchanged = %q", got) + } +}