🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

前言

本文章介紹的 Model Context Protocol (MCP)伺服器是在本地 PC 上運行的本地伺服器,而不是在雲端或內部網路上的遠端伺服器。
最終,與 Claude Desktop 的通信也將通過標準輸入輸出(stdio)進行。

文章概要

  1. 準備 MCP 伺服器(TypeScript)
    1. 建立 MCP 伺服器
    2. 通過 dotenv 讀取環境變數
    3. 以工具形式公開專用的 SELECT 查詢讀取 API
    4. 實現 SELECT 專用的安全執行
    5. 將錯誤和執行的 SQL 輸出到日誌檔
  2. 使用 MCP Inspector 進行運作確認
  3. 在 Claude Desktop 中新增 mcp 設定,實現「自然語言 → SQL」的對話流程

ℹ️ 附註:本文章不涉及如何設置與 DB(Azure SQL Database)的連接。請根據您的環境自行設置連接信息。

🧪 成果:當您對 Claude 說「告訴我最近的訪問日誌」時,MCP 伺服器會安全地訪問 DB 並返回摘要。

背景與目標

  • 使用者希望以自然語言訪問存儲在 DB 中的內部信息。
  • 以往需要使用 LangChain 等框架自行構建自然語言 → SQL 的轉換處理。
  • 利用 MCP 可以標準化工具整合和輸入輸出的交互,並使得將自然語言轉換為 SQL 的設計更容易委託給 LLM。
    因此,從零開始構建專用的編排層所需的工時將大幅減少。
  • 首先在本地環境中構建 MCP 伺服器,並驗證其能否按照預期運行。

整體架構

Claude Desktop ──(MCP stdio)──> Node.js MCP 伺服器 ──> Azure SQL Database
       ▲                                            ▲
       │                                            │
  自然語言提示                               Entra ID 認證
  • 通信使用 stdio
  • DB 連接使用 Entra ID 認證

事前準備

1) 環境變數(範例)

在根目錄下創建 .env 文件,並填寫連接的 DB 信息等。

# DB 連接
DB_SERVER=your-sql-server.database.windows.net
DB_PORT=1433
DB_NAME=your_db

# 認證
AUTH_TYPE=azure-active-directory-default
AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

2) 套件

安裝所需的 npm 套件。

npm i @modelcontextprotocol/sdk mssql dotenv @azure/identity

實作重點

建立 MCP 伺服器

以下代碼為在本地執行 MCP 伺服器的最小配置。
這裡假設其位於 src/index.ts 中。

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
    CallToolRequestSchema,
    ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import dotenv from "dotenv";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";

const server = new Server(
    {
        name: "xxx-server",
        version: "1.0.0",
    },
    {
        capabilities: {
            tools: {},
        },
    }
);

// 工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
    // 後述
});

// 工具執行
server.setRequestHandler(CallToolRequestSchema, async (request) => {
    // 後述
});

// 啟動伺服器
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
}

main().catch(() => process.exit(1));

通過 dotenv 讀取環境變數

筆者曾在此處遇到困難,將 dotenv.config() 放在 main 函數之外會導致其在 server.connect() 前執行,從而在 stdio 的通信中混入,導致測試時發生錯誤。
需要注意 dotenv 的調用位置。

async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);

    // 獲取當前文件目錄
    const __dirname = dirname(fileURLToPath(import.meta.url));
    // 調整以便無論從 src / dist 執行都指向根目錄
    const rootDir = resolve(__dirname, '..', '..');
    const envPath = resolve(rootDir, '.env');
    dotenv.config({ path: envPath });
}

以工具形式公開專用的 SELECT 查詢 API

公開一個可以安全執行 SELECT 查詢的工具。
還可以獲取結構、表格列表等信息。
(如有安全方面的限制或改善建議,請隨時告訴我們 🙏)

server.setRequestHandler(ListToolsRequestSchema, async () => {
    return {
        tools: [
            {
                name: "query_database",
                description: "對 SQL Server 數據庫執行 SELECT 查詢(只讀)",
                inputSchema: {
                    type: "object",
                    properties: {
                        query: {
                            type: "string",
                            description: "要執行的 T-SQL 查詢(僅限 SELECT)",
                        },
                    },
                    required: ["query"],
                },
            },
            {
                name: "get_table_schema",
                description: "獲取表格的架構信息",
                inputSchema: {
                    type: "object",
                    properties: {
                        table_name: {
                            type: "string",
                            description: "表格名稱(可帶架構: dbo.Customers)",
                        },
                    },
                    required: ["table_name"],
                },
            },
            {
                name: "list_tables",
                description: "獲取數據庫內的表格列表",
                inputSchema: {
                    type: "object",
                    properties: {
                        schema: {
                            type: "string",
                            description: "架構名稱(預設: dbo)",
                            default: "dbo",
                        },
                    },
                },
            },
        ],
    };
});

SELECT 專用的安全執行

關於 CallToolRequestSchema 的詳細實作(包括 DB 連接處理)本文章將不再贅述,但這裡對工具 query_database 做補充說明。
該工具允許輸入任意 SQL 查詢。
然而,為了防止不慎執行更新類的 SQL,除了將 DB 權限設置為只讀之外,還加上了簡單的查詢限制,如下所示。

function isSafeQuery(query: string): boolean {
    const normalized = query.trim().toUpperCase();

    // 檢查是否以 SELECT 或 WITH 開頭
    const isSelectQuery = /^(SELECT|WITH)\b/.test(normalized);
    if (!isSelectQuery) return false;

    // 危險關鍵字列表
    const dangerous = [
        "DROP", "DELETE", "INSERT", "UPDATE", "ALTER",
        "CREATE", "TRUNCATE", "EXEC", "EXECUTE", "SP_", "XP_"
    ];

    // 如果包含危險關鍵字則返回 false
    const containsDangerous = dangerous.some(keyword =>
        new RegExp(`\\b${keyword}\\b`, "i").test(normalized)
    );

    return !containsDangerous;
}

將錯誤和執行的 SQL 輸出到日誌檔

通過向 stderr 和日誌文件同時輸出,可以在不干擾通信的情況下留下記錄(因為 MCP 客戶端將 stderr 視為日誌並忽略它,因此是安全的)。

function log(level: LogLevel, message: string, data?: any) {
    const timestamp = new Date().toISOString();
    const logMessage = data
        ? `[${timestamp}] [${level}] ${message} ${JSON.stringify(data)}`
        : `[${timestamp}] [${level}] ${message};`;

    // stderr 輸出(MCP 要求)
    console.error(logMessage);

    // 同時寫入文件
    try {
        appendFileSync(LOG_FILE, logMessage + '\n', 'utf8');
    } catch (error) {
        console.error(`無法寫入日誌到文件: ${error}`);
    }
}

使用 MCP Inspector 進行運作確認

通過以下命令啟動 MCP Inspector。

npx @modelcontextprotocol/inspector tsx src/index.ts

執行後,瀏覽器將啟動並顯示 MCP 伺服器的測試 UI。
啟動 MCP Inspector

點擊 Connect → List Tools,您可以查看伺服器公開的工具列表。
MCP Inspector 工具列表

選擇 get_table_schema 並執行,即可獲取指定表的架構信息。
MCP Inspector 工具執行

Claude Desktop

MCP 設定

當到此為止的運作確認完成後,將 MCP 設定新增到 Claude Desktop。

{
  "mcpServers": {
    "qiita-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"]
    }
  }
}

重啟後,在「設定 → 連接器」中可以查看 MCP 伺服器。如果配置正確,則可以打開工具列表並連接。

實演 — 與 Claude 對話

如以下截圖所示,對 Claude 說話時,MCP 伺服器會生成 SQL,並從 DB 中獲取結果來回答您。
Claude Desktop 提示範例
Claude Desktop 回答範例

反思

已確認可以通過 LLM(Claude)以自然語言獲取內部 DB 的信息。
能夠在不撰寫任何 SQL 轉換處理的情況下實現,讓我感到極大的可能性。

MCP 被稱為“AI 的 USB-C”,未來不僅 Claude Desktop 的客戶端可以連接,也可以期待其他客戶端的支持。

接下來,我希望實現特定銷售視圖的專用工具,進一步推進更實務的應用。
此外,這次是本地執行,下一步我也想挑戰作為可串流的 HTTP 伺服器進行遠端部署。

如果有改善建議或任何提議,請隨時在評論中告訴我們!


原文出處:https://qiita.com/kunitomo926/items/3424fbab1160de8ed9ed


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝27   💬3   ❤️6
626
🥈
我愛JS
📝1   💬8   ❤️1
68
🥉
御魂
💬1  
4
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付