Apuntar Claude Code a GPT y a modelos locales de Ollama con un proxy
Redirige ANTHROPIC_BASE_URL a un proxy de traducción, prueba con un backend de pago, cambia a modelos locales gratuitos y descubre qué se rompe y cómo repartir las tareas con sensatez.
Si pagas varias suscripciones de IA, seguramente te has pillado abriendo otra terminal o pestaña del navegador solo para darle una tarea a otro modelo, porque Claude Code solo habla con Claude. El selector de modelos que trae parece prometedor, pero su desplegable no muestra nada aparte de los modelos de Anthropic. Hay quien ha creado pequeños plugins de pasarela que redirigen su tráfico a otros proveedores sin hacer ruido, y resulta que el truco que hay detrás no tiene nada de exótico. Esto es lo que hace falta para montarlo, lo que se rompió por el camino y el esquema que se quedó.
Por qué esto no es un hack
Claude Code, como la mayoría de los agentes de línea de comandos basados en el SDK de Anthropic, no lleva api.anthropic.com fijado en el código. Lee ANTHROPIC_BASE_URL, con ANTHROPIC_AUTH_TOKEN para la clave, y envía cada petición a esa dirección. El ajuste existe para empresas que hacen pasar la herramienta por una pasarela interna, un proxy de auditoría o un espejo en su región. No se pensó como interruptor para cambiar de modelo, pero se comporta como tal en cuanto te das cuenta de que el servicio del otro lado no tiene por qué ser Anthropic. Solo tiene que aceptar el formato Messages de Anthropic y responder con la forma que espera Claude Code, incluidos los eventos de streaming y los bloques de llamadas a herramientas.
Así que toda la idea es un pequeño servicio HTTP. Traduce el esquema de petición y respuesta de Anthropic a lo que hable el backend real, normalmente el formato chat completion de OpenAI, porque casi todos los proveedores, locales incluidos, han convergido en él, y luego traduce la respuesta de vuelta:
Claude Code --Anthropic Messages format--> local proxy --OpenAI format--> GPT-5 / Grok / Ollama / whatever
Claude Code <--Anthropic Messages format-- local proxy <--OpenAI format-- (response, tool calls, streaming deltas)
Visto así, preguntarse por qué Claude Code no soporta sin más otros modelos deja de tener interés. Ya los soporta, de forma indirecta, siempre que algo en medio se encargue de la traducción.
Las herramientas disponibles
No hay un ganador evidente. Lo que encuentras es un puñado de proyectos pequeños, casi todos de una sola persona, que resuelven el mismo problema de traducción con distinto alcance. Estos son los que vale la pena comparar:
+---------------------------+--------+------------------------------+------------------------------------------+
| Project | Stack | Backends | Notes |
+---------------------------+--------+------------------------------+------------------------------------------+
| claude-code-proxy | Go | OpenAI, OpenRouter, Ollama | Single binary, pattern-based model |
| (nielspeter) | | | mapping (*opus*/*sonnet*/*haiku*), |
| | | | full streaming + tool call support |
+---------------------------+--------+------------------------------+------------------------------------------+
| claude-code-ollama-proxy | Python | Ollama, OpenAI (fallback), | Built specifically for local-first use, |
| (mattlqx) | / uv | Gemini | uses LiteLLM under the hood for format |
| | | | translation |
+---------------------------+--------+------------------------------+------------------------------------------+
| claude-code-router | Node | OpenAI, Anthropic, Gemini, | The most feature-complete option, ships |
| (musistudio) | | DeepSeek, Kimi, custom | a UI, rule-based routing (background / |
| | | endpoints | think / longContext), retries, failover |
+---------------------------+--------+------------------------------+------------------------------------------+
| LiteLLM proxy | Python | 100+ providers | Not built for Claude Code specifically, |
| (generic) | | | but exposes an Anthropic-compatible route |
| | | | so it works the same way |
+---------------------------+--------+------------------------------+------------------------------------------+
Todos tienen licencia MIT, escuchan en localhost por defecto y hacen el mismo trabajo básico. Lo que cambia es cuánta lógica de enrutado apilan sobre la capa de traducción. Empezar por el más simple es sensato, porque así aprendes la forma de peticiones y respuestas antes de confiar en que el motor de reglas de un router decida por ti.
Primera prueba: un backend de pago
Antes de meter un modelo local, confirma que la fontanería funciona. Por eso la primera ejecución usó OpenAI como backend a través de claude-code-proxy, un programa en Go:
# clone and build (Go toolchain required)
git clone https://github.com/nielspeter/claude-code-proxy.git
cd claude-code-proxy
go build -o claude-code-proxy cmd/claude-code-proxy/main.go
La configuración está en un archivo env normal, donde los niveles de modelo de Claude se asignan a lo que de verdad pagas:
# ~/.claude/proxy.env
OPENAI_API_KEY=sk-...
OPENAI_BASE_URL=https://api.openai.com/v1
# map Claude's model tiers to whatever you're actually paying for
ANTHROPIC_DEFAULT_OPUS_MODEL=gpt-5
ANTHROPIC_DEFAULT_SONNET_MODEL=gpt-5
ANTHROPIC_DEFAULT_HAIKU_MODEL=gpt-5-mini
Arranca el proxy y apunta Claude Code hacia él en lugar del endpoint de Anthropic:
./claude-code-proxy & # listens on localhost:8082 by default
export ANTHROPIC_BASE_URL=http://localhost:8082
claude
No hace falta más. Claude Code arranca como siempre y la interfaz no cambia, pero ahora cada mensaje pasa por GPT-5. Las llamadas a herramientas, las ediciones de archivos y todo el bucle del agente funcionaron sin tocar la configuración propia de Claude Code.
Una sorpresa: este patrón de proxy es anterior a la oleada de plugins de enrutado de modelos de la que se habla. Es la misma idea que hay detrás de la documentación de Anthropic sobre pasarelas empresariales, solo que autoalojada y apuntando a un destino no oficial.
El objetivo real: un modelo local gratuito
Pagar GPT detrás de Claude Code echa por tierra la mitad del propósito. Lo mejor es pasar el aburrido 80% de una sesión a algo gratuito y local, como arreglos de formato, refactorizaciones mecánicas y el test obvio para una función, y reservar los modelos de pago y de más calidad para el trabajo que requiere criterio.
Ollama lo pone casi demasiado fácil, ya que las versiones recientes exponen un endpoint compatible con OpenAI en /v1 sin adaptadores:
# install Ollama if you don't have it: https://ollama.com/download
ollama pull qwen2.5-coder:14b
ollama serve
Luego apunta el mismo proxy hacia él en lugar de OpenAI:
# ~/.claude/proxy.env
OPENAI_BASE_URL=http://localhost:11434/v1
# no API key needed for local Ollama, the field just needs to be non-empty
OPENAI_API_KEY=ollama
ANTHROPIC_DEFAULT_SONNET_MODEL=qwen2.5-coder:14b
ANTHROPIC_DEFAULT_HAIKU_MODEL=qwen2.5-coder:7b
Ejecútalo exactamente igual que antes:
./claude-code-proxy &
export ANTHROPIC_BASE_URL=http://localhost:8082
claude
Sin clave de API, sin panel de facturación y sin que nada salga de la máquina. Si no quieres instalar Ollama de forma nativa, la versión Docker es la misma configuración dentro de un contenedor:
# docker-compose.yml
services:
ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
volumes:
ollama_data:
Después descarga el modelo dentro de él:
docker compose up -d
docker exec -it $(docker compose ps -q ollama) ollama pull qwen2.5-coder:14b
OPENAI_BASE_URL se queda en http://localhost:11434/v1 y no cambia nada más.
Qué salió mal
No todo funcionó a la primera. Estos modos de fallo explican por qué «basta con apuntarlo a un proxy» subestima el esfuerzo:
+-------------------------------------+--------------------------------------------------------------+
| What broke | Why, and what fixed it |
+-------------------------------------+--------------------------------------------------------------+
| Tool calls silently stopped mid-task | Smaller local models (7B/8B) don't reliably emit well-formed |
| | tool-call JSON under Claude Code's system prompt, which is |
| | dense and multi-step. Bumping to a 14B code-tuned model fixed |
| | most of it, not all of it. |
+-------------------------------------+--------------------------------------------------------------+
| Streaming responses arrived as one | The proxy has to translate each OpenAI streaming delta into a |
| big chunk instead of token-by-token | matching Anthropic streaming event. If it buffers instead of |
| | forwarding incrementally, you lose the live-typing feel, it |
| | still works, it just feels laggy. |
+-------------------------------------+--------------------------------------------------------------+
| Claude Code's /model picker didn't | The simple proxies don't hook into the picker UI at all, model |
| show my local model as an option | selection happens entirely through the env file mapping, not |
| | inside Claude Code itself. Only the router-style tools with a |
| | UI (claude-code-router) actually inject entries into /model. |
+-------------------------------------+--------------------------------------------------------------+
| Context got truncated on long | Local models running through Ollama default to a much smaller |
| sessions | context window than the model card advertises unless you |
| | explicitly set `num_ctx` when pulling or running the model. |
+-------------------------------------+--------------------------------------------------------------+
Lo de la ventana de contexto es lo más traicionero. Ollama carga sin problema un modelo anunciado con 128k tokens y luego limita en silencio el contexto real a 2048 o 4096 salvo que lo sobrescribas. Una sesión larga de Claude Code acaba entonces con el modelo olvidando cómo empezó la conversación, sin ningún error. Si vas a hacer algo más que una consulta rápida y puntual, fíjalo explícitamente:
ollama run qwen2.5-coder:14b --keepalive 30m
# or, more durably, in a Modelfile:
# PARAMETER num_ctx 32768
Decidir qué va a dónde
Hacer que la fontanería funcione es el 20% fácil. Lo difícil es elegir qué trabajo va a qué modelo, y ayuda dejar de pensar en el binario local contra pago y pensar en la forma de la tarea:
+----------------------------------+-------------------------------+---------------------------+
| Task type | What I route it to | Why |
+----------------------------------+-------------------------------+---------------------------+
| Mechanical edits, renames, | Local Ollama model | Free, fast enough, doesn't |
| boilerplate, formatting fixes | (qwen2.5-coder:14b) | need real judgment |
+----------------------------------+-------------------------------+---------------------------+
| Everyday feature work, most | Mid-tier hosted model | Good enough reasoning at a |
| refactors, writing tests | (gpt-5-mini class) | fraction of top-tier cost |
+----------------------------------+-------------------------------+---------------------------+
| Architecture decisions, gnarly | Whatever your best model is | This is where model quality |
| bugs, anything security-adjacent | (Claude Opus, GPT-5, etc.) | differences actually show |
+----------------------------------+-------------------------------+---------------------------+
Enrutar solo según si algo es barato o gratis lleva a pedirle a un modelo local de 14B cosas que no hace de forma fiable, y acabas quemando más tiempo depurando sus llamadas a herramientas, erróneas pero muy seguras, del que ahorraste en inferencia. El proxy hace el enrutado mecánicamente posible. No toma la decisión por ti, y eso no se volvió más fácil con ninguna de las herramientas probadas. Solo se volvió más fácil cuando dejé de esperar que un plugin decidiera automáticamente y empecé a cambiar el archivo env a mano según lo que iba a preguntar.
Un aviso de seguridad
Mantén el proxy en localhost. Cambiar la dirección de escucha a 0.0.0.0 para llegar desde otra máquina deja en tu red un endpoint sin autenticación que puede gastar dinero real en el backend de pago que hayas configurado, para cualquiera que llegue a él. Ninguno de los proxies probados activa la autenticación por defecto. Si necesitas acceso remoto, pon delante algo que compruebe de verdad unas credenciales. No abras el puerto sin más.
Lo que se quedó en uso
Ganó la opción más simple: claude-code-proxy con dos archivos env entre los que alternar. Uno apunta a un modelo de pago para todo lo que requiere razonamiento de verdad, y el otro a Ollama local para el trabajo mecánico. No hizo falta un router completo con interfaz, porque la decisión real es gruesa, dos cubos en lugar de cinco, y cambiar un archivo de configuración da menos fricción que aprender un motor de reglas.
La lección más grande no es exclusiva de Claude Code. Un agente de programación y el modelo que tiene detrás están más separados de lo que sugiere la interfaz, y en cuanto sigues un proxy de traducción de principio a fin, esa separación deja de parecer un truco y empieza a verse como la arquitectura que hay debajo de la mayoría de estas herramientas. Claude Code es simplemente la que uso a diario, así que era la que merecía la pena conectar.
Si lo pruebas, empieza con un backend de pago. Es mucho más fácil saber si la configuración del proxy está rota o si el modelo simplemente se queda corto para la tarea cuando no estás depurando las dos cosas a la vez.