Агенты самообслуживания в качестве средств MCP

Note

Поддержка самостоятельного размещения инструментов MCP в .NET скоро появится.

Note

Поддержка инструментов MCP для самостоятельного хостинга в настоящее время недоступна для Go.

Используйте agent-framework-hosting-mcp, чтобы предоставить агент Agent Framework или рабочий процесс как инструмент в собственном SDK Model Context Protocol. Пакет не выбирает веб-фреймворк и не управляет жизненным циклом сервера MCP SDK; ответственность за Server, регистрацию обработчиков, транспорт, политику ключей сеанса, аутентификацию, авторизацию и развертывание по-прежнему остаётся на приложении.

pip install --pre agent-framework-hosting-mcp

Преобразование по границе протокола

mcp_to_run(...) преобразует проверенные аргументы инструментов MCP в сообщения Agent Framework и выбранные параметры чата и mcp_from_run(...) преобразует завершенный ответ в собственные значения MCP ContentBlock . Используйте эти две функции непосредственно, когда контракт средства приложения требует полностью настраиваемой собственной схемы и обработчика:

@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Return the app-owned native MCP tool definition."""
    return [
        types.Tool(
            name="run_agent_manually",
            description=agent.description or "",
            inputSchema={
                "type": "object",
                "properties": {
                    TASK_ARGUMENT: {
                        "type": "string",
                        "description": "The request for the hosted agent.",
                    },
                    **CHAT_OPTION_ARGUMENTS,
                },
                "required": [TASK_ARGUMENT],
                "additionalProperties": False,
            },
        )
    ]


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, object] | None) -> list[types.ContentBlock]:
    """Convert, run, and render without the agent-backed adapter."""
    if name != "run_agent_manually":
        raise ValueError(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 mcp_from_run(result)

Только имена аргументов, перечисленные в chat_option_arguments, копируются в run["options"]; другие аргументы MCP остаются доступными в необработанном представлении сообщения, но не пересылаются клиенту модели.

Разместить агента как единый сгенерированный инструмент

AgentMCPTool извлекает имя, описание и схему встроенного инструмента из агента и поддерживает согласованность списка, разбора, выполнения и преобразования результатов, чтобы они не расходились:

agent_tool = AgentMCPTool(
    agent,
    name="run_agent",
    argument_description="The request for the hosted agent.",
    chat_option_parameters={
        "reasoning_effort": {
            "type": "string",
            "enum": ["low", "medium", "high"],
            "description": "Optional reasoning effort for models that support it.",
        }
    },
)


@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Describe the app-owned MCP tool schema."""
    return await agent_tool.list_tools()


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, object] | None) -> list[types.ContentBlock]:
    """Run the app-owned tool with native MCP and Agent Framework values."""
    return await agent_tool.call_tool(name, arguments)

AgentMCPTool использует имя и описание агента, если эти значения не переопределены. parameters добавляет свойства схемы JSON, принадлежащие приложению, которые остаются доступными в необработанных аргументах MCP, и chat_option_parameters добавляет свойства, значения которых явно копируются в параметры чата Agent Framework.

Сохранять сеанс для каждого вызова

Передайте существующий AgentState и session_id_parameter, чтобы повторные вызовы с одним и тем же непрозрачным идентификатором session_id, определяемым приложением, продолжали один и тот же диалог:

session_locks: dict[str, asyncio.Lock] = {}


@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Return the agent-derived MCP tool definition."""
    return await agent_tool.list_tools()


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, object] | None) -> list[types.ContentBlock]:
    """Serialize calls per app-owned session before using ``AgentState``."""
    session_id = arguments.get("session_id") if arguments else None
    if not isinstance(session_id, str) or not session_id:
        raise ValueError("MCP tool argument 'session_id' must be a non-empty string.")
    lock = session_locks.setdefault(session_id, asyncio.Lock())
    async with lock:
        return await agent_tool.call_tool(name, arguments)

AgentMCPTool выполняет только последовательность get/run/set для сеанса AgentState; ваше приложение должно аутентифицировать или авторизовать идентификатор сеанса и сериализовать одновременные вызовы для одного и того же сеанса, как это показано в примере с отдельным asyncio.Lock для каждого сеанса. Это не ветвление в стиле previous_response_id — приложение, которому нужно разветвить диалог, должно принимать отдельные идентификаторы источника и назначения, копировать исходный сеанс и сохранять результат по ключу назначения.

Размещение рабочего процесса в качестве инструмента

WorkflowMCPTool формирует собственный инструмент MCP на основе типа входных данных стартового исполнителя процесса и преобразует выходные данные завершённого процесса. Dataclass, Pydantic и другие входные данные, формируемые объектом, становятся аргументами MCP верхнего уровня; примитивные входные данные упаковываются в настраиваемое имя аргумента:

server = Server("agent-framework-hosting-mcp-workflow-sample")
workflow_tool = WorkflowMCPTool(
    WorkflowState(create_workflow, cache_target=False),
    name="draft_content",
)

Экземпляры рабочего процесса сохраняют состояние исполнения, поэтому приложения, которым требуются независимые вызовы, должны предоставлять фабрику WorkflowState с cache_target=False, как показано выше. Восстановление контрольных точек, ответы с участием человека и идентификаторы продолжения остаются в ведении приложения; если рабочий процесс запрашивает внешний ввод, адаптер генерирует исключение вместо возврата пустого успешного результата инструмента.

Полный набор серверов, готовых к запуску, включая вариант FastMCP, который выводит свою схему из декорированной функции, см. в примерах размещения MCP.

Important

Обработайте идентификатор сеанса MCP и любой аргумент, определенный session_id приложением, как ненадежные входные данные. Проверяйте подлинность и авторизуйте инициатора запроса перед тем, как использовать любой из этих механизмов для загрузки или сохранения состояния сеанса, и определяйте устойчивое разбиение на основе аутентифицированного арендатора, пользователя или рабочей области, а не исходного значения.

Дальнейшие действия

Вернитесь глубже: