實戰教學 · MCP

用 Python 與 Azure Functions 建立 MCP Server,
讓 AI 呼叫自己的請假工具

讓 AI 找到串接企業的功能,知道該傳什麼參數,並把執行結果帶回對話。

如果我們希望 AI 回答「David 到目前為止請了幾天假?」,甚至讓企業內的使用者,可以透過AI Agent來請假,光靠模型本身是不夠的。這個答案應該來自公司的系統,因此要串接到企業內的資料庫,而不是讓模型根據對話猜一個數字。

那接下來的問題就是:怎麼讓 AI 找到串接企業的功能,知道該傳什麼參數,並把執行結果帶回對話?

這個範例用 Python 與 Azure Functions 建立一個 MCP Server,再透過 VS Code 裡的 AI Agent 呼叫它。情境選的是請假:查詢員工的請假天數、送出請假資訊,以及取得目前日期。

把這個互動跑通,就能把函式裡的示範資料換成真正的資料庫查詢或既有 API,實現與企業系統整合的功能。

本次示範完成的是本機開發與呼叫測試,還沒有部署到 Azure。完整範例我放在我的GitHub Rep leave-request-mcp-tools-by-azure-func。

MCP 把程式功能描述給 AI 使用

MCP(Model Context Protocol)提供一套共同的溝通方式,讓支援它的應用程式(AI Agent)得以找到外部工具並提出呼叫。在這個範例裡,VS Code 是使用工具的一端(也就是AI Agent);Azure Functions 提供工具的執行環境(run-time);Python 函式負責處理實際工作。

工具需要有名稱、用途說明與參數定義。例如 get_leave_record_amount 的用途是「取得請假天數」,參數 employeeName 則表示要查詢哪一位員工。當使用者問「David 到目前為止請了幾天假」,Agent 可以根據這些資訊,選擇適合的工具,把 David 放進參數,再取得執行結果。

因此,工具的描述也是介面的一部分。只寫一個模糊的名稱,AI 未必能判斷什麼時候應該呼叫;用途與參數說清楚,才比較容易把自然語言需求對應到正確的程式功能。

這裡也有一個分工:模型負責理解問題與組織回覆,請假天數仍由程式提供。對企業應用來說,這讓既有的業務規則與資料來源可以繼續留在後端。

Azure Functions 如何變成 MCP 工具

我們可以先使用 VS Code 的 Azure Functions 擴充功能建立 Python 專案,並選擇 HTTP Trigger 作為起始範本。那份範本展示的是一般 HTTP 請求與回應,之後會被 MCP 工具的程式碼替換。

這個差異要分清楚:不是選了 HTTP Trigger,函式就自動變成 MCP Tool。真正把功能公開成工具的是後來加入的 MCP Trigger 定義。參考影片中的做法,我們採用 @app.generic_trigger,並將 type 設為 mcpToolTrigger。

以下整理錄影中查詢工具的關鍵結構,省略其他工具:

@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="get_leave_record_amount",
    description="取得請假天數",
    toolProperties='[{"propertyName":"employeeName",'
                   '"propertyType":"string",'
                   '"description":"員工名稱"}]'
)
def get_leave_record_amount(context: str) -> str:
    request = json.loads(context)
    arguments = request.get("arguments", {})
    employee_name = str(arguments.get("employeeName", ""))

    leave_days = {"david": 5, "eric": 8}.get(
        employee_name.lower(), 3
    )
    return json.dumps({"content": str(leave_days)})

上方的裝飾器描述工具,下方的函式執行工作。其中 toolName 是呼叫時使用的識別名稱;description 說明用途;toolProperties 描述輸入參數。進入函式後,程式從 context 的 JSON 內容取出 arguments,再讀取 employeeName。

這支函式刻意把資料寫得很簡單:David 回傳 5,Eric 回傳 8,其他名稱則回傳預設值 3。這是用來驗證工具呼叫的示範資料,當然不是真的人事紀錄。

但若要接正式系統,可以把模擬的查詢替換成資料庫或 API,並明確處理「查無此員工」之類的狀況,即可完成。

目前官方也提供 Python 專用的 @app.mcp_tool_trigger 裝飾器;影片使用的是 generic trigger 的寫法。新建專案時,應依採用的套件版本選擇相容語法。參考 MCP Tool Trigger 官方文件。

為什麼請假範例還需要日期工具

範例另外提供 leave_request 與 get_current_date。前者接收請假資訊並回傳訊息,後者提供目前的日期時間。

日期工具的用途很直接。當使用者說「我要從明天開始請兩天年假」,「明天」是一個相對日期,需要有明確的今天作為基準。把目前時間交給工具提供,Agent 就有機會先取得日期,再整理後續請假需要的參數。

實際應用仍要決定時區與工作日規則,不能只把兩天理解成任意的 48 小時。

錄影中的 leave_request 沒有真的寫入資料庫,只是整理並回傳訊息;實際展示的對話測試則是查詢 David 的請假天數。因此,這個範例驗證的是工具公開、參數傳遞與回傳結果,還不是一套完整的請假簽核系統。

設定檔各自負責不同的事情

開發這類專案時,容易把幾個 JSON 檔混在一起。它們雖然都叫設定檔,讀取它們的對象卻不同。

host.json 提供 Functions Host 的設定,包括擴充套件相關資訊。local.settings.json 放本機執行所需的設定,例如 Python Worker 與 AzureWebJobsStorage。function_app.py 則是工具定義與程式邏輯所在的位置。

.vscode/mcp.json 是給 VS Code 使用的。它告訴用戶端應該連到哪一個 MCP Server,而不是用來啟動 Python 函式本身。錄影採用 SSE,設定結構如下:

{
  "servers": {
    "Leave-Request-Tools-Local": {
      "type": "sse",
      "url": "http://localhost:7071/runtime/webhooks/mcp/sse"
    }
  }
}

所以本機測試有兩件事要完成:先準備 Python 虛擬環境與依賴套件,使用 func start 啟動 Functions Host;再讓 VS Code 連接這個端點,取得可用工具。只建立 mcp.json,後端服務不會因此自動啟動。

版本也需要留意。影片使用 Preview extension bundle 與 SSE 設定;目前官方文件建議新的連線使用 Streamable HTTP,對應端點是 /runtime/webhooks/mcp。若要重現影片,應先維持相容的版本組合;若要新建專案,則核對當前文件,避免把不同時期的設定混用。細節可參考 Azure Functions MCP bindings。

從對話看到真正的函式執行

服務啟動後,VS Code 會偵測到三個工具。接著,使用者可以在 Agent Mode 輸入「David 到目前為止請了幾天假?」,Agent 找到查詢工具,將員工名稱帶入,並在這次呼叫過程中要求使用者同意執行。

同意後,終端機出現 get_leave_record_amount 的執行紀錄,聊天視窗也回覆 David 請了 5 天假。將這兩個畫面對照,就能確認工具被執行,而不只是看到一段LLM給出的看起來合理的文字。

這個例子的實用之處,是把原本寫在 Python 裡的功能,接進支援 MCP 的 Agent 工作流程。後續若要接正式人事系統,除了替換資料來源,也需要在後端實作身分、查詢權限、輸入驗證與請假寫入規則。用戶端的「同意執行」是這次工具呼叫的確認,不能取代後端對「誰可以查誰的資料」的判斷。

先用一支簡單的查詢函式把整條呼叫路徑驗證清楚,再逐步接入真正的業務邏輯,問題會比較容易定位:是 Agent 沒選到工具、參數沒有帶對,還是後端執行出了問題。這也是這個小型請假範例最適合拿來練習的地方。

參考資料