MCP 도구로 사용하는 자체 호스팅 에이전트

비고

.NET 자체 호스팅 MCP 도구 지원이 곧 제공될 예정입니다.

Go는 tool/agenttool, tool/mcptool, 그리고 공식 MCP Go SDK를 결합하여 에이전트를 MCP 도구로 노출합니다. 에이전트를 agenttool.New로 래핑하고, 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 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는 원시 MCP 인수에서 계속 사용 가능한 앱 소유의 JSON 스키마 속성을 추가하고, 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 . 애플리케이션은 세션별 asyncio.Lock샘플과 마찬가지로 세션 식별자를 인증하거나 권한을 부여하고 동일한 세션에 대한 동시 호출을 직렬화해야 합니다. 이는 스타일 분기가 아닙니다 previous_response_id. 대화를 포크해야 하는 애플리케이션은 별도의 원본 및 대상 ID를 수락하고, 원본 세션을 복사하고, 결과를 대상 키 아래에 저장해야 합니다.

워크플로를 도구로 호스트

WorkflowMCPTool 는 워크플로의 시작 실행기 입력 형식에서 하나의 네이티브 MCP 도구를 파생시키고 완료된 워크플로 출력을 변환합니다. 데이터 클래스, Pydantic 및 기타 개체 모양의 입력은 최상위 MCP 인수가 됩니다. 기본 입력은 구성 가능한 인수 이름으로 래핑됩니다.



async def list_tools(
    _ctx: ServerRequestContext[dict[str, Any]], params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:

워크플로 인스턴스는 실행 상태를 유지하므로 서로 독립적인 호출이 필요한 애플리케이션은 위와 같이 WorkflowState를 사용해 cache_target=False 팩터리를 제공해야 합니다. 검사점 복원, 사람 개입형 응답, 그리고 연속 식별자는 애플리케이션이 관리합니다. 워크플로가 외부 입력을 요청하는 경우, 어댑터는 빈 성공 도구 결과를 반환하는 대신 예외를 발생시킵니다.

MCPServer 변형을 포함한 실행 가능한 서버의 전체 집합은 MCP 호스팅 샘플에서 확인하세요.

중요합니다

MCP 세션 식별자 및 앱 정의 session_id 인수를 신뢰할 수 없는 입력으로 처리합니다. 세션 상태를 로드하거나 저장하는 데 사용하기 전에 호출자를 인증하고 권한을 부여하고, 원시 값이 아닌 인증된 테넌트, 사용자 또는 작업 영역에서 지속성 분할을 파생합니다.

다음 단계

더 자세히 살펴보기: