@cle-does-things/fuseproxy
v0.3.2
Published
A lightweight, observable proxy for Anthropic's Messages API.
Downloads
924
Maintainers
Readme
fuseproxy
A lightweight, observable proxy for Anthropic's POST /v1/messages API.
fuseproxy sits between your internal clients and Anthropic, validates inbound requests, forwards them to Anthropic, and emits tracing + generation telemetry to Langfuse via OpenTelemetry.
What It Does
- Exposes
POST /v1/messagesonhttp://localhost:5678 - Requires bearer auth using an internal API key (
INTERNAL_API_KEY) - Validates request bodies with Zod before forwarding
- Supports both non-streaming and streaming responses
- Streams Anthropic events as Server-Sent Events (SSE) when
stream=true - Records spans/generations and token/cost metadata to Langfuse
Tech Stack
- Runtime: Bun + TypeScript
- HTTP server: Express 5
- LLM provider:
@anthropic-ai/sdk - Observability: OpenTelemetry (
@opentelemetry/sdk-node) + Langfuse (@langfuse/otel,@langfuse/tracing) - Validation: Zod
- Logging: tslog
Project Structure
src/index.ts: app entrypointsrc/api.ts: Express server and/v1/messagesroutesrc/types.ts: request schema + model pricing tablesrc/instrumentation.ts: OpenTelemetry/Langfuse setupsrc/logger.ts: structured logger with trace/span IDscompose.yaml: local Langfuse stack (web, worker, postgres, redis, clickhouse, minio)
Prerequisites
- Bun installed
- Anthropic API key
- Docker + Docker Compose (optional, if running local Langfuse)
Environment Variables
Minimum required to run the proxy:
INTERNAL_API_KEY: bearer token your clients must sendANTHROPIC_API_KEY: key used for upstream Anthropic calls (can also be from an Anthopric-compatible provider like Kimi or DeepSeek)
For Langfuse tracing export:
LANGFUSE_BASE_URL(for local stack:http://localhost:3000)LANGFUSE_PUBLIC_KEYLANGFUSE_SECRET_KEY
Optional:
DEPLOYMENT_ENVIRONMENT(recorded in tracing metadata)LOG_LEVEL(silly|trace|debug|info|warn|error|fatal, defaultdebug)ANTHROPIC_BASE_URL(Base URL for the Anthropic API, supports all Anthropic-compatible providers, like Kimi or DeepSeek)
Install
From NPM:
npm install -g @cle-does-things/fuseproxyFrom source:
git clone https://github.com/AstraBert/fuseproxy
cd fuseproxy
bun installRun
If installed with NPM:
fuseproxy runWith npx:
npx @cle-does-things/fuseproxy runIf installed from source:
bun run startServer listens on http://localhost:5678.
Example Request
curl -X POST http://localhost:5678/v1/messages \
-H "Authorization: Bearer $INTERNAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 256,
"system": "You are concise.",
"messages": [{"role":"user","content":"Say hello in one sentence."}],
"stream": false
}'Integrations
GitHub Copilot CLI
Follow these steps to configure Copilot to route its LLM requests through fuseproxy:
- Start the proxy:
bun run start- In another shell, load Copilot env vars:
export COPILOT_PROVIDER_TYPE="anthropic" && \
export COPILOT_PROVIDER_BEARER_TOKEN="<internal fuseproxy bearer token>" && \
export COPILOT_MODEL="claude-opus-4-7" && \
export COPILOT_PROVIDER_BASE_URL=http://localhost:5678 && \
export COPILOT_PROVIDER_MAX_OUTPUT_TOKENS=32000 && \
export COPILOT_PROVIDER_MAX_PROMPT_TOKENS=64000- Confirm values are set:
echo "$COPILOT_PROVIDER_TYPE"
echo "$COPILOT_PROVIDER_BASE_URL"
echo "$COPILOT_MODEL"Important:
COPILOT_PROVIDER_BEARER_TOKENmust matchINTERNAL_API_KEYused by the proxy, or requests will return401 Unauthorized.
Pi Agents Setup
You can set up fuseproxy for your Pi agents by adding this to your ~/.pi/agent/models.json config file:
{
"providers": {
"fuseproxy": {
"baseUrl": "http://localhost:5678",
"api": "anthropic-messages",
"apiKey": "fuseproxy", // <- not needed, just a placeholder
"headers": {
"x-api-key": "YOUR_INTERNAL_API_KEY" // <- must match with the internal API key
},
"models": [
{
"id": "claude-opus-4-7",
"name": "Claude Opus 4.7",
"reasoning": true,
"input": ["text", "image"],
"contextWindow": 64000,
"maxTokens": 32000,
"cost": {
"input": 5,
"output": 25,
"cacheRead": 0.5,
"cacheWrite": 6.25
}
},
{
"id": "kimi-k2.6",
"name": "Kimi K2.6",
"reasoning": true,
"input": ["text"],
"contextWindow": 64000,
"maxTokens": 32000,
"cost": {
"input": 0.95,
"output": 4,
"cacheRead": 0.16,
"cacheWrite": 0
}
}
]
}
}
}Streaming Behavior
When stream=true, the service:
- Sets
Content-Type: text/event-stream - Forwards Anthropic stream events as SSE (
event: <type>,data: <json>) - Emits an
errorevent and closes the stream on failure
Local Langfuse Stack (Optional)
A full local Langfuse stack is provided in compose.yaml.
- Review
compose.yamland replace every# CHANGEMEsecret. - Start services:
docker compose up -d- Open Langfuse at
http://localhost:3000.
Default exposed ports include:
3000: Langfuse web3030: Langfuse worker9090: MinIO S3 endpoint
Development Commands
bun run lint
bun run prettier
bun run prettier:checkNotes
- The proxy currently implements Anthropic-style
/v1/messagesonly. src/types.tsincludes aMODEL_COSTStable used to estimate generation cost metadata for traces.- Graceful shutdown flushes Langfuse spans on
SIGINT/SIGTERM.
