Integrating MCP Servers with McpWorkbench for External Tool Access: A Complete Technical Guide

The MCP Workbench in AutoGen Studio provides a WebSocket-based protocol bridge that enables real-time bidirectional communication between the frontend UI and any MCP-compatible external tool server, supporting stdio, SSE, and streamable HTTP transports.

The Microsoft AutoGen repository includes a powerful Model-Context Protocol (MCP) Workbench within AutoGen Studio that simplifies connecting to external tools. This guide explains how to leverage McpWorkbench to integrate MCP servers using a standardized WebSocket gateway architecture that handles protocol translation, session management, and user elicitation.

Architecture Overview

The integration relies on three distinct architectural layers that manage transport, protocol translation, and business logic.

Transport Layer

Located in autogenstudio/web/routes/mcp.py, the mcp_websocket endpoint accepts WebSocket connections and decodes the server_params query string to instantiate the appropriate MCP client. The create_mcp_session helper function determines whether to use stdio, SSE, or streamable HTTP based on the decoded parameters.

Protocol Bridge

The MCPWebSocketBridge class in autogenstudio/mcp/wsbridge.py implements the MCPEventHandler interface. This bridge translates MCP events (initialization, operation results, errors, sampling requests) into JSON messages for the UI, and forwards UI-initiated operations back to the MCP client.

Business Logic Layer

Callbacks for logging protocol messages, handling AI sampling, and managing user input requests reside in autogenstudio/mcp/callbacks.py. Key functions include create_message_handler, create_sampling_callback, and create_elicitation_callback, which convert MCP protocol callbacks into UI-friendly activity logs.

Core Components and File Paths

Understanding the source structure is essential for extending the integration or debugging connection issues.

Data Flow Walkthrough

The communication follows a strict sequence from UI initialization through tool execution and response handling.

  1. Connection Initiation: The UI constructs a WebSocket URL in the format ws://<host>/mcp/ws/<session_id>?server_params=<base64-json>, where server_params encodes the server configuration type and connection details.

  2. Session Creation: The mcp_websocket handler decodes the base64 parameters and invokes create_mcp_session to instantiate the appropriate MCP client (stdio, SSE, or HTTP).

  3. Bridge Registration: The MCPWebSocketBridge registers the client via set_mcp_client and calls mcp_client.initialize(), relaying the initialized event to the UI.

  4. Operation Execution: The UI sends JSON messages with "type": "operation" (e.g., list_tools, call_tool), which the bridge forwards to MCPClient.handle_operation for processing by the underlying MCP library.

  5. Response Handling: Results, errors, and sampling requests flow back through bridge callbacks (on_operation_result, on_sampling, etc.) as typed JSON messages.

  6. Elicitation Management: When tools require user input, the bridge stores an asyncio.Future in pending_elicitations keyed by a UUID. The UI receives an elicitation_request, and when the user responds, the bridge resolves the future to continue the MCP operation.

Implementation Examples

Frontend WebSocket Connection

The React hook in useMcpWebSocket.ts demonstrates how to construct the connection URL and manage the WebSocket lifecycle.

import { v4 as uuidv4 } from "uuid";

function buildMcpWsUrl(
  host: string,
  sessionId: string,
  serverParams: StdioServerParams | SseServerParams | StreamableHttpServerParams
) {
  const json = JSON.stringify(serverParams);
  const b64 = btoa(json);
  return `ws://${host}/mcp/ws/${sessionId}?server_params=${b64}`;
}

// Example: Connect to a local stdio-based tool server
const url = buildMcpWsUrl(
  "localhost:8081",
  uuidv4(),
  {
    type: "StdioServerParams",
    command: "python",
    args: ["-m", "my_tool_server"],
    env: { OPENAI_API_KEY: "*****" }
  }
);

const ws = new WebSocket(url);
ws.onmessage = ev => console.log("MCP message:", JSON.parse(ev.data));

Programmatic Python Access

You can bypass the UI entirely by connecting directly to the WebSocket endpoint using Python.

import asyncio
import json
import base64
import uuid
from websockets import connect

async def main():
    # Build base64-encoded params matching the UI format

    params = {
        "type": "StdioServerParams",
        "command": "python",
        "args": ["-m", "my_tool_server"],
        "env": {}
    }
    server_params = base64.b64encode(json.dumps(params).encode()).decode()
    
    ws_url = f"ws://localhost:8081/mcp/ws/{uuid.uuid4()}?server_params={server_params}"
    
    async with connect(ws_url) as ws:
        # Wait for initialization confirmation

        init_msg = json.loads(await ws.recv())
        print("Init:", init_msg)
        
        # List available tools

        await ws.send(json.dumps({
            "type": "operation", 
            "operation": "list_tools"
        }))
        tools = json.loads(await ws.recv())
        print("Tools:", tools)
        
        # Call a specific tool

        await ws.send(json.dumps({
            "type": "operation",
            "operation": "call_tool",
            "tool_name": "my_tool",
            "arguments": {"text": "hello"}
        }))
        result = json.loads(await ws.recv())
        print("Result:", result)

asyncio.run(main())

Extending with Custom Operations

To add new operation types, extend the handle_operation method in autogenstudio/mcp/client.py.


# In autogenstudio/mcp/client.py

elif operation_type == "custom_foo":
    payload = operation.get("payload", {})
    result = await self.session.custom_foo(payload)
    await self.event_handler.on_operation_result(
        "custom_foo", 
        {"payload": payload, "result": result}
    )

The UI can then invoke this custom operation by sending:

{
  "type": "operation",
  "operation": "custom_foo",
  "payload": { "key": "value" }
}

Configuration and Server Types

The integration supports three transport mechanisms defined in autogen_ext/tools/mcp/_config.py:

  • StdioServerParams: For local subprocess-based tools requiring command and arguments
  • SseServerParams: For Server-Sent Events HTTP connections
  • StreamableHttpServerParams: For modern streamable HTTP-based MCP servers

Each model validates the specific parameters required for its transport type, ensuring type-safe configuration when building the WebSocket URL.

Summary

  • The MCP Workbench provides a WebSocket gateway that bridges AutoGen Studio's UI with external MCP-compatible tool servers.
  • Key architectural components include the FastAPI router (mcp.py), protocol bridge (wsbridge.py), and client wrapper (client.py).
  • The system supports stdio, SSE, and streamable HTTP transports through Pydantic configuration models.
  • Data flows through initialization, operation execution, and response handling with support for user elicitation and AI sampling.
  • Both frontend TypeScript and backend Python can interact with the Workbench using the same WebSocket protocol.

Frequently Asked Questions

What transport protocols does the MCP Workbench support?

The MCP Workbench supports three transport mechanisms: stdio for local subprocess communication, Server-Sent Events (SSE) for HTTP streaming, and streamable HTTP for modern MCP servers. These are configured via StdioServerParams, SseServerParams, and StreamableHttpServerParams models in autogen_ext/tools/mcp/_config.py.

How does the Workbench handle user input requests from tools?

When an MCP tool requires user input, the bridge stores an asyncio.Future in a pending_elicitations dictionary keyed by a UUID. The UI receives an elicitation_request message and responds with an elicitation_response containing the UUID and user data, which resolves the future and allows the MCP operation to continue execution.

Can I connect to MCP servers without using the AutoGen Studio UI?

Yes, you can connect programmatically by constructing the same WebSocket URL used by the frontend. Encode your server parameters as base64 JSON and connect to ws://<host>/mcp/ws/<session_id>?server_params=<params>. You can then send operation messages and receive responses using any WebSocket client library in Python, TypeScript, or other languages.

Where do I add support for a new MCP server type?

To add a new server type, define a Pydantic model in autogen_ext/tools/mcp/_config.py, import it at the top of autogenstudio/web/routes/mcp.py, and extend the create_mcp_session function with a new conditional branch to instantiate the appropriate MCP client. The bridge and callback layers remain unchanged because they operate on the generic MCP protocol.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →