From 5dc936a9a45b459eb6a2a950492f24a5b4f39f0f Mon Sep 17 00:00:00 2001 From: Skyuno Date: Wed, 28 Jan 2026 23:49:02 +0800 Subject: [PATCH 1/2] fix: filter out web_search/websearch tools unsupported by Kiro API --- .../translator/kiro/claude/kiro_claude_request.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/internal/translator/kiro/claude/kiro_claude_request.go b/internal/translator/kiro/claude/kiro_claude_request.go index f92be9d5..6edbd3d0 100644 --- a/internal/translator/kiro/claude/kiro_claude_request.go +++ b/internal/translator/kiro/claude/kiro_claude_request.go @@ -17,7 +17,6 @@ import ( "github.com/tidwall/gjson" ) - // Kiro API request structs - field order determines JSON key order // KiroPayload is the top-level request structure for Kiro API @@ -34,7 +33,6 @@ type KiroInferenceConfig struct { TopP float64 `json:"topP,omitempty"` } - // KiroConversationState holds the conversation context type KiroConversationState struct { ChatTriggerType string `json:"chatTriggerType"` // Required: "MANUAL" - must be first field @@ -378,7 +376,6 @@ func hasThinkingTagInBody(body []byte) bool { return strings.Contains(bodyStr, "") || strings.Contains(bodyStr, "") } - // IsThinkingEnabledFromHeader checks if thinking mode is enabled via Anthropic-Beta header. // Claude CLI uses "Anthropic-Beta: interleaved-thinking-2025-05-14" to enable thinking. func IsThinkingEnabledFromHeader(headers http.Header) bool { @@ -518,6 +515,15 @@ func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { for _, tool := range tools.Array() { name := tool.Get("name").String() + + // Filter out web_search/websearch tools (Kiro API doesn't support them) + // This matches the behavior in AIClient-2-API/claude-kiro.js + nameLower := strings.ToLower(name) + if nameLower == "web_search" || nameLower == "websearch" { + log.Debugf("kiro: skipping unsupported tool: %s", name) + continue + } + description := tool.Get("description").String() inputSchemaResult := tool.Get("input_schema") var inputSchema interface{} From 3c7a5afdcca5e09e3ee9bea438e57bdd0d6c511d Mon Sep 17 00:00:00 2001 From: Skyuno Date: Sat, 31 Jan 2026 23:54:31 +0800 Subject: [PATCH 2/2] feat: inject web_search alternative hint instead of silently filtering --- .../kiro/claude/kiro_claude_request.go | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/internal/translator/kiro/claude/kiro_claude_request.go b/internal/translator/kiro/claude/kiro_claude_request.go index 6edbd3d0..c04ee939 100644 --- a/internal/translator/kiro/claude/kiro_claude_request.go +++ b/internal/translator/kiro/claude/kiro_claude_request.go @@ -217,7 +217,26 @@ func BuildKiroPayload(claudeBody []byte, modelID, profileArn, origin string, isA } // Convert Claude tools to Kiro format - kiroTools := convertClaudeToolsToKiro(tools) + kiroTools, hasWebSearch := convertClaudeToolsToKiro(tools) + + // If web_search was requested but filtered, inject alternative hint + if hasWebSearch { + webSearchHint := `[CRITICAL WEB ACCESS INSTRUCTION] +You have the Fetch/read_url_content tool available. When the user asks about current events, weather, news, or any information that requires web access: +- DO NOT say you cannot search the web +- DO NOT refuse to help with web-related queries +- IMMEDIATELY use the Fetch tool to access relevant URLs +- Use well-known official websites, documentation sites, or API endpoints +- Construct appropriate URLs based on the query context + +IMPORTANT: Always attempt to fetch information FIRST before declining. You CAN access the web via Fetch.` + if systemPrompt != "" { + systemPrompt += "\n" + webSearchHint + } else { + systemPrompt = webSearchHint + } + log.Infof("kiro: injected web_search alternative hint (tool was filtered)") + } // Thinking mode implementation: // Kiro API supports official thinking/reasoning mode via tag. @@ -506,11 +525,13 @@ func ensureKiroInputSchema(parameters interface{}) interface{} { } } -// convertClaudeToolsToKiro converts Claude tools to Kiro format -func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { +// convertClaudeToolsToKiro converts Claude tools to Kiro format. +// Returns the converted tools and a boolean indicating if web_search was filtered. +func convertClaudeToolsToKiro(tools gjson.Result) ([]KiroToolWrapper, bool) { var kiroTools []KiroToolWrapper + hasWebSearch := false if !tools.IsArray() { - return kiroTools + return kiroTools, hasWebSearch } for _, tool := range tools.Array() { @@ -521,6 +542,7 @@ func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { nameLower := strings.ToLower(name) if nameLower == "web_search" || nameLower == "websearch" { log.Debugf("kiro: skipping unsupported tool: %s", name) + hasWebSearch = true continue } @@ -567,7 +589,7 @@ func convertClaudeToolsToKiro(tools gjson.Result) []KiroToolWrapper { // This prevents 500 errors when Claude Code sends too many tools kiroTools = compressToolsIfNeeded(kiroTools) - return kiroTools + return kiroTools, hasWebSearch } // processMessages processes Claude messages and builds Kiro history