From 9fe5b485f8765b1c720637c3ffc159f466ccc666 Mon Sep 17 00:00:00 2001 From: Ramiro Rivera Date: Thu, 1 Jan 2026 13:33:40 +0100 Subject: [PATCH 1/2] feat: support ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN for custom endpoints - apps/server/src/providers/claude-provider.ts Add ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN to the environment variable allowlist, enabling use of LLM gateways (LiteLLM, Helicone) and Anthropic- compatible providers (GLM 4.7, Minimax M2.1, etc.). Closes #338 --- apps/server/src/providers/claude-provider.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/server/src/providers/claude-provider.ts b/apps/server/src/providers/claude-provider.ts index 33494535..ee3204f0 100644 --- a/apps/server/src/providers/claude-provider.ts +++ b/apps/server/src/providers/claude-provider.ts @@ -19,6 +19,8 @@ import type { // Only these vars are passed - nothing else from process.env leaks through. const ALLOWED_ENV_VARS = [ 'ANTHROPIC_API_KEY', + 'ANTHROPIC_BASE_URL', + 'ANTHROPIC_AUTH_TOKEN', 'PATH', 'HOME', 'SHELL', From d2f64f10fff11f796a64c15295f8f00e23b68ebe Mon Sep 17 00:00:00 2001 From: Ramiro Rivera Date: Thu, 1 Jan 2026 13:43:12 +0100 Subject: [PATCH 2/2] test: add tests for ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN passthrough - apps/server/tests/unit/providers/claude-provider.test.ts Verify custom endpoint environment variables are passed to the SDK. --- .../unit/providers/claude-provider.test.ts | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/apps/server/tests/unit/providers/claude-provider.test.ts b/apps/server/tests/unit/providers/claude-provider.test.ts index 3dbd9982..e9afa1c0 100644 --- a/apps/server/tests/unit/providers/claude-provider.test.ts +++ b/apps/server/tests/unit/providers/claude-provider.test.ts @@ -12,6 +12,8 @@ describe('claude-provider.ts', () => { vi.clearAllMocks(); provider = new ClaudeProvider(); delete process.env.ANTHROPIC_API_KEY; + delete process.env.ANTHROPIC_BASE_URL; + delete process.env.ANTHROPIC_AUTH_TOKEN; }); describe('getName', () => { @@ -286,6 +288,93 @@ describe('claude-provider.ts', () => { }); }); + describe('environment variable passthrough', () => { + afterEach(() => { + delete process.env.ANTHROPIC_BASE_URL; + delete process.env.ANTHROPIC_AUTH_TOKEN; + }); + + it('should pass ANTHROPIC_BASE_URL to SDK env', async () => { + process.env.ANTHROPIC_BASE_URL = 'https://custom.example.com/v1'; + + vi.mocked(sdk.query).mockReturnValue( + (async function* () { + yield { type: 'text', text: 'test' }; + })() + ); + + const generator = provider.executeQuery({ + prompt: 'Test', + cwd: '/test', + }); + + await collectAsyncGenerator(generator); + + expect(sdk.query).toHaveBeenCalledWith({ + prompt: 'Test', + options: expect.objectContaining({ + env: expect.objectContaining({ + ANTHROPIC_BASE_URL: 'https://custom.example.com/v1', + }), + }), + }); + }); + + it('should pass ANTHROPIC_AUTH_TOKEN to SDK env', async () => { + process.env.ANTHROPIC_AUTH_TOKEN = 'custom-auth-token'; + + vi.mocked(sdk.query).mockReturnValue( + (async function* () { + yield { type: 'text', text: 'test' }; + })() + ); + + const generator = provider.executeQuery({ + prompt: 'Test', + cwd: '/test', + }); + + await collectAsyncGenerator(generator); + + expect(sdk.query).toHaveBeenCalledWith({ + prompt: 'Test', + options: expect.objectContaining({ + env: expect.objectContaining({ + ANTHROPIC_AUTH_TOKEN: 'custom-auth-token', + }), + }), + }); + }); + + it('should pass both custom endpoint vars together', async () => { + process.env.ANTHROPIC_BASE_URL = 'https://gateway.example.com'; + process.env.ANTHROPIC_AUTH_TOKEN = 'gateway-token'; + + vi.mocked(sdk.query).mockReturnValue( + (async function* () { + yield { type: 'text', text: 'test' }; + })() + ); + + const generator = provider.executeQuery({ + prompt: 'Test', + cwd: '/test', + }); + + await collectAsyncGenerator(generator); + + expect(sdk.query).toHaveBeenCalledWith({ + prompt: 'Test', + options: expect.objectContaining({ + env: expect.objectContaining({ + ANTHROPIC_BASE_URL: 'https://gateway.example.com', + ANTHROPIC_AUTH_TOKEN: 'gateway-token', + }), + }), + }); + }); + }); + describe('getAvailableModels', () => { it('should return 4 Claude models', () => { const models = provider.getAvailableModels();