Servidor MCP de Ollama: cómo conectar modelos locales con un puente
Construye un puente de servidor MCP para Ollama en ambas direcciones: un cliente en Python que permite a los modelos locales llamar a herramientas MCP, y un servidor que expone Ollama a Claude Desktop y Claude Code. Incluye dimensionamiento de hardware, código funcional, archivos de configuración y soluciones a los tres errores que rompen la mayoría de los primeros intentos.
Tu equipo portátil ya puede ejecutar un modelo de lenguaje capaz, pero la mayoría de tus herramientas no sabe que existe. Una configuración de servidor MCP de Ollama soluciona eso. Un pequeño puente se sitúa entre Ollama, que sirve los modelos en localhost:11434, y el Model Context Protocol (MCP), el estándar que usan las aplicaciones de IA para encontrar y llamar herramientas. Lo construyes una vez y un modelo local puede leer tus archivos, consultar una base de datos o buscar en tus notas. Si cambias la dirección, Claude Desktop o Claude Code pueden delegar tareas baratas y privadas en la GPU que tienes debajo de la mesa.
Este artículo construye las dos direcciones con código Python funcional: un cliente puente que permite a los modelos de Ollama usar cualquier servidor MCP, y un servidor puente que expone Ollama como un conjunto de herramientas para cualquier cliente MCP. También incluye cifras de memoria, archivos de configuración y los tres errores que consumen la mayor parte del tiempo de depuración.
Qué hace un puente de Ollama MCP
Ollama y MCP resuelven mitades distintas de un mismo problema. Ollama descarga modelos de pesos abiertos y los sirve a través de una API HTTP sencilla. MCP, que Anthropic publicó a finales de 2024, estandariza la forma en que una aplicación de IA se comunica con herramientas, archivos y datos a través de pequeños programas llamados servidores. La API de Ollama habla con mensajes de chat y definiciones de herramientas. No habla MCP, y los clientes MCP no saben dónde viven tus modelos. El puente traduce entre ambos.
Dos formas de construir un puente
"Puente" significa dos programas distintos según quién necesite qué, así que elige la dirección antes de escribir cualquier código.
Dirección
Cliente MCP
Servidor MCP
Uso típico
Ollama usa herramientas
Tu script puente, que envuelve un modelo de Ollama
Servidor de archivos, base de datos o búsqueda
Un asistente local que lee tus notas
Ollama como herramienta
Claude Desktop, Claude Code, Cursor
Tu script puente, que envuelve Ollama
Delegar tareas privadas o baratas en un modelo local
La primera dirección da a un modelo local la capacidad de actuar. La segunda le da a un asistente en la nube un colega local. La mayoría termina construyendo ambas, porque la segunda solo lleva unas 30 líneas.
Una nota rápida sobre el transporte. Los ejemplos usan stdio, donde el cliente inicia el servidor como proceso hijo y se comunica con él por la entrada y salida estándar. Es la opción más sencilla para un equipo personal. Si quieres un único puente compartido por varias aplicaciones o por varios equipos de tu red, ejecútalo con el transporte Streamable HTTP y mantenlo detrás de tu propio cortafuegos. El código de las herramientas sigue siendo el mismo, y solo cambia la última línea del servidor.
Cómo viaja una llamada a una herramienta
Sea cual sea la dirección que elijas, una llamada a una herramienta sigue el mismo ciclo:
El puente se conecta a un servidor MCP y pide la lista de herramientas con tools/list.
Reescribe el esquema de cada herramienta al formato que espera Ollama.
Envía la pregunta del usuario y esas definiciones de herramientas a un modelo local.
El modelo responde con una entrada tool_calls en lugar de texto.
El puente ejecuta esa llamada en el servidor MCP con tools/call.
El resultado vuelve al modelo como un mensaje tool.
Los pasos del 3 al 6 se repiten hasta que el modelo responde con texto plano.
💡 El modelo nunca toca tu disco. Solo solicita una acción. Tu puente decide si la ejecuta, y por eso es el lugar adecuado para las listas de permitidos, las confirmaciones y el registro de actividad.
Lo que necesitas antes
Hardware que funciona
Los pesos del modelo tienen que caber en la memoria de la GPU (o en la memoria unificada de un Mac) con espacio libre para el contexto. Estas cifras son aproximadas y corresponden a versiones cuantizadas a 4 bits:
Tamaño del modelo
Memoria para los pesos
Configuración cómoda
3B a 4B
2 a 3 GB
Cualquier equipo portátil reciente
7B a 8B
5 a 6 GB
GPU de 8 GB o 16 GB de memoria unificada
14B
9 a 10 GB
GPU de 12 GB
20B
13 a 15 GB
GPU de 16 GB o 24 GB de memoria unificada
32B
19 a 21 GB
GPU de 24 GB
Los modelos que se desbordan hacia la RAM del sistema siguen funcionando, pero la velocidad de tokens cae en picado. Un puente hace varias llamadas al modelo por pregunta, así que la velocidad importa más que el tamaño. Un modelo de 8B que cabe por completo en la GPU suele superar a un modelo de 32B que no cabe.
Modelos que admiten llamadas a herramientas
No todos los modelos pueden pedir una herramienta. Ollama revisa la plantilla de chat del modelo, y un modelo sin soporte para herramientas devuelve un error cuando le pasas tools. Filtra la biblioteca de Ollama por la etiqueta tools o empieza con esta lista corta:
Etiqueta de Ollama
Tamaño en disco
Por qué usarlo
llama3.1:8b
unos 4,9 GB
Opción predeterminada fiable para las primeras pruebas
qwen3:8b
unos 5,2 GB
Sólido en uso de herramientas de varios pasos, más lento con el pensamiento activado
mistral-nemo
unos 7,1 GB
Contexto largo, maneja muchas herramientas
gpt-oss:20b
unos 14 GB
El GPT OSS 20B de pesos abiertos, diseñado pensando en el uso de herramientas
Instala las piezas
Activa primero un entorno virtual (source .venv/bin/activate en macOS y Linux, .venv\Scripts\activate en Windows) y luego ejecuta:
# 1. Pull a tool-capable model and confirm Ollama is serving
ollama pull llama3.1:8b
curl http://localhost:11434/api/tags
# 2. Install the two Python packages the bridge needs
pip install ollama mcp
# 3. Check Node, because the example MCP server runs through npx
node --version
Si curl devuelve una lista JSON con tus modelos, Ollama está listo. Si la conexión es rechazada, inícialo con ollama serve.
Construye el cliente puente en Python
El cliente es un solo archivo. Lanza un servidor MCP como proceso hijo mediante el transporte stdio, lee sus herramientas y ejecuta el ciclo de la sección anterior. El servidor del ejemplo es el servidor oficial de archivos, apuntado a una carpeta de notas.
Convierte las herramientas MCP al formato de Ollama
Una herramienta MCP tiene un name, una description y un inputSchema escritos en JSON Schema. El formato de llamadas a funciones de Ollama necesita esas mismas tres piezas, envueltas en un objeto function. La conversión es casi solo un cambio de nombre:
La mayoría de los esquemas pasan sin cambios. Si un servidor incluye construcciones poco comunes como anyOf o $ref, aplánalas antes de enviarlas. Los modelos pequeños manejan los esquemas planos mucho mejor.
Escribe el ciclo de llamadas a herramientas
import asyncio
import ollama
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
MODEL = "llama3.1:8b"
NOTES_DIR = "/home/me/notes"
MAX_TURNS = 8
server_params = StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", NOTES_DIR],
)
def to_ollama_tool(tool):
return {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description or "",
"parameters": tool.inputSchema,
},
}
async def ask(question: str) -> str:
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
listed = await session.list_tools()
tools = [to_ollama_tool(t) for t in listed.tools]
client = ollama.AsyncClient()
messages = [{"role": "user", "content": question}]
for _ in range(MAX_TURNS):
response = await client.chat(model=MODEL, messages=messages, tools=tools)
message = response.message
messages.append(message)
if not message.tool_calls:
return message.content
for call in message.tool_calls:
result = await session.call_tool(
call.function.name, dict(call.function.arguments)
)
text = "\n".join(
block.text for block in result.content if block.type == "text"
)
messages.append(
{"role": "tool", "tool_name": call.function.name, "content": text}
)
return "Stopped: the model kept calling tools."
if __name__ == "__main__":
print(asyncio.run(ask("Which markdown files in my notes folder mention invoices?")))
Cuatro detalles merecen atención:
MAX_TURNS es una barrera de seguridad. Un modelo confundido puede llamar a la misma herramienta sin parar, y un contador convierte eso en un fallo claro.
dict(call.function.arguments) importa porque Ollama devuelve los argumentos como un mapeo que la sesión MCP espera como diccionario simple.
Solo bloques de texto. Los resultados de MCP pueden incluir imágenes y recursos incrustados. Esta versión conserva el texto e ignora el resto.
tool_name en el mensaje de la herramienta indica al modelo a qué llamada pertenece el resultado, lo que evita que se mezclen las llamadas paralelas.
Ejecútalo con archivos reales
Guarda el archivo como bridge_client.py, cambia NOTES_DIR por una carpeta real y ejecuta python bridge_client.py. Una ejecución correcta primero hace una llamada para listar directorios o buscar, luego una o dos lecturas de archivos, y después responde en texto plano. Para ver cómo el modelo elige las herramientas, añade print(call.function.name, call.function.arguments) al inicio del bucle interno.
💡 Consejo para Windows: si Python no puede lanzar npx, usa npx.cmd como comando. La marca -y hace que npx instale el servidor de archivos la primera vez sin pedir confirmación.
Antes de apuntar el puente a algo sensible, decide qué puede hacer el modelo. El servidor de archivos solo accede a las carpetas que le pasas en la línea de comandos, así que dale una carpeta de notas en lugar de tu directorio personal. Para las herramientas que escriben, borran o envían datos, añade un paso de confirmación dentro del bucle: muestra la llamada y pide un sí antes de que se ejecute session.call_tool. Treinta segundos de fricción valen más que un modelo de 8B decidiendo que una limpieza es buena idea.
Expón Ollama como servidor MCP
Ahora cambia la dirección. En lugar de que un modelo local use herramientas ajenas, publicas el modelo local como una herramienta. Cualquier cliente MCP puede entonces llamarlo para redactar, resumir o clasificar texto que nunca debería salir de tu equipo.
Un servidor pequeño en Python
El SDK oficial de Python incluye FastMCP, que construye el esquema de la herramienta a partir de tus anotaciones de tipo y la descripción a partir de tu docstring:
import sys
import ollama
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("ollama-bridge")
client = ollama.AsyncClient()
DEFAULT_MODEL = "llama3.1:8b"
@mcp.tool()
async def list_local_models() -> list[str]:
"""List the models installed in the local Ollama instance."""
listed = await client.list()
return [m.model for m in listed.models]
@mcp.tool()
async def ask_local_model(
prompt: str, model: str = DEFAULT_MODEL, temperature: float = 0.2
) -> str:
"""Send a prompt to a local Ollama model and return its reply.
Use it for private text or cheap drafts that should stay on this machine."""
print(f"ask_local_model: {model}", file=sys.stderr)
response = await client.chat(
model=model,
messages=[{"role": "user", "content": prompt}],
options={"temperature": temperature},
)
return response.message.content
if __name__ == "__main__":
mcp.run(transport="stdio")
Escribe el docstring para quien llama, no para ti. El modelo cliente lo lee para decidir cuándo merece la pena llamar a tu herramienta, así que "úsala para texto privado" hace un trabajo real.
Conéctalo a Claude Desktop
Abre claude_desktop_config.json. En Windows está en %APPDATA%\Claude, y en macOS en ~/Library/Application Support/Claude. Añade el servidor bajo mcpServers:
Apunta command al Python del entorno virtual, no al del sistema. Claude Desktop no activa tu entorno, y un intérprete del sistema sin el paquete mcp falla sin avisar. Cierra la aplicación y vuelve a abrirla, y las dos herramientas aparecerán en el menú de herramientas.
Conéctalo a Claude Code
Claude Code registra los servidores desde la terminal:
claude mcp add ollama-bridge -- /path/to/.venv/bin/python /path/to/ollama_bridge.py
claude mcp list
Todo lo que va después de los dos guiones es el comando de lanzamiento. Cuando claude mcp list muestre el servidor como conectado, pide a Claude Code que "resuma este log con el modelo local" y observa cómo llama a ask_local_model.
Soluciona los problemas que te vas a encontrar
Tres problemas explican la mayoría de los primeros intentos fallidos. Los tres tienen soluciones breves.
La salida estándar rompe los servidores stdio
Un servidor MCP stdio envía mensajes JSON-RPC por stdout. Un print() perdido inyecta texto en ese flujo, y el cliente cierra la conexión o informa de un error de análisis. El síntoma es un servidor que se conecta y, en menos de un segundo, se desconecta.
La solución es un hábito: registra en stderr (print(..., file=sys.stderr)) o en un archivo, y nunca escribas nada más en stdout. Eso incluye las barras de progreso y las advertencias que imprimen las bibliotecas importadas.
Los modelos pequeños ignoran las herramientas
Un modelo de 8B al que le das 25 herramientas a menudo responde de memoria o llama a la equivocada. Cuatro cambios ayudan, más o menos por orden de impacto:
Envía menos herramientas. Filtra la lista hasta dejar entre tres y seis herramientas que encajen con la pregunta.
Reescribe las descripciones. "Lee el contenido de un archivo por su ruta absoluta" es mejor que "Lector de archivos".
Baja la temperatura a 0,1 o 0,2 para la selección de herramientas.
Sube un tamaño. Pasar de 3B a 8B corrige más fallos de llamadas a herramientas que cualquier truco de prompt.
Las ventanas de contexto se llenan rápido
Las definiciones de herramientas y los resultados de las herramientas consumen contexto, y una sola lectura de un archivo grande puede empujar la pregunta fuera de la ventana. El contexto predeterminado de Ollama es pequeño, así que auméntalo de forma explícita y recorta los resultados antes de que lleguen al modelo:
response = await client.chat(
model=MODEL,
messages=messages,
tools=tools,
options={"num_ctx": 8192},
)
text = text[:4000] # trim large tool results before appending them
Los valores más altos de num_ctx usan más memoria para la caché de atención, así que auméntalos por pasos. Además, define OLLAMA_KEEP_ALIVE con un valor más largo, como 30m. Ollama descarga los modelos inactivos después de cinco minutos por defecto, y cada recarga añade segundos a la primera respuesta.
Modelos locales o modelos alojados
Un puente no te obliga a elegir. Te permite enviar cada tarea al lugar más barato que puede hacerla bien.
Los modelos locales ganan cuando:
el texto es privado, como contratos, notas de salud o código fuente bajo acuerdo de confidencialidad
la tarea se repite miles de veces, así que el precio por token se acumula
trabajas sin conexión o en una red que no controlas
Los modelos alojados ganan cuando:
tu GPU tiene menos de 8 GB de memoria
la tarea necesita un modelo de más de 30B parámetros
necesitas una respuesta en segundos desde un arranque en frío
En la práctica, lo que mejor funciona es un enfoque híbrido. Deja que el modelo local se encargue de los primeros borradores, la clasificación y todo lo que toque archivos privados, y envía el 10 % más difícil a un modelo alojado más grande. Como ambos están detrás de la misma interfaz MCP, tu asistente puede elegir entre ask_local_model y una alternativa alojada con solo una descripción de herramienta más clara.
Los modelos alojados en PicassoIA sirven como referencias útiles. Ejecuta el mismo prompt en un modelo local de 8B y en uno de estos, y verás exactamente lo que aporta el tamaño extra:
Escribe tu prompt en el campo Prompt. Una buena prueba es el docstring que piensas dar a ask_local_model, con la pregunta "¿Sabrías cuándo llamar a esta herramienta?".
Deja Temperature en su valor predeterminado de 0,1 para obtener resultados precisos y repetibles. Súbelo para lluvia de ideas.
Mantén Max Tokens en 2048 para respuestas largas, o bájalo para respuestas cortas.
Ajusta Top P, Presence Penalty y Frequency Penalty solo si la salida se repite en bucle o resulta repetitiva.
Ejecútalo, ajusta y vuelve a ejecutarlo. La página del modelo indica generaciones ilimitadas, así que iterar no cuesta nada.
💡 Compara la respuesta alojada con tu ejecución local. Si coinciden en gran medida, la configuración local está cumpliendo su función y puedes dejar de pagar la llamada alojada.
Combina el puente con la generación de imágenes
Una vez que ask_local_model existe, puede hacer más que resumir logs. Un modelo local es una forma barata y privada de redactar prompts para imágenes, y ahí es donde el puente se encuentra con el trabajo visual.
Añade una tercera herramienta que reciba un tema y devuelva un prompt fotográfico de 60 palabras: sujeto, escenario, dirección de la luz, objetivo y estilo de película. Tu asistente la llama, y pegas el resultado en un modelo de texto a imagen. P-Image es una opción rápida para borradores. FLUX 2 Pro sirve para una segunda pasada cuando un prompt necesita más detalle.
Una rutina sencilla funciona muy bien:
Pide al modelo local tres variantes de prompt sobre un mismo tema.
El mismo patrón sirve para miniaturas, fotos de producto y cabeceras de blog. El puente mantiene gratuita y privada la fase de redacción, y los modelos alojados se encargan del render pesado.
Crea tus propias imágenes en PicassoIA
Ya tienes un puente funcionando en ambas direcciones: un modelo local que puede usar herramientas y un modelo local al que otras aplicaciones pueden llamar. El siguiente paso es ponerlo a trabajar en algo que puedas ver.
Lleva la idea de redactar prompts a la práctica hoy mismo. Pide a tu modelo local un prompt fotográfico, abre Picasso IA y genera tu primera imagen con P-Image o FLUX 2 Pro. Cambia el objetivo, la dirección de la luz o el escenario, y vuelve a ejecutarlo. Cuando un fotograma funcione, conviértelo en un video corto. Explora todos los modelos disponibles en el catálogo completo de PicassoIA y empieza a experimentar con Picasso IA.