From 4ca47d191850a0b3646b957f76814fa0fbb9c1ca Mon Sep 17 00:00:00 2001 From: Markov Date: Mon, 23 Feb 2026 10:19:10 +0100 Subject: [PATCH] OAuth stealth mode: Claude Code headers + tool name mapping --- picogent/providers/anthropic.py | 37 +++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/picogent/providers/anthropic.py b/picogent/providers/anthropic.py index 3b75bd8..a12ac9a 100644 --- a/picogent/providers/anthropic.py +++ b/picogent/providers/anthropic.py @@ -10,6 +10,13 @@ from .base import BaseProvider logger = logging.getLogger("picogent.anthropic") +# Claude Code tool name mapping (lowercase -> CC canonical) +CLAUDE_CODE_TOOLS = { + "read": "Read", "write": "Write", "edit": "Edit", "bash": "Bash", + "grep": "Grep", "glob": "Glob", +} +CC_TOOL_REVERSE = {v.lower(): k for k, v in CLAUDE_CODE_TOOLS.items()} + class AnthropicProvider(BaseProvider): """Anthropic Claude provider using direct API calls""" @@ -18,6 +25,7 @@ class AnthropicProvider(BaseProvider): super().__init__(api_key, model) self.base_url = "https://api.anthropic.com/v1" self.anthropic_version = "2023-06-01" + self.is_oauth = api_key.startswith("sk-ant-oat") async def generate_response( self, @@ -28,12 +36,19 @@ class AnthropicProvider(BaseProvider): ) -> Dict[str, Any]: """Generate response using Anthropic Messages API""" - # OAuth tokens (sk-ant-oat*) use Bearer auth, regular keys use x-api-key - if self.api_key.startswith("sk-ant-oat"): + is_oauth = self.api_key.startswith("sk-ant-oat") + + if is_oauth: + # OAuth tokens: Bearer auth + Claude Code stealth headers headers = { "Content-Type": "application/json", + "Accept": "application/json", "Authorization": f"Bearer {self.api_key}", - "anthropic-version": self.anthropic_version + "anthropic-version": self.anthropic_version, + "anthropic-beta": "claude-code-20250219,oauth-2025-04-20", + "anthropic-dangerous-direct-browser-access": "true", + "User-Agent": "claude-cli/2.1.2 (external, cli)", + "x-app": "cli", } else: headers = { @@ -49,9 +64,15 @@ class AnthropicProvider(BaseProvider): "messages": messages } - # Add tools if provided + # Add tools if provided (rename for OAuth/Claude Code stealth) if tools: - payload["tools"] = tools + if is_oauth: + payload["tools"] = [ + {**t, "name": CLAUDE_CODE_TOOLS.get(t["name"], t["name"])} + for t in tools + ] + else: + payload["tools"] = tools logger.info(f"POST {self.base_url}/messages | model={self.model} | key={self.api_key[:8]}...{self.api_key[-4:]} (len={len(self.api_key)})") logger.debug(f"Headers: x-api-key={self.api_key[:8]}..., anthropic-version={self.anthropic_version}") @@ -84,9 +105,13 @@ class AnthropicProvider(BaseProvider): if block.get("type") == "text": text_parts.append(block.get("text", "")) elif block.get("type") == "tool_use": + name = block.get("name") + # Map Claude Code names back to original + if self.is_oauth: + name = CC_TOOL_REVERSE.get(name.lower(), name) tool_calls.append({ "id": block.get("id"), - "name": block.get("name"), + "name": name, "input": block.get("input", {}) })