Inicio / Artículos / Protocolo de contexto de modelos para principiantes con FastMCP y Ollama

Protocolo de contexto de modelos para principiantes con FastMCP y Ollama

Aprenda los roles de MCP: anfitrión, cliente, servidor y transporte; luego conecte un servidor de herramientas meteorológicas a un modelo local qwen3:8b mediante FastMCP y STDIO.

1841 palabras

El Model Context Protocol, abreviado habitualmente como MCP, es un lenguaje compartido para conectar modelos de lenguaje grandes con herramientas y fuentes de datos a los que no pueden acceder por sí solos. Llamarlo protocolo resalta que estandariza cómo debe ser la conversación; las bibliotecas concretas implementan luego ese estándar para que los equipos no tengan que inventar sus propios sockets y esquemas de mensajes. FastMCP es una de estas implementaciones utilizadas en la guía a continuación.

Repositorio complementario: https://github.com/harshagangari747/MCPTutorial/tree/main

Requisitos previos

La demostración depende de tres paquetes: fastmcp, ollama y langchain-community. La inferencia se realiza con el modelo local qwen3:8b. Inícielo con:

ollama run qwen3:8b

Prepare una carpeta de proyecto que ya contenga archivos vacíos denominados weather_server_mcp.py y app.py para que el servidor y la aplicación tengan ubicaciones definidas.

Comprensión de MCP

Por sí solo, un LLM es un transductor de tokens: los tokens entran y los tokens salen. No llama a APIs meteorológicas, abre bases de datos ni lee el reloj del sistema, a menos que algo externo al modelo realice esas acciones. Los proveedores de nube a veces integran ejecutores de herramientas propietarias en sus APIs, lo cual es conveniente en entornos de producción pero problemático cuando el objetivo es analizar el protocolo en sí. Ejecutar un modelo local a través de Ollama mantiene el experimento autónomo.

Pruebe con una pregunta como “¿cómo está el clima hoy en Italia?”. Una respuesta típica local comienza admitiendo que no existe una fuente de datos meteorológicos en tiempo real. Aun así, la oración contiene tres indicaciones que el sistema debe resolver: el clima como tema, “hoy” como fecha y Italia como lugar. El modelo necesita una forma de calcular o obtener la información meteorológica, un medio para determinar qué significa “hoy” y una forma de asociar ese clima con Italia.

La falta de referencia al calendario es la brecha evidente. Los pesos del modelo no saben con fiabilidad la fecha actual. MCP resulta útil cuando el modelo puede proponer herramientas y argumentos, y un entorno de ejecución correspondiente los pone en práctica, devolviendo observaciones actualizadas que el modelo puede integrar en una respuesta.

Componentes en MCP

Una implementación práctica de MCP suele incluir varios elementos que colaboran entre sí:

  1. API funcional — cualquier servicio que ya responda a la pregunta del dominio, como un punto de conexión meteorológico disponible en línea.
  2. Servidor MCP — un proceso que oculta cómo se accede a la API o a la base de datos y publica herramientas llamables.
  3. Anfitrión MCP — la interfaz del producto, por ejemplo un planificador de viajes inteligente que combina el razonamiento de LLM con datos en tiempo real.
  4. Cliente MCP — un puente integrado dentro del anfitrión. Indica al modelo qué herramientas existen, convierte las intenciones del modelo en solicitudes MCP y transforma las respuestas MCP en un contexto adecuado para el modelo.
  5. Capa de transporte — JSON-RPC 2.0, entregado ya sea mediante HTTP/SSE cuando los componentes están remotos, o a través de STDIO cuando el modelo y las herramientas comparten la misma máquina.
  6. LLM — aquí qwen3:8b proporcionado por Ollama.
  • Harness — una orquestación opcional que une la pila con menos código escrito a mano; el artículo original menciona a Goose AI como un ejemplo.
  • Al definir esos roles, la pregunta sobre el clima en Italia se convierte en una secuencia coordinada en lugar de una sola llamada al modelo.

    Analogía

    Una metáfora de conducción mantiene claros los roles. La intención de conducir corresponde a la aplicación anfitriona. El cerebro es el LLM: lee el contexto de la carretera y decide acelerar, frenar o cambiar de marcha, pero no puede presionar los pedales. Las extremidades corresponden al servidor MCP; los músculos y huesos dentro de una extremidad son herramientas individuales: una extremidad dirige o cambia de marcha, mientras que otra frena o acelera. La interfaz nerviosa entre el cerebro y los músculos es el cliente MCP. Los nervios que transmiten impulsos eléctricos son el medio de transporte. El coche es la API externa. El cuerpo completo constituye el conjunto que permite que las partes cooperen.

    Mapeo resumido:

    • LLM → cerebro
    • Servidor MCP → extremidad
    • Herramienta → acción muscular
    • Anfitrión MCP → intención de conducción
    • Cliente MCP → interfaz nerviosa
    • Medio de transporte → nervios
    • API funcional → coche
    • Conjunto de componentes → cuerpo completo

    Esa imagen es suficiente para evitar que el servidor, el cliente y el mecanismo de transporte se conviertan en un único “plugin” vago.

    Funcionamiento de MCP

    La implementación sigue estos roles: establecer un servidor, un anfitrión, un LLM, un mecanismo de transporte, opcionalmente un sistema de integración, y una API o servicio real. El servidor abstracta la API y expone herramientas. Cada herramienta representa una acción concreta que el modelo puede solicitar; el modelo nunca ejecuta la llamada HTTP por sí mismo. El cliente tanto publica el catálogo como realiza traducciones en ambas direcciones, lo que mantiene al modelo y al servidor ligeramente acoplados.

    Dado un servidor que ofrece get_todays_date() y get_weather_data(city, date), una consulta como “¿Cuál es el clima hoy en París?” puede desarrollarse de la siguiente manera:

    1. El modelo se da cuenta de que necesita la fecha de hoy.
    2. Pide al cliente MCP que utilice get_todays_date.
    3. El cliente envía la solicitud al servidor.
  • El servidor lo cumple.
  • El cliente reformata la respuesta del servidor para el modelo.
  • Aun teniendo una fecha, el modelo aún necesita los datos meteorológicos de París.
  • Pide al cliente que llame a get_weather_data con la ciudad y la fecha.
  • El cliente convierte esa intención en una solicitud al servidor.
  • El servidor llama a la API meteorológica y devuelve el contenido.
  • El cliente vuelve a convertir ese contenido para el modelo.
  • El modelo presenta la respuesta final al usuario.
  • Las preguntas históricas que caen dentro del período de entrenamiento pueden responderse únicamente con la memoria, pero el propósito de MCP es el contexto actual: fechas y condiciones meteorológicas que cambian después del entrenamiento.

    El proyecto

    La muestra mantiene la descripción del clima concreta. Un servidor MCP gestiona la lógica que se comunica con una API meteorológica externa. Una aplicación anfitriona crea el cliente MCP, registra al servidor y realiza consultas a Ollama. Al aislar el acceso al LLM en su propio helper, la estructura de transmisión queda más legible.

    Servidor MCP

    # MCP Server
    # weather_server_mcp.py
    from fastmcp import FastMCP
    import requests
    
    # This is a server instance that we register in our host
    server = FastMCP("weather-mcp-server")
    
    
    # Third party api data
    WEATHER_API_KEY = "api_key_here"
    WEATHER_BASE_URL = "https://api.weatherapi.com/v1/"
    
    # Tool 1
    @server.tool()
    def get_weather_data(city: str) -> float:
        """Get current temperature in Celsius"""
        response = requests.get(
            WEATHER_BASE_URL + "current.json",
            params={"key": WEATHER_API_KEY, "q": city},
        )
        response.raise_for_status()
        return response.json()["current"]["temp_c"]
    
    # Tool 2
    @server.tool()
    def get_historical_weather_data(city: str, date: str) -> float:
        """Get max temperature for a historical date"""
        response = requests.get(
            WEATHER_BASE_URL + "history.json",
            params={"key": WEATHER_API_KEY, "q": city, "dt": date},
        )
        response.raise_for_status()
        return response.json()["forecast"]["forecastday"][0]["day"]["maxtemp_c"]
    
    
    if __name__ == "__main__":
        server.run()
    

    Las funciones que acceden a la API están anotadas con @server.tool(), lo que las publica como herramientas. Las documentaciones al inicio de cada función no son meros adornos; indican al modelo cuándo utilizar esa herramienta. El ejemplo incluye dos herramientas: una que obtiene el clima actual de una ciudad y otra que obtiene el clima histórico de una ciudad en un día pasado.

    Anfitrión MCP, cliente, LLM, método de transmisión

    import asyncio
    import sys
    import json
    from pathlib import Path
    from langchain_community.llms import Ollama
    from fastmcp import Client
    from fastmcp.client.transports import StdioTransport
    
    
    async def main():
        # We mention the mcp server path.
        server_path = Path(__file__).parent / "weather_server_mcp.py"
    
        # The transport method here is STDIO
        transport = StdioTransport(
            command=sys.executable,
            args=[str(server_path)],
        )
    
        # Register the MCP Client
        mcp_client = Client(transport)
    
        # LLM via Ollama
        llm = Ollama(model="qwen3:8b", temperature=0.5)
    
        async with mcp_client:
            print("✓ Connected to MCP server!")
    
            # We can now access that tools are present in the weather server mcp now.
            mcp_tools = await mcp_client.list_tools()
            tools_info = "\n".join([f"- {t.name}: {t.description or t.name}" for t in mcp_tools])
    
            print(f"✓ Available tools:\n{tools_info}\n")
    
            # Interactive loop
            while True:
                question = input("🌤️  Ask: ").strip()
                if question.lower() == 'exit':
                    break
    
                try:
                    # Step 1: Ask LLM to decide which tool to use
                    decision_prompt = f"""Given the question: "{question}"
    
    Available tools:
    {tools_info}
    
    Respond with ONLY a JSON object (no other text):
    {{"tool": "tool_name", "params": {{"city": "city_name"}}}}
    
    For get_historical_weather_data, use: {{"tool": "get_historical_weather_data", "params": {{"city": "city_name", "date": "YYYY-MM-DD"}}}}"""
    
                    print(f"\n📍 Processing: {question}")
                    llm_response = llm.invoke(decision_prompt)
    
                    # Step 2: Parse JSON from LLM response
                    json_start = llm_response.find('{')
                    json_end = llm_response.rfind('}') + 1
    
                    if json_start == -1 or json_end == 0:
                        print("❌ LLM didn't return valid tool call")
                        continue
    
                    json_str = llm_response[json_start:json_end]
                    tool_call = json.loads(json_str)
    
                    print("Tool call: ", tool_call)
    
                    # Handle array responses
                    if isinstance(tool_call, list):
                        tool_call = tool_call[0]
    
                    tool_name = tool_call.get("tool")
                    params = tool_call.get("params", {})
    
                    print(f"🔧 Calling: {tool_name} with {params}")
    
                    # Step 3: Call MCP tool. This is where we actually call the tool.
                    result = await mcp_client.call_tool(tool_name, params)
                    answer = result.content[0].text
    
                    print(f"✓ Answer: {answer}°C\n")
    
                except json.JSONDecodeError as e:
                    print(f"❌ JSON parsing error: {e}")
                except Exception as e:
                    print(f"❌ Error: {e}\n")
    
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    ¿Qué está sucediendo?

    Se resuelve la ruta del módulo del servidor junto al anfitrión:

    server_path = Path(__file__).parent / "weather_server_mcp.py"
    

    Cree un transporte STDIO que inicie ese módulo con el intérprete de Python actual:

      # The transport method here is STDIO
        transport = StdioTransport(
            command=sys.executable,
            args=[str(server_path)],
        )
    

    Instancie el cliente MCP a partir del transporte:

    mcp_client = Client(transport)
    

    El host ahora cuenta con una ruta del servidor registrada, un transporte seleccionado y un cliente. Conecte el modelo a través de Ollama:

    llm = Ollama(model="qwen3:8b", temperature=0.5)
    

    Pida al cliente el catálogo de herramientas publicado por weather_server_mcp.py:

    mcp_tools = await mcp_client.list_tools()
    

    Pase ese catálogo al prompt e indique al modelo que responda únicamente con el nombre de la herramienta y sus parámetros. Después de analizarlo, ejecute la herramienta seleccionada:

    result = await mcp_client.call_tool(tool_name, params)
    

    Por lo tanto, la estructura básica del tutorial es: crear el servidor, registrarlo, registrar el cliente, conectar un LLM y seleccionar un transporte. Las herramientas de agente pueden ocultar parte de esta configuración; un bucle simple mantiene visibles todos los pasos del protocolo durante el proceso de aprendizaje.

    En conjunto, MCP es menos una llamada a una biblioteca única y más una división del trabajo. El modelo propone; el cliente traduce; el servidor actúa; el transporte transmite mensajes JSON-RPC; el host se encarga del bucle orientado al usuario. Una vez claros esos límites, reemplazar el clima por calendarios, CRM o búsquedas internas consiste principalmente en crear nuevas herramientas y documentarlas lo suficientemente bien para que el modelo elija correctamente. Mientras el bucle está en ejecución, observe qué emite el modelo antes de cada llamada a una herramienta. Una traza saludable muestra que el modelo menciona una herramienta que realmente existe, proporciona las claves de los argumentos descritas en la documentación y espera a que el cliente devuelva datos antes de redactar la frase orientada al usuario. Si el modelo inventa un nombre de herramienta, refine la instrucción o mejore las descripciones de las herramientas. Si el servidor lanza un error, muestre dicho error a través del cliente para que el modelo pueda intentarlo de nuevo o disculparse en lugar de generar información falsa.

    Los valores meteorológicos de Ating. Esa disciplina en la retroalimentación es tan importante como el cableado inicial.

    Lecturas relacionadas

  • Deja de devolver JSON: Envía interfaces de usuario interactivas con aplicaciones MCP — Las aplicaciones MCP permiten a los servidores enviar interfaces de usuario en entornos aislados junto con herramientas, para que los usuarios puedan aprobarlas, configurarlas y operarlas sin salir de la conversación con el agente.
  • Herramientas de visión MCP con MiniCPM-V en Ollama — Exponer herramientas de descripción, OCR y comparación de imágenes a través de MCP para que Cursor y Claude Desktop puedan analizar capturas de pantalla localmente.