iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
生成式 AI

當 .NET 遇見 AI Agents:用 Semantic Kernel × MCP 打造智慧協作應用系列 第 24

Day 24: Semantic Kernel 使用 Stdio 模式串接 MCP Server

  • 分享至 

  • xImage
  •  

在 Day 23 中,介紹如何使用 Semantic Kernel 使用 Streamable HTTP 模式串接 MCP Server ,今天來另一種 Stdio 模式串接 MCP Server。Stdio 模式是透過標準輸入輸出進行通訊,適合在本地環境或受限網路環境中使用,Stdio 模式下,MCP Server 會與執行在本地端的 MCP Client(例如 LLM runtime 或自訂應用)透過標準輸入輸出來交換 JSON-RPC 訊息。簡單來說,就是在本地端啟動一個 MCP Server 程式,然後「透過標準輸入輸出進行通訊」與 MCP Client(你的 LLM 應用) 進行互動,過程中 MCP Client 會把請求透過 stdin 傳給 MCP Server,MCP Server 再透過 stdout 把回應送回去,整個過程像是在同一個機器內部用管線(pipe)傳資料。

為了示範 Stdio 模式,需要先建立一個 MCP Server 的範例程式,這裡我使用 .NET 主控台應用程式來實現,它提供了訂單查詢的功能,可以使用訂單編號查詢或是客戶姓名關鍵字模糊查詢。此外要建立本地 MCP Server 範例程式,還需要官方的 MCP .NET 套件 ModelContextProtocol 的支援。

建立 MCP Server 範例程式

  • 建立 .NET 主控台應用程式並安裝必要的 MCP 官方.NET 套件,以及self-hosting套件
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting --version 9.0.6
  • 建立要暴露的工具類別 OrderTool,提供兩個方法 GetOrderByIdSearchOrdersByCustomerName,分別用於根據訂單編號查詢訂單和根據客戶姓名關鍵字模糊查詢訂單。作為 MCP Server 工具,必須使用 [McpServerToolType] 標註類別,並使用 [McpServerTool] 標註方法,這樣 MCP Server 才能識別並暴露這些工具給 MCP Client 使用。另外,使用 Description 屬性來提供方法的描述,這部份相關重要,它是有助於 MCP Client 理解工具的功能。
[McpServerToolType]
public class OrderTool
{
    // --- 模擬資料(實務上應該從資料庫取得) ---
    private static readonly List<OrderDto> _orders =
    [
        new(1001, "王小美",  new(2025, 6, 1), 1299, "已出貨"),
        new(1002, "陳錢錢",  new(2025, 6, 3), 2599, "處理中"),
        new(1003, "阿土伯",  new(2025, 6, 5),  499, "已取消")
    ];

    // 依訂單編號查詢
    [McpServerTool,
     Description("Query order data by order ID")]
    public OrderDto? GetOrderById(int orderId) =>
        _orders.FirstOrDefault(o => o.Id == orderId);

    // 依客戶姓名關鍵字模糊查詢
    [McpServerTool,
     Description("Query order list by customer name keyword")]
    public IEnumerable<OrderDto> SearchOrdersByCustomer(string keyword) =>
        _orders.Where(o =>
            o.Customer.Contains(keyword, StringComparison.OrdinalIgnoreCase));
}
  • 在主程式 Program.cs,使用 AddMcpServer 來建立 MCP Server 主機,並在指定 WithStdioServerTransport 使用使用 Stdio 傳輸方式,最後透過 WithToolsFromAssembly掃描工具類,將工具加入到 MCP Server 中,最後用 await builder.Build().RunAsync() 啟動 MCP Server 。
Console.WriteLine("Hello, MCP Server!");

var builder = Host.CreateApplicationBuilder(args);

// 建立 MCP Server,採用 Stdio 傳輸並自動掃描工具
builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()
    .WithToolsFromAssembly();    // 掃描工具類別


await builder.Build().RunAsync();

使用 Semantic Kernel 串接本地 MCP Server

  • 建立 Kernel ,這裡我們使用 OpenAI 的 GPT-4.1 模型作為 LLM,並設定 API Key 和 Model ID
 Kernel kernel = Kernel.CreateBuilder()
                .AddOpenAIChatCompletion(
                    apiKey: Config.OpenAI_ApiKey,
                    modelId: Config.ModelId)
                .Build();
  • 配置 MCP Server 的連線,這裡我們使用 Stdio 模式連接本地的 MCP Server 範例程式
    • Name = OrderServer,做為 MCP Server 的名稱識別
    • Command = dotnet,指定執行的命令,由於 MCP Server 是 .NET 主控台應用程式,所以這裡使用 dotnet 命令來啟動
    • Arguments = ["run", "--project", "../mymcpserver"],指定 dotnet 命令的參數,這裡使用 run 來執行指定路徑的 MCP Server 專案,根據專案路徑調整 --project 的值
 //連線到 MCP Server (stdio模式,指定MCP Server Project Path)
var clientTransport = new StdioClientTransport(new()
{
    Name = "OrderServer",
    Command = "dotnet",
    Arguments = ["run", "--project", "../mymcpserver"]
});
  • 建立 MCP Client,並將其加入到 Kernel 中
 await using var mcpClient = await McpClient.CreateAsync(clientTransport!);
  • 檢視是否成功連接到 MCP Server,並取得可用的工具清單
// 取得 MCP Server 工具清單
var tools = await mcpClient.ListToolsAsync();
// 工具清單顯示
foreach (var tool in tools)
{
    Console.WriteLine($"Connected to My MCP server with tools: {tool.Name}");
}

  • 將本地 MCP Server 暴露的工具加入到 Kernel 中
// 匯入工具並組裝 Agent
kernel.Plugins.AddFromFunctions("McpTools", tools.Select(t => t.AsKernelFunction()));
  • 建立 Agent,並使用 MCP 工具來回答問題
// 建立 Agent
ChatCompletionAgent agent =
    new()
    {
        Name = "SupportAgent",
        Description = "一個可以回答訂單資訊的助手",
        Instructions = @"你是一位專業且有禮貌的助手,負責協助顧客查詢訂單資訊。請依照以下規則提供回覆:
                        1. 如果詢問訂單狀態,請引導提供訂單編號或是顧客姓名,並使用已提供的查詢工具查詢對應資料。
                        2. 回覆時要友善、清楚,避免使用過於技術化的語言。
                        3. 若問題無法直接回答,請告知「我會轉交給專人協助」,並避免捏造答案。
                        4. 僅限提供查詢訂單狀態,若超出職責範圍,請禮貌回覆並引導聯繫客服專線0800-888-888。",
        Kernel = kernel,
        Arguments = new(new PromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() })
    };

// 建立對話歷史 thread
ChatHistoryAgentThread agentThread = new();

Console.Write("User > ");
string? userInput;
while ((userInput = Console.ReadLine()) is not null)
{
    if (string.IsNullOrWhiteSpace(userInput) || userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
        break;

    ChatMessageContent message = new(AuthorRole.User, userInput);

    bool isFirst = false;

    await foreach (StreamingChatMessageContent response in agent.InvokeStreamingAsync(message, agentThread))
    {

        if (string.IsNullOrEmpty(response.Content))
        {
            StreamingFunctionCallUpdateContent? functionCall = response.Items.OfType<StreamingFunctionCallUpdateContent>().SingleOrDefault();

            //追蹤函數調用
            if (!string.IsNullOrEmpty(functionCall?.Name))
            {
                Console.WriteLine($"\n# trace {response.Role} - {response.AuthorName ?? "*"}: FUNCTION CALL - {functionCall.Name}");
            }
            continue;
        }

        if (!isFirst)
        {
            Console.Write($"{response.Role} - {response.AuthorName ?? "*"} > ");
            isFirst = true;
        }

        Console.Write($"{response.Content}");
    }
    Console.WriteLine();
    Console.WriteLine($"\n# trace chat thread with agent: {agent.Name} - {agent.Description},threadId: {agentThread.Id} \n");
    Console.Write("User > ");
}
  • 執行結果,可看到成功連接到 MCP Server 並取得可用的工具清單,問答中使用 MCP 工具來回答問題
Connected to My MCP server with tools: search_orders_by_customer
Connected to My MCP server with tools: get_order_by_id
User > 阿土伯的訂單現在情況是什麼

# trace Assistant - SupportAgent: FUNCTION CALL - McpTools-search_orders_by_customer
assistant - SupportAgent > 阿土伯的訂單目前狀態是「已取消」。如果您有其他訂單需要查詢,或需要更多協助,歡迎提供訂單編號或進一步告知。我很樂意協助您!

# trace chat thread with agent: SupportAgent - 一個可以回答訂單資訊的助手,threadId: 7283c2f704d9415aaafd02e7b407e76a

結言

透過 Stdio 模式串接 MCP Server,可以在本地環境中打造 MCP Server + MCP Client 的架構,適合在受限網路環境中使用,或是本地工具的整合,例如讀取本地的檔案系統,或是調用本地CLI服務等,同時也利用 MCP Server 提供的工具來增強 LLM 的能力。簡單來說,Stdio 適合「本地、桌面、工具類」的應用場景,而 Streamable HTTP 適合「Web、遠程、服務類」的場景!


上一篇
Day 23: Semantic Kernel 使用 Streamable HTTP 模式串接 MCP Server
下一篇
Day 25: Semantic Kernel 無縫整合 OpenAI Assistant Agent - 智能客服系統實作
系列文
當 .NET 遇見 AI Agents:用 Semantic Kernel × MCP 打造智慧協作應用25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言