Uso de SQL MCP Server con modelos locales

Importante

Sql Model Context Protocol (MCP) Server está disponible en Data API Builder versión 1.7. Para conocer las funcionalidades y correcciones de errores más recientes, use la versión preliminar 2.0.

Sql Model Context Protocol (MCP) Server funciona con cualquier cliente compatible con MCP, no solo con servicios de INTELIGENCIA ARTIFICIAL hospedados en la nube. Si su entorno restringe el acceso al modelo de lenguaje grande en la nube (LLM), común en la atención sanitaria, defensa, finanzas, energía y industrias marítimas, puede conectar un modelo local servido a través de Ollama o herramientas similares. En esta guía se tratan la configuración, la configuración de metadatos de campo y los patrones de solicitud que hacen que los modelos locales pequeños sean confiables.

Prerequisites

  • CLI de Data API Builder instalada y configurada con al menos una entidad. Instale la CLI.
  • Ollama con un modelo que admite llamadas a herramientas (por ejemplo, qwen3:8b, llama3.1:8b).
  • Python 3.10+ con los paquetes mcp y ollama.
  • Una instancia de SQL Server en ejecución con datos.

Paso 1: Configurar metadatos de campo

Los metadatos de campo son el paso de configuración más importante para la precisión del modelo local. Sin nombres de campo y descripciones, los agentes solo ven nombres de entidad y adivinan nombres de columna incorrectamente.

Advertencia

Si se omite este paso, se genera un servidor MCP que técnicamente funciona, pero que es funcionalmente inutilizable por cualquier modelo que lea las respuestas de la herramienta. El modelo no tiene información sobre tus columnas.

Agregue la entidad con una descripción y agregue descripciones de campo que incluyan valores válidos para columnas restringidas:

dab add ServerInventory \
  --source dbo.ServerInventory \
  --permissions "anonymous:read" \
  --description "SQL Server instance inventory with version, environment, and sizing data"

dab update ServerInventory \
  --fields.name InstanceName --fields.primary-key true \
  --fields.description "SQL Server instance name (e.g., YOURSERVER01)"

dab update ServerInventory \
  --fields.name Environment \
  --fields.description "Deployment environment. Valid values: Prod, Dev, Test, UAT"

Para obtener la referencia completa de la CLI y los procedimientos recomendados, incluidos los valores restringidos, las descripciones de parámetros y los patrones de scripting, consulte Adición de descripciones a entidades.

Nota:

La dab update CLI trata comas como separadores de argumentos. Si la descripción contiene comas, edite dab-config.json directamente en su lugar.

Paso 2: Iniciar SQL MCP Server

dab start

SQL MCP Server escucha en http://localhost:5000/mcp mediante transporte HTTP de flujo continuo de forma predeterminada. Cualquier cliente que implemente el protocolo MCP puede conectarse a este punto de conexión.

Paso 3: Conexión del modelo local

Cree un cliente MCP que conecte el modelo de Ollama a SQL MCP Server. En el ejemplo de Python siguiente se usa MCP Python SDK y el paquete ollama.

Instalación de dependencias

pip install mcp ollama

Arnés de Python mínimo

import asyncio
import json
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
import ollama

MCP_URL = "http://localhost:5000/mcp"
MODEL = "qwen3:8b"

async def get_schema(session: ClientSession) -> str:
    """Call describe_entities and format results for the system prompt."""
    result = await session.call_tool("describe_entities", arguments={})
    entities = json.loads(result.content[0].text)
    lines = []
    for entity in entities.get("entities", []):
        fields = ", ".join(
            f"{f['name']} ({f.get('description', 'no description')})"
            for f in entity.get("fields", [])
        )
        lines.append(f"- {entity['name']}: {entity.get('description', '')}")
        if fields:
            lines.append(f"  Fields: {fields}")
    return "\n".join(lines)

async def run(user_question: str):
    async with streamable_http_client(MCP_URL) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # Preinject schema into the system prompt
            schema_text = await get_schema(session)
            system_prompt = f"""You query a SQL database through MCP tools.

Available entities:
{schema_text}

Rules:
- Use the exact field names shown above.
- Answer count questions with the count only.
- Do not produce summaries unless asked.
- Do not invent example data. Only return data from tool responses.
- If no results, say "No results found" and stop.
"""
            # Get available tools for Ollama
            tools_result = await session.list_tools()
            ollama_tools = [
                {
                    "type": "function",
                    "function": {
                        "name": t.name,
                        "description": t.description or "",
                        "parameters": t.inputSchema,
                    },
                }
                for t in tools_result.tools
            ]

            messages = [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_question},
            ]

            # Chat loop: let the model call tools until it produces a final answer
            while True:
                response = ollama.chat(
                    model=MODEL, messages=messages, tools=ollama_tools
                )
                msg = response["message"]
                messages.append(msg)

                if not msg.get("tool_calls"):
                    print(msg["content"])
                    break

                for tc in msg["tool_calls"]:
                    result = await session.call_tool(
                        tc["function"]["name"],
                        arguments=tc["function"]["arguments"],
                    )
                    messages.append(
                        {
                            "role": "tool",
                            "content": result.content[0].text,
                        }
                    )

asyncio.run(run("How many SQL 2019 servers are in production?"))

Este marco gestiona el ciclo completo: preinyección de esquemas, descubrimiento de herramientas, invocación de herramientas en varios turnos y extracción de la respuesta final. Ajuste MODEL y MCP_URL para su entorno.

Inyectar previamente el esquema al iniciar

Los modelos locales pequeños (con menos de 14.000 millones de parámetros) generan llamadas a herramientas más fiables cuando los metadatos del esquema se incluyen en el prompt del sistema antes de que comience la conversación. En lugar de confiar en que el modelo llame a describe_entities automáticamente durante la conversación, llámalo al iniciar el entorno de pruebas e inyecta el resultado.

¿Por qué importa la preinjección?

Approach Comportamiento con modelos pequeños
Detección dinámica El modelo debe decidir llamar describe_entities primero, luego interpretar los resultados y, a continuación, llamar a la herramienta correcta con nombres de campo correctos. Múltiples puntos de fallo.
Preinyección El modelo ve inmediatamente los nombres de entidad, los nombres de campo y las descripciones. Corrija las llamadas a herramientas en el primer intento.

En el ejemplo de harness de la sección anterior se muestra este patrón. La función get_schema() llama a describe_entities una vez durante el inicio y da formato al resultado para incluirlo en el prompt del sistema.

Sugerencia

Los modelos de nube más grandes (GPT-4o, Claude) suelen detectar el esquema durante la conversación sin necesidad de preinjección. Este patrón es más valioso para los modelos con parámetros 14B.

Restricción de las respuestas del modelo

Un modelo puede realizar una llamada de herramienta correcta, recuperar los datos correctos y seguir produciendo una respuesta incorrecta. Por ejemplo, a un modelo al que se le preguntara «¿cuántos servidores de producción?» podría recuperar correctamente 16 filas y, a continuación, responder con un resumen ejecutivo de 40 líneas con ejemplos inventados en lugar del número 16.

Agregue reglas negativas explícitas al símbolo del sistema:

Rules:
- Answer count questions with the count only.
- Do not produce summaries unless the user asks for one.
- Do not invent example data. Only return data from tool responses.
- If a tool returns no results, say "No results found" and stop.

La fidelidad en la invocación de herramientas y la disciplina en las respuestas son problemas distintos. DAB garantiza una recuperación de datos precisa a través de la capa de herramientas. Tu sistema de instrucciones controla cómo el modelo muestra los resultados.

Consideraciones

Tema Detalles
Hardware La invocación de herramientas funciona en hardware básico. Un modelo de parámetros 8B en una GPU nvidia de consumidor (RAM de vídeo de 8 GB) genera resultados útiles. Cabe esperar una latencia de varias decenas de segundos para cada pregunta, lo que resulta adecuado para cargas de trabajo por lotes.
Batch frente a interactivo Los modelos pequeños son adecuados para el procesamiento por lotes (informes de rendimiento, consultas de inventario) donde la tolerancia a la latencia es mayor.
Disponibilidad de herramientas aggregate_records está disponible en la versión preliminar 2.0 y versiones posteriores. En la versión 1.7.x, las consultas de recuento y agregación obligan al modelo a leer todas las filas coincidentes. Consulte disponibilidad de herramientas por versión.
Transporte Los modelos locales se conectan mediante HTTP de streaming a /mcp. El transporte estándar de entrada/salida (stdio) es una alternativa para las configuraciones de un solo proceso.
Autenticación Para el desarrollo local, utilice los permisos anonymous. Para producción, configure la autenticación adecuada para su entorno.