備註
.NET 即將提供對自行託管 MCP 工具的支援。
Go 透過結合 tool/agenttool、tool/mcptool 與官方 MCP Go SDK 來將代理程式提供為 MCP 工具。 使用 agenttool.New 包裝 Agent、使用 mcptool.AddTool 註冊,並透過 MCP 傳輸提供服務。
持續維護的 Foundry 範例使用 stdio。 它需要 Foundry 專案端點、模型部署及 Azure 憑證:
// Copyright (c) Microsoft. All rights reserved.
// This sample shows how to expose an Agent as an MCP tool.
package main
import (
"context"
"github.com/microsoft/agent-framework-go/agent"
"github.com/microsoft/agent-framework-go/examples/internal/demo"
"github.com/microsoft/agent-framework-go/provider/foundryprovider"
"github.com/microsoft/agent-framework-go/tool/agenttool"
"github.com/microsoft/agent-framework-go/tool/mcptool"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// Run "npx @modelcontextprotocol/inspector go run ." to connect to this MCP server.
func main() {
token := demo.FoundryTokenCredential()
// Create Microsoft Foundry agent with the same configuration as the C# example.
agent := foundryprovider.NewAgent(
demo.FoundryProjectEndpoint,
token,
foundryprovider.ModelDeployment(demo.FoundryModel),
foundryprovider.AgentConfig{
Instructions: "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
Config: agent.Config{
Name: "Joker",
Description: "An agent that tells jokes.",
},
},
)
// Create an MCP server with the agent exposed as a tool.
srv := mcp.NewServer(&mcp.Implementation{
Name: "agent-mcp-server",
Version: "1.0.0",
}, nil)
// Register the agent as an MCP tool.
mcptool.AddTool(srv, agenttool.New(agent, agenttool.Config{}))
// Run the MCP server with StdIO transport.
if err := srv.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
demo.Panic(err)
}
}
請參閱 MCP 工具整合 以了解客戶端與伺服器模式,以及 完整的 MCP 伺服器範例 以了解執行說明。
使用 agent-framework-hosting-mcp 將 Agent Framework 代理程式或工作流程在原生 Model Context Protocol SDK 中公開為工具。 這個套件不會選擇網頁框架,也不會包裝 MCP SDK 伺服器生命週期。 你的應用程式仍然擁有 MCP 伺服器、處理器註冊、傳輸、會話金鑰政策、認證、授權和部署。
pip install --pre agent-framework-hosting-mcp
在協定邊界進行轉換
mcp_to_run(...) 將已驗證的 MCP 工具參數轉換為代理框架訊息及選定的聊天選項,並將 mcp_from_run(...) 完成的回應轉換為原生 MCP ContentBlock 值。 當應用程式的工具合約需要完全自訂的原生結構描述與處理常式時,請直接使用這兩個函式:
"properties": {
TASK_ARGUMENT: {
"type": "string",
"description": "The request for the hosted agent.",
},
**CHAT_OPTION_ARGUMENTS,
},
"required": [TASK_ARGUMENT],
"additionalProperties": False,
},
)
]
)
async def call_tool(
_ctx: ServerRequestContext[dict[str, Any]], params: types.CallToolRequestParams
) -> types.CallToolResult:
"""Convert, run, and render without the agent-backed adapter."""
name = params.name
arguments = params.arguments or {}
if name != "run_agent_manually":
raise MCPError(types.INVALID_PARAMS, f"Unknown MCP tool: {name}")
run = mcp_to_run(
arguments,
argument_name=TASK_ARGUMENT,
chat_option_arguments=CHAT_OPTION_ARGUMENTS,
)
result = await agent.run(run["messages"], options=run["options"])
return types.CallToolResult(content=mcp_from_run(result))
server = Server("agent-framework-hosting-mcp-manual-sample", on_list_tools=list_tools, on_call_tool=call_tool)
credential = DefaultAzureCredential()
只有 中 chat_option_arguments 列出的參數名稱會被複製到 run["options"];其他 MCP 參數仍保留在訊息的原始表示中,但不會轉發給模型用戶端。
使用 MCPServer 裝載
對於 MCP SDK 2.x 應用程式,請使用 MCPServer 從裝飾函式衍生原生 MCP 工具架構。 在轉換完成的代理框架回應後,明確回傳 CallToolResult :
from contextlib import asynccontextmanager
from typing import Literal
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_hosting_mcp import mcp_from_run, mcp_to_run
from azure.identity.aio import DefaultAzureCredential
from mcp import types
from mcp.server.mcpserver import MCPServer
credential = DefaultAzureCredential()
agent = Agent(
client=FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["FOUNDRY_MODEL"],
credential=credential,
),
name="MCPHostedAgent",
description="Answer a request with the hosted Agent Framework agent.",
instructions="Answer the user's request clearly and concisely.",
)
@asynccontextmanager
async def lifespan(_server: MCPServer[None]) -> AsyncGenerator[None]:
"""Close the model credential when the MCP server stops."""
async with credential:
yield
server = MCPServer(
name="agent-framework-hosting-mcpserver-sample",
instructions="Expose an Agent Framework agent as an MCP tool.",
lifespan=lifespan,
)
@server.tool(
name="run_agent",
description="Run the hosted Agent Framework agent.",
structured_output=False,
)
async def run_agent(
task: str,
reasoning_effort: Literal["low", "medium", "high"] | None = None,
) -> types.CallToolResult:
"""Run the agent with MCPServer-validated arguments."""
範例使用無狀態可串流 HTTP。 在將伺服器暴露於受信任的本地環境之外之前,先加入應用程式自有的認證、授權與會話持久性。
將代理作為一個生成工具來架設
AgentMCPTool 從代理衍生出原生工具名稱、描述與結構,並保持列表、解析、執行與結果轉換保持一致,避免兩者漂移:
) -> types.CallToolResult:
"""Run the app-owned tool with native MCP and Agent Framework values."""
return await agent_tool.call_tool(params.name, params.arguments)
server = Server("agent-framework-hosting-mcp-sample", on_list_tools=list_tools, on_call_tool=call_tool)
credential = DefaultAzureCredential()
agent = Agent(
client=FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["FOUNDRY_MODEL"],
credential=credential,
),
name="MCPHostedAgent",
description="Answer a request with the hosted Agent Framework agent.",
instructions="Answer the user's request clearly and concisely.",
)
agent_tool = AgentMCPTool(
agent,
name="run_agent",
argument_description="The request for the hosted agent.",
chat_option_parameters={
"reasoning_effort": {
"type": "string",
除非覆寫,否則 AgentMCPTool 會使用代理程式的名稱與描述。
parameters 新增應用程式擁有的 JSON Schema 屬性,這些屬性仍保留在原始 MCP 參數中,並 chat_option_parameters 新增可明確複製至 Agent Framework 聊天選項的屬性。
每次呼叫持續一個工作階段
傳遞現有的 AgentState 和 session_id_parameter,讓具有相同、應用程式定義的模糊 session_id 的重複呼叫能夠繼續同一個交談:
state = AgentState(agent)
agent_tool = AgentMCPTool(
state,
name="run_agent",
argument_description="The request for the hosted agent.",
parameters={
"session_id": {
"type": "string",
"minLength": 1,
"description": (
"Opaque, app-defined conversation key. Reuse the same value to continue and update one conversation."
),
}
},
required_parameters={"session_id"},
chat_option_parameters={
"reasoning_effort": {
"type": "string",
AgentMCPTool 僅執行 AgentState get /run/set 這個會話序列;你的應用程式必須認證或授權會話識別碼,並序列化同一會話的並行呼叫,就像範例中對每個會話 asyncio.Lock所做的 。 這不是 previous_response_id 風格的分支 — 需要分岔交談的應用程式應該接受獨立的來源與目的識別碼、複製來源工作階段,並將結果儲存在目的地金鑰下方。
將工作流程當作工具來架設
WorkflowMCPTool 從工作流程的啟動-執行器輸入型態衍生出一個原生 MCP 工具,並將完成的工作流程輸出轉換。 Dataclass、Pydantic 及其他物件形狀輸入成為頂層 MCP 參數;原始輸入會被包裹在可配置的參數名稱中:
async def list_tools(
_ctx: ServerRequestContext[dict[str, Any]], params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:
工作流程執行個體會保留執行狀態,因此需要獨立呼叫的應用程式應提供具有 cache_target=False 的 WorkflowState 工廠,如上所示。 檢查點還原、人機互動回應和接續識別碼仍由應用程式擁有;若工作流程要求外部輸入,介面卡會引發例外狀況,而不是傳回空的成功工具結果。
如需查看完整的可執行伺服器組合(包括 MCPServer 變體),請參見 MCP 主機範例。
這很重要
將 MCP 會話識別碼及任何應用程式定義 session_id 的參數視為不可信輸入。 在使用前,先驗證並授權呼叫者,以載入或儲存會話狀態,並從已認證的租戶、使用者或工作區而非原始值中推導出持久的分割。
下一步
深入探討: