使用 SQL MCP Server 搭配本機模型

Important

SQL 模型情境協定(MCP)伺服器可在資料 API 建構器 1.7 版及更新版本中提供。 想要獲得最新功能和錯誤修正,請使用最新的 2.0 版本。

SQL 模型情境協定(MCP)伺服器可支援任何相容 MCP 用戶端,而非僅限於雲端託管的 AI 服務。 如果您的環境限制了雲端大型語言模型(LLM)存取——這在醫療保健、國防、金融、能源及海事產業中很常見——你可以連接透過 Ollama 或類似工具服務的本地模型。 本指南涵蓋設置、欄位元資料配置,以及讓小型本地模型可靠的提示模式。

先決條件

  • 已安裝並設定 Data API 建構器 CLI,且已設定至少一個實體。 安裝 CLI。
  • Ollama 搭配支援工具呼叫的模型(例如, qwen3:8b, llama3.1:8b)。
  • Python 3.10+搭配 mcp 和 ollama 套件。
  • 一個正在執行的 SQL Server 實例,裡面有資料。

步驟 1:設定欄位元資料

現場元資料是本地模型準確性的最重要的配置步驟。 若沒有欄位名稱與描述,代理人只會看到實體名稱,並錯誤猜測欄位名稱。

警告

跳過此步驟會產生一個在技術上可以運作,但在功能上任何會讀取工具回應的模型都無法實際使用的 MCP 伺服器。 模型沒有您欄位的相關資訊。

先新增實體並加上描述,然後再新增欄位描述,其中包含受限欄位的有效值:

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"

欲了解完整的 CLI 參考與最佳實務——包括受限值、參數描述及腳本模式——請參見 「將描述加入實體」。

Note

dab update CLI 將逗號視為論元分隔符。 如果你的描述包含逗號,請直接編輯 dab-config.json 。

步驟 2:啟動 SQL MCP 伺服器

dab start

SQL MCP 伺服器預設使用可串流的 HTTP 傳輸,並在 http://localhost:5000/mcp 上接聽。 任何實作 MCP 協定的用戶端都能連接到此端點。

步驟 3:連接你當地的模型

建立一個 MCP 客戶端,將你的 Ollama 模型連接到 SQL MCP Server。 以下Python範例使用 MCP Python SDK 與 套件。

安裝依賴項

pip install mcp ollama

極簡 Python 線束

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?"))

此工具涵蓋完整流程:模式預注入、工具發現、多回合工具呼叫,以及最終回應擷取。 調整 MODEL 並 MCP_URL 適應你的環境。

啟動時的預注入結構

小型本地模型(參數少於 14B)在對話開始前,若系統提示中包含結構描述中繼資料,會產生更可靠的工具呼叫。 與其在對話中依賴模型自行呼叫 describe_entities ,不如在 harness startup 時呼叫並注入結果。

為什麼注射前很重要

方法 使用小型模型時的行為
動態探索 模型必須先決定呼叫 describe_entities ,再解讀結果,再呼叫具有正確欄位名稱的正確工具。 多個故障點。
預注射 模型能立即看到實體名稱、欄位名稱和描述。 第一次嘗試就正確地選對了工具。

前一節的背帶範例展示了這種模式。 get_schema() 函式會在啟動時呼叫一次 describe_entities,並將結果格式化後放入系統提示。

Tip

較大型的雲端模型(如 GPT-4o、Claude)通常無須預先注入資訊,就能在對話過程中推斷出結構描述(schema)。 此模式對於參數為14B的模型最有價值。

限制模型回應

模型可以做出正確的工具呼叫,取得正確的資料,但仍會產生錯誤的答案。 例如,當被問到「有多少台生產伺服器?」時,某個模型可能會正確擷取到 16 列資料,接著卻回覆一份長達 40 行、包含憑空捏造範例的主管摘要,而不是數字 16。

在系統提示中加入明確的負面規則:

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.

工具呼叫的準確度與接答紀律是不同的問題。 DAB 透過工具層確保能準確擷取資料。 你的提示束帶控制模型呈現結果的方式。

Considerations

主題 詳細資訊
硬體 工具呼叫功能可在規格不高的硬體上運作。 在消費級 Nvidia GPU(8 GB 視訊記憶體)上採用 8B 參數模型,能產生有用的結果。 每個問題的延遲時間預計為數十秒,因此適合批次工作負載。
批次與互動式 小型模型非常適合批次處理(效能報告、庫存查詢),因為延遲容忍度較高。
工具可用性 此 aggregate_records 工具僅在 2.0 版本及更新版本中提供。 在 1.7.x 版本中,計數與彙總查詢會強制模型讀取所有相符的列。 請參閱 依版本分類的工具可用性。
交通 本地模型透過可串流的 HTTP 連接到 /mcp。 標準輸入/輸出(stdio)傳輸是單程序設置的替代方案。
驗證 在地開發時,請使用 anonymous 權限。 對於生產環境,請設定符合你環境的 認證 。