fix(claude): centralize oauth tool-name transform flow

This commit is contained in:
Enzo Lucchesi
2026-04-19 14:36:25 +00:00
committed by edlsh
parent 03ea4e569f
commit fc1ddf365f
2 changed files with 100 additions and 38 deletions
+36 -38
View File
@@ -191,14 +191,8 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
bodyForUpstream := body
oauthToken := isClaudeOAuthToken(apiKey)
var oauthToolNamesReverseMap map[string]string
if oauthToken && !auth.ToolPrefixDisabled() {
bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix)
}
// Remap third-party tool names to Claude Code equivalents and remove
// tools without official counterparts. This prevents Anthropic from
// fingerprinting the request as third-party via tool naming patterns.
if oauthToken {
bodyForUpstream, oauthToolNamesReverseMap = remapOAuthToolNames(bodyForUpstream)
bodyForUpstream, oauthToolNamesReverseMap = prepareClaudeOAuthToolNamesForUpstream(bodyForUpstream, claudeToolPrefix, auth.ToolPrefixDisabled())
}
// Enable cch signing by default for OAuth tokens (not just experimental flag).
// Claude Code always computes cch; missing or invalid cch is a detectable fingerprint.
@@ -292,13 +286,7 @@ func (e *ClaudeExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
} else {
reporter.Publish(ctx, helps.ParseClaudeUsage(data))
}
if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() {
data = stripClaudeToolPrefixFromResponse(data, claudeToolPrefix)
}
// Reverse the OAuth tool name remap so the downstream client sees original names.
if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 {
data = reverseRemapOAuthToolNames(data, oauthToolNamesReverseMap)
}
data = restoreClaudeOAuthToolNamesFromResponse(data, claudeToolPrefix, auth.ToolPrefixDisabled(), oauthToolNamesReverseMap)
var param any
out := sdktranslator.TranslateNonStream(
ctx,
@@ -373,14 +361,8 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
bodyForUpstream := body
oauthToken := isClaudeOAuthToken(apiKey)
var oauthToolNamesReverseMap map[string]string
if oauthToken && !auth.ToolPrefixDisabled() {
bodyForUpstream = applyClaudeToolPrefix(body, claudeToolPrefix)
}
// Remap third-party tool names to Claude Code equivalents and remove
// tools without official counterparts. This prevents Anthropic from
// fingerprinting the request as third-party via tool naming patterns.
if oauthToken {
bodyForUpstream, oauthToolNamesReverseMap = remapOAuthToolNames(bodyForUpstream)
bodyForUpstream, oauthToolNamesReverseMap = prepareClaudeOAuthToolNamesForUpstream(bodyForUpstream, claudeToolPrefix, auth.ToolPrefixDisabled())
}
// Enable cch signing by default for OAuth tokens (not just experimental flag).
if oauthToken || experimentalCCHSigningEnabled(e.cfg, auth) {
@@ -471,12 +453,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
if detail, ok := helps.ParseClaudeStreamUsage(line); ok {
reporter.Publish(ctx, detail)
}
if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() {
line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix)
}
if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 {
line = reverseRemapOAuthToolNamesFromStreamLine(line, oauthToolNamesReverseMap)
}
line = restoreClaudeOAuthToolNamesFromStreamLine(line, claudeToolPrefix, auth.ToolPrefixDisabled(), oauthToolNamesReverseMap)
// Forward the line as-is to preserve SSE format
cloned := make([]byte, len(line)+1)
copy(cloned, line)
@@ -501,12 +478,7 @@ func (e *ClaudeExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
if detail, ok := helps.ParseClaudeStreamUsage(line); ok {
reporter.Publish(ctx, detail)
}
if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() {
line = stripClaudeToolPrefixFromStreamLine(line, claudeToolPrefix)
}
if isClaudeOAuthToken(apiKey) && len(oauthToolNamesReverseMap) > 0 {
line = reverseRemapOAuthToolNamesFromStreamLine(line, oauthToolNamesReverseMap)
}
line = restoreClaudeOAuthToolNamesFromStreamLine(line, claudeToolPrefix, auth.ToolPrefixDisabled(), oauthToolNamesReverseMap)
chunks := sdktranslator.TranslateStream(
ctx,
to,
@@ -556,12 +528,8 @@ func (e *ClaudeExecutor) CountTokens(ctx context.Context, auth *cliproxyauth.Aut
// Extract betas from body and convert to header (for count_tokens too)
var extraBetas []string
extraBetas, body = extractAndRemoveBetas(body)
if isClaudeOAuthToken(apiKey) && !auth.ToolPrefixDisabled() {
body = applyClaudeToolPrefix(body, claudeToolPrefix)
}
// Remap tool names for OAuth token requests to avoid third-party fingerprinting.
if isClaudeOAuthToken(apiKey) {
body, _ = remapOAuthToolNames(body)
body, _ = prepareClaudeOAuthToolNamesForUpstream(body, claudeToolPrefix, auth.ToolPrefixDisabled())
}
url := fmt.Sprintf("%s/v1/messages/count_tokens?beta=true", baseURL)
@@ -1001,6 +969,36 @@ func isClaudeOAuthToken(apiKey string) bool {
return strings.Contains(apiKey, "sk-ant-oat")
}
// prepareClaudeOAuthToolNamesForUpstream applies the Claude OAuth tool-name
// transforms in the same order across request paths. Remap runs before prefixing
// so any future non-empty prefix still composes correctly with the per-request
// reverse map.
func prepareClaudeOAuthToolNamesForUpstream(body []byte, prefix string, prefixDisabled bool) ([]byte, map[string]string) {
body, reverseMap := remapOAuthToolNames(body)
if !prefixDisabled {
body = applyClaudeToolPrefix(body, prefix)
}
return body, reverseMap
}
// restoreClaudeOAuthToolNamesFromResponse undoes the Claude OAuth tool-name
// transforms for non-stream responses in reverse order.
func restoreClaudeOAuthToolNamesFromResponse(body []byte, prefix string, prefixDisabled bool, reverseMap map[string]string) []byte {
if !prefixDisabled {
body = stripClaudeToolPrefixFromResponse(body, prefix)
}
return reverseRemapOAuthToolNames(body, reverseMap)
}
// restoreClaudeOAuthToolNamesFromStreamLine undoes the Claude OAuth tool-name
// transforms for SSE lines in reverse order.
func restoreClaudeOAuthToolNamesFromStreamLine(line []byte, prefix string, prefixDisabled bool, reverseMap map[string]string) []byte {
if !prefixDisabled {
line = stripClaudeToolPrefixFromStreamLine(line, prefix)
}
return reverseRemapOAuthToolNamesFromStreamLine(line, reverseMap)
}
// remapOAuthToolNames renames third-party tool names to Claude Code equivalents
// and removes tools without an official counterpart. This prevents Anthropic from
// fingerprinting the request as a third-party client via tool naming patterns.