OAuth stealth mode: Claude Code headers + tool name mapping

This commit is contained in:
Markov 2026-02-23 10:19:10 +01:00
parent 911bfe78b1
commit 4ca47d1918

View File

@ -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", {})
})