Merk
Tilgang til denne siden krever autorisasjon. Du kan prøve å logge på eller endre kataloger.
Tilgang til denne siden krever autorisasjon. Du kan prøve å endre kataloger.
Note
Self-hosting MCP tool support in .NET is coming soon.
Note
Self-hosting MCP tool support is not currently available for Go.
Use agent-framework-hosting-mcp to expose an Agent Framework agent or workflow as a tool on the native Model Context Protocol SDK. The package doesn't choose a web framework or wrap the MCP SDK server lifecycle. Your application still owns the MCP server, handler registration, transport, session-key policy, authentication, authorization, and deployment.
pip install --pre agent-framework-hosting-mcp
Convert at the protocol boundary
mcp_to_run(...) converts validated MCP tool arguments into Agent Framework messages and selected chat options, and mcp_from_run(...) converts a completed response into native MCP ContentBlock values. Use these two functions directly when an application's tool contract needs a fully custom native schema and handler:
"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()
Only argument names listed in chat_option_arguments are copied into run["options"]; other MCP arguments stay available on the message's raw representation but aren't forwarded to the model client.
Host with MCPServer
For MCP SDK 2.x applications, use MCPServer to derive a native MCP tool schema from a decorated function. Return CallToolResult explicitly after converting the completed Agent Framework response:
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."""
The sample uses stateless streamable HTTP. Add application-owned authentication, authorization, and session persistence before exposing the server beyond a trusted local environment.
Host an agent as one generated tool
AgentMCPTool derives the native tool name, description, and schema from an agent, and keeps listing, parsing, execution, and result conversion aligned so the two can't drift:
) -> 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 uses the agent's name and description unless overridden. parameters adds app-owned JSON Schema properties that stay available in the raw MCP arguments, and chat_option_parameters adds properties whose values are explicitly copied into Agent Framework chat options.
Persist a session per call
Pass an existing AgentState and a session_id_parameter to let repeated calls with the same opaque, app-defined session_id continue one conversation:
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 only performs the AgentState session get/run/set sequence; your application must authenticate or authorize the session identifier and serialize concurrent calls for the same session, as the sample does with a per-session asyncio.Lock. This isn't previous_response_id-style branching — an application that needs to fork a conversation should accept separate source and destination IDs, copy the source session, and store the result under the destination key.
Host a workflow as a tool
WorkflowMCPTool derives one native MCP tool from a workflow's start-executor input type and converts completed workflow outputs. Dataclass, Pydantic, and other object-shaped inputs become top-level MCP arguments; primitive inputs are wrapped in a configurable argument name:
async def list_tools(
_ctx: ServerRequestContext[dict[str, Any]], params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:
Workflow instances preserve execution state, so applications that need independent calls should supply a WorkflowState factory with cache_target=False, as shown above. Checkpoint restoration, human-in-the-loop responses, and continuation identifiers remain application-owned; if a workflow requests external input, the adapter raises instead of returning an empty successful tool result.
For the complete set of runnable servers, including the MCPServer variant,
see the MCP hosting samples.
Important
Treat the MCP session identifier and any app-defined session_id argument as untrusted input. Authenticate and authorize the caller before using either to load or save session state, and derive durable partitioning from the authenticated tenant, user, or workspace rather than the raw value.
Next steps
Go deeper: