Skip to content

Commit 0b7b235

Browse files
committed
Add authentication and host configuration to Anthropic proxy
Change-Id: If6f97c548b00d040beccf17e207c389bcaef3be8 Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent 4050bf6 commit 0b7b235

File tree

2 files changed

+43
-5
lines changed

2 files changed

+43
-5
lines changed

src/anthropic-proxy.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import { providerizeSchema } from "./json-schema";
1515
export type CreateAnthropicProxyOptions = {
1616
providers: Record<string, ProviderV2>;
1717
port?: number;
18+
host?: string; // default: 127.0.0.1
19+
authToken?: string; // if set, require matching custom header
20+
authHeaderName?: string; // if authToken is set, required header name (default: X-AnyClaude-Token)
1821
};
1922

2023
// createAnthropicProxy creates a proxy server that accepts
@@ -23,10 +26,25 @@ export type CreateAnthropicProxyOptions = {
2326
// to the Anthropic Message API format.
2427
export const createAnthropicProxy = ({
2528
port,
29+
host = "127.0.0.1",
2630
providers,
31+
authToken,
32+
authHeaderName = "X-AnyClaude-Token",
2733
}: CreateAnthropicProxyOptions): string => {
2834
const proxy = http
2935
.createServer((req, res) => {
36+
// Basic auth gate: require matching token via a custom header
37+
if (authToken) {
38+
const key = authHeaderName.toLowerCase();
39+
const headerVal = req.headers[key];
40+
const provided = Array.isArray(headerVal) ? headerVal[0] : headerVal;
41+
if (!provided || provided !== authToken) {
42+
res.writeHead(401, { "Content-Type": "application/json" });
43+
res.end(JSON.stringify({ error: "Unauthorized" }));
44+
return;
45+
}
46+
}
47+
3048
if (!req.url) {
3149
res.writeHead(400, {
3250
"Content-Type": "application/json",
@@ -222,7 +240,7 @@ export const createAnthropicProxy = ({
222240
);
223241
});
224242
})
225-
.listen(port ?? 0);
243+
.listen(port ?? 0, host);
226244

227245
const address = proxy.address();
228246
if (!address) {
@@ -231,5 +249,5 @@ export const createAnthropicProxy = ({
231249
if (typeof address === "string") {
232250
return address;
233251
}
234-
return `http://localhost:${address.port}`;
252+
return `http://${host === "127.0.0.1" ? "localhost" : host}:${address.port}`;
235253
};

src/main.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createGoogleGenerativeAI } from "@ai-sdk/google";
66
import { createOpenAI } from "@ai-sdk/openai";
77
import { createXai } from "@ai-sdk/xai";
88
import { spawn } from "child_process";
9+
import { randomUUID } from "crypto";
910
import {
1011
createAnthropicProxy,
1112
type CreateAnthropicProxyOptions,
@@ -42,28 +43,47 @@ if (process.env.ANTHROPIC_API_KEY) {
4243
});
4344
}
4445

46+
// Authentication token for the proxy. If the user already provided an
47+
// ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY, reuse it so Claude sends it as
48+
// Authorization: Bearer / X-Api-Key. Otherwise generate an ephemeral token and
49+
// inject it only into the spawned Claude process.
50+
const proxyAuthToken =
51+
process.env.ANTHROPIC_AUTH_TOKEN ??
52+
process.env.ANTHROPIC_API_KEY ??
53+
randomUUID();
54+
55+
const proxyAuthHeaderName = process.env.ANYCLAUDE_AUTH_HEADER ?? "X-AnyClaude-Token";
56+
4557
const proxyURL = createAnthropicProxy({
4658
providers,
59+
host: "127.0.0.1",
60+
authToken: proxyAuthToken,
61+
authHeaderName: proxyAuthHeaderName,
4762
});
4863

4964
if (process.env.PROXY_ONLY === "true") {
50-
console.log("Proxy only mode: "+proxyURL);
65+
console.log("Proxy only mode: " + proxyURL);
5166
} else {
5267
const claudeArgs = process.argv.slice(2);
5368
const proc = spawn("claude", claudeArgs, {
5469
env: {
5570
...process.env,
5671
ANTHROPIC_BASE_URL: proxyURL,
72+
// Ensure the Claude CLI includes our custom header via ANTHROPIC_CUSTOM_HEADERS
73+
ANTHROPIC_CUSTOM_HEADERS: (() => {
74+
const existing = process.env.ANTHROPIC_CUSTOM_HEADERS;
75+
const line = `${proxyAuthHeaderName}: ${proxyAuthToken}`;
76+
return existing ? `${existing}\n${line}` : line;
77+
})(),
5778
},
5879
stdio: "inherit",
5980
});
6081
proc.on("exit", (code) => {
6182
if (claudeArgs[0] === "-h" || claudeArgs[0] === "--help") {
6283
console.log("\nCustom Models:")
63-
console.log(" --model <provider>/<model> e.g. openai/o3");
84+
console.log(" --model <provider>/<model> e.g. openai/gpt-5-mini");
6485
}
6586

6687
process.exit(code);
6788
});
6889
}
69-

0 commit comments

Comments
 (0)