Selbstgehostete Agents als MCP-Tools

Note

Die Self-Hosting-MCP-Toolunterstützung in .NET wird in Kürze verfügbar sein.

Note

Self-Hosting-MCP-Toolunterstützung ist derzeit nicht für Go verfügbar.

Verwenden Sie agent-framework-hosting-mcp, um einen Agent Framework-Agenten oder Workflow als Werkzeug im nativen Model Context Protocol-SDK verfügbar zu machen. Das Paket wählt kein Webframework aus oder umschließt den MCP SDK-Serverlebenszyklus; Ihre Anwendung besitzt weiterhin die Handlerregistrierung, transport Server, Sitzungsschlüsselrichtlinie, Authentifizierung, Autorisierung und Bereitstellung.

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

An der Protokollgrenze konvertieren

mcp_to_run(...) konvertiert überprüfte MCP-Toolargumente in Agent Framework-Nachrichten und ausgewählte Chatoptionen und mcp_from_run(...) konvertiert eine abgeschlossene Antwort in systemeigene MCP-Werte ContentBlock . Verwenden Sie diese beiden Funktionen direkt, wenn der Toolvertrag einer Anwendung ein vollständig benutzerdefiniertes systemeigenes Schema und einen Handler benötigt:

@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)

Nur die in chat_option_arguments aufgeführten Argumentnamen werden in run["options"] kopiert; andere MCP-Argumente bleiben in der Rohdarstellung der Nachricht verfügbar, werden jedoch nicht an den Modellclient weitergeleitet.

Hosten eines Agents als ein generiertes Tool

AgentMCPTool leitet den nativen Tool-Namen, die Beschreibung und das Schema von einem Agenten ab und hält Auflistung, Parsing, Ausführung und Ergebniskonvertierung aufeinander abgestimmt, sodass sie nicht auseinanderlaufen können:

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 verwendet den Namen und die Beschreibung des Agents, es sei denn, sie werden außer Kraft gesetzt. parameters Fügt appeigene JSON-Schemaeigenschaften hinzu, die in den unformatierten MCP-Argumenten verfügbar bleiben, und chat_option_parameters fügt Eigenschaften hinzu, deren Werte explizit in Agent Framework-Chatoptionen kopiert werden.

Beibehalten einer Sitzung pro Anruf

Übergeben Sie eine vorhandene AgentState und eine session_id_parameter, damit wiederholte Aufrufe mit demselben opaken, von der App definierten session_id dieselbe Unterhaltung fortsetzen:

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 führt nur die AgentState Abfolge „get/run/set“ für Sitzungen aus; Ihre Anwendung muss die Sitzungskennung authentifizieren oder autorisieren und gleichzeitige Aufrufe für dieselbe Sitzung serialisieren, wie es das Beispiel mit einem asyncio.Lock pro Sitzung tut. Dies ist keine previous_response_id-Verzweigung – eine Anwendung, die eine Konversation verzweigen muss, sollte getrennte Quell- und Ziel-IDs akzeptieren, die Quellsitzung kopieren und das Ergebnis unter dem Zielschlüssel speichern.

Hosten eines Workflows als Tool

WorkflowMCPTool erstellt ein natives MCP-Tool aus dem Eingabetyp des Start-Executors eines Workflows und konvertiert die Ausgaben abgeschlossener Workflows. Datenklassen-, Pydantische und andere objektförmige Eingaben werden zu MCP-Argumenten der obersten Ebene; Primitive Eingaben werden in einen konfigurierbaren Argumentnamen eingeschlossen:

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

Workflowinstanzen behalten ihren Ausführungszustand bei, sodass Anwendungen, die unabhängige Aufrufe benötigen, wie oben gezeigt eine WorkflowStateFactory mit cache_target=False bereitstellen sollten. Prüfpunktwiederherstellung, Menschliche-in-the-Loop-Antworten und Fortsetzungs-IDs bleiben anwendungseigene; wenn ein Workflow externe Eingaben anfordert, löst der Adapter aus, anstatt ein leeres erfolgreiches Toolergebnis zurückzugeben.

Die vollständige Sammlung der ausführbaren Server – einschließlich der FastMCP-Variante, die ihr Schema aus einer dekorierten Funktion ableitet – finden Sie in den MCP-Hostingbeispielen.

Important

Behandeln Sie den MCP-Sitzungsbezeichner und jedes app-definierte session_id Argument als nicht vertrauenswürdige Eingabe. Authentifizieren und autorisieren Sie den Aufrufer, bevor Sie ihn zum Laden oder Speichern des Sitzungszustands verwenden, und leiten Sie die persistente Partitionierung aus dem authentifizierten Mandanten, Benutzer oder Arbeitsbereich ab statt aus dem Rohwert.

Nächste Schritte

Gehen Sie tiefer: