Skip to content

feat: add ANTHROPIC_BASE_URL proxy support with optional TLS skip - #20

Merged
cemalturkcan merged 2 commits into
cemalturkcan:mainfrom
Steffen025:feat/proxy-support
Apr 10, 2026
Merged

feat: add ANTHROPIC_BASE_URL proxy support with optional TLS skip#20
cemalturkcan merged 2 commits into
cemalturkcan:mainfrom
Steffen025:feat/proxy-support

Conversation

@Steffen025

Copy link
Copy Markdown
Contributor

Summary

  • Adds ANTHROPIC_BASE_URL support to route API requests through a custom proxy/gateway
  • Adds ANTHROPIC_INSECURE to optionally skip TLS verification (double-gated: only active when ANTHROPIC_BASE_URL is also set)
  • New src/proxy.ts module with 24 tests

Problem

Users behind API gateways, corporate proxies, or monitoring layers (e.g. LiteLLM, custom logging proxies) cannot use the plugin because all requests go directly to api.anthropic.com. There's no way to reroute traffic through an intermediary.

Solution

Two opt-in environment variables:

Variable Purpose Example
ANTHROPIC_BASE_URL Override the API origin (protocol + host) https://proxy.internal:8443
ANTHROPIC_INSECURE Skip TLS verification (for self-signed certs) 1 or true

Safety design

  • ANTHROPIC_INSECURE is double-gated — it has no effect unless ANTHROPIC_BASE_URL is also set. This prevents accidentally disabling TLS against the production Anthropic API.
  • URLs with embedded credentials (user:pass@host) are rejected.
  • Non-HTTP(S) protocols are rejected.
  • When neither variable is set, behavior is identical to before — zero impact on existing users.

Integration

The change to fetch.ts is minimal (4 lines):

// Before:
const reqInput = addBetaParam(input);
// ...
let response = await fetch(reqInput, { ...init, body, headers: reqHeaders });

// After:
const reqInput = rewriteOrigin(addBetaParam(input));
// ...
let response = await fetch(reqInput, {
  ...init, body, headers: reqHeaders,
  ...(isInsecure() && { tls: { rejectUnauthorized: false } }),
} as RequestInit);

New module: src/proxy.ts

Function Purpose
resolveBaseUrl() Parse and validate ANTHROPIC_BASE_URL
isInsecure() Check if TLS skip is active (requires both env vars)
rewriteOrigin() Rewrite request origin preserving path and query

Tests

24 new tests covering:

  • URL parsing (valid HTTPS, HTTP, invalid, empty, whitespace)
  • Credential rejection, unsupported protocols
  • TLS gating (requires both env vars)
  • Origin rewriting for string, URL, and Request inputs
  • Path/query preservation, no-op when same origin

All 115 tests pass (91 existing + 24 new):

bun test v1.3.10
 115 pass
 0 fail
 149 expect() calls
Ran 115 tests across 7 files.

Add proxy module (src/proxy.ts) with three utilities:
- resolveBaseUrl(): parses ANTHROPIC_BASE_URL, rejects non-HTTP protocols
  and URLs with embedded credentials
- isInsecure(): reads ANTHROPIC_INSECURE, only active when BASE_URL is set
- rewriteOrigin(): rewrites request origin while preserving path and query

Integration into fetch.ts is minimal:
- rewriteOrigin() wraps addBetaParam() to rewrite the request origin
- isInsecure() conditionally adds Bun's tls option to skip TLS verification

Both features are opt-in via environment variables and have zero impact
when unset. Useful for users routing through LiteLLM, API gateways,
monitoring proxies, or corporate environments with custom endpoints.

Includes 24 tests covering URL parsing, credential rejection, TLS gating,
origin rewriting for string/URL/Request inputs, and no-op behavior.

@cemalturkcan cemalturkcan left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things to fix before merging:

1. Retry path drops the TLS skip

Initial fetch gets tls: { rejectUnauthorized: false } but handleRetryableError doesn't carry it through. First request works against a self-signed proxy, retry breaks.

2. Path in base URL gets silently ignored

ANTHROPIC_BASE_URL=https://proxy.company.com/anthropic the /anthropic part disappears. Requests go to https://proxy.company.com/v1/messages with no warning. Either join the path or reject URLs with a path and log why.

3. Silent catch in rewriteOrigin

Catch block returns original input without logging. If rewriting fails, requests silently go to api.anthropic.com. Add a log.warn.

4. No startup log when proxy is active

Only debug-level per-request log. Should have an info/warn at startup when proxy is configured.

5. Type cast

as RequestInit hides the Bun-specific tls property. Use BunFetchRequestInit.

@cemalturkcan cemalturkcan left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(

- Carry TLS skip option through retry path in handleRetryableError
- Reject ANTHROPIC_BASE_URL with path component (would be silently dropped)
- Log warning when rewriteOrigin fails instead of silent fallthrough
- Log proxy config at startup (info level) when ANTHROPIC_BASE_URL is set
- Replace 'as RequestInit' with explicit BunFetchRequestInit type
- Cache resolveBaseUrl result for process lifetime (avoid repeated parsing)
- Add resetProxyCache() for test isolation
@Steffen025

Copy link
Copy Markdown
Contributor Author

All five points addressed:

  1. Retry TLStlsOpts extracted and passed through to handleRetryableError
  2. Path in base URL — URLs with a path are now rejected with a log hint suggesting the origin-only form
  3. Silent catchrewriteOrigin now logs a warning on failure
  4. Startup loglog.info("Proxy configured", { baseUrl, insecure }) on first resolve
  5. Type cast — Introduced BunFetchRequestInit type instead of as RequestInit

Also cached resolveBaseUrl() result for the process lifetime and added resetProxyCache() for test isolation.

116 tests pass, lint clean.

@Steffen025
Steffen025 requested a review from cemalturkcan April 9, 2026 14:57
@cemalturkcan

Copy link
Copy Markdown
Owner

Looks good

@cemalturkcan
cemalturkcan merged commit ffdc996 into cemalturkcan:main Apr 10, 2026
1 check passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.5.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants