Skip to content

Commit c6e5197

Browse files
committed
fix: conditionally add claude-code-20250219 beta header based on tools presence
Claude Code only includes claude-code-20250219 in anthropic-beta header when tools are present in the request. Updated getBetaHeadersForPath to accept hasTools parameter and modified handleAnthropicRequest and chat.params hook to check tools presence before setting headers.
1 parent 0a562e3 commit c6e5197

File tree

1 file changed

+31
-21
lines changed

1 file changed

+31
-21
lines changed

index.mjs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,13 @@ function applyStainlessHeaders(headers, isStream = false) {
153153
}
154154
}
155155

156-
function getBetaHeadersForPath(pathname) {
156+
function getBetaHeadersForPath(pathname, hasTools = false) {
157157
if (pathname === "/v1/messages") {
158-
return ["claude-code-20250219", "oauth-2025-04-20", "interleaved-thinking-2025-05-14"];
158+
// Claude Code only includes claude-code-20250219 when tools are present
159+
if (hasTools) {
160+
return ["claude-code-20250219", "oauth-2025-04-20", "interleaved-thinking-2025-05-14"];
161+
}
162+
return ["oauth-2025-04-20", "interleaved-thinking-2025-05-14"];
159163
}
160164
if (pathname === "/v1/messages/count_tokens") {
161165
return [
@@ -604,14 +608,6 @@ async function handleAnthropicRequest(input, init, auth, baseFetch) {
604608

605609
const requestHeaders = mergeHeaders(input instanceof Request ? input : null, init);
606610

607-
// Beta headers
608-
const betaHeaders = getBetaHeadersForPath(requestUrl.pathname);
609-
if (betaHeaders.length > 0) {
610-
requestHeaders.set("anthropic-beta", betaHeaders.join(","));
611-
} else {
612-
requestHeaders.delete("anthropic-beta");
613-
}
614-
615611
// Auth & stainless headers
616612
requestHeaders.set("authorization", `Bearer ${auth.access}`);
617613
requestHeaders.delete("x-api-key");
@@ -630,10 +626,14 @@ async function handleAnthropicRequest(input, init, auth, baseFetch) {
630626
}
631627

632628
let isStream = false;
629+
let hasTools = false;
633630
if (body && typeof body === "string") {
634631
try {
632+
const parsed = JSON.parse(body);
633+
// Check if tools array has items BEFORE normalization
634+
hasTools = Array.isArray(parsed.tools) && parsed.tools.length > 0;
635635
const result = await normalizeRequestBody(
636-
JSON.parse(body),
636+
parsed,
637637
requestUrl.pathname === "/v1/messages",
638638
);
639639
body = JSON.stringify(result.body);
@@ -643,6 +643,14 @@ async function handleAnthropicRequest(input, init, auth, baseFetch) {
643643
}
644644
}
645645

646+
// Set beta headers AFTER body parsing to know if tools are present
647+
const betaHeaders = getBetaHeadersForPath(requestUrl.pathname, hasTools);
648+
if (betaHeaders.length > 0) {
649+
requestHeaders.set("anthropic-beta", betaHeaders.join(","));
650+
} else {
651+
requestHeaders.delete("anthropic-beta");
652+
}
653+
646654
applyStainlessHeaders(requestHeaders, isStream);
647655

648656
// Beta query param
@@ -894,12 +902,22 @@ export async function AnthropicAuthPlugin({ client }) {
894902
const options = output.options ?? {};
895903
output.options = options;
896904

897-
// Headers
905+
// Tools & messages - match Claude Code behavior
906+
// Must normalize tools BEFORE setting headers (to determine hasTools)
907+
const hasTools = Array.isArray(options.tools) && options.tools.length > 0;
908+
if (options.tools) {
909+
options.tools = normalizeTools(options.tools);
910+
} else {
911+
options.tools = [];
912+
}
913+
if (Array.isArray(options.messages)) options.messages = normalizeMessagesForClaude(options.messages);
914+
915+
// Headers - set AFTER knowing if tools are present
898916
const headers = options.headers instanceof Headers
899917
? options.headers
900918
: new Headers(options.headers ?? {});
901919

902-
const betaHeaders = getBetaHeadersForPath("/v1/messages");
920+
const betaHeaders = getBetaHeadersForPath("/v1/messages", hasTools);
903921
headers.set("anthropic-beta", betaHeaders.join(","));
904922
applyStainlessHeaders(headers, !!options.stream);
905923

@@ -916,14 +934,6 @@ export async function AnthropicAuthPlugin({ client }) {
916934
options.model = normalizeModelId(options.model ?? input.model?.id);
917935
}
918936

919-
// Tools & messages - match Claude Code behavior
920-
if (options.tools) {
921-
options.tools = normalizeTools(options.tools);
922-
} else {
923-
options.tools = [];
924-
}
925-
if (Array.isArray(options.messages)) options.messages = normalizeMessagesForClaude(options.messages);
926-
927937
// Remove temperature - Claude Code doesn't send it
928938
if ("temperature" in options) delete options.temperature;
929939

0 commit comments

Comments
 (0)