How ChatMCP Integrates with MCP Servers: Transport Protocols Explained

ChatMCP integrates with MCP servers through a flexible plugin architecture that supports four distinct transport protocols—StdIO, SSE, Streamable HTTP, and InMemory—enabling both local process communication and remote HTTP-based interactions via the Model Context Protocol (MCP).

ChatMCP, developed by daodao97/chatmcp, treats MCP servers as dynamic plugins that extend functionality through the Model Context Protocol (MCP) JSON-RPC API. The application manages server configurations via mcp_server.json and automatically selects appropriate transport protocols based on server type definitions, implementing a modular communication layer defined in lib/mcp/mcp.dart.

Understanding ChatMCP MCP Server Architecture

Server Configuration and Provider Model

Each MCP server is defined in assets/mcp_server.json (managed by McpServerProvider in lib/provider/mcp_server_provider.dart). The JSON structure specifies:

{
  "name": "My Server",
  "type": "sse",
  "command": "https://example.com/mcp",
  "args": [],
  "env": {},
  "tools": []
}

The type field determines which transport protocol ChatMCP uses to establish communication. Valid values are stdio, sse, streamable, and inmemory.

The Initialization Entry Point

When users select a server, ChatMCP invokes initializeMcpServer from lib/mcp/mcp.dart (lines 11-75). This function:

  1. Parses the configuration into a ServerConfig model (lib/mcp/models/server.dart)
  2. Instantiates the appropriate McpClient implementation based on the type value
  3. Establishes the transport connection and performs the MCP handshake

Supported Transport Protocols in ChatMCP

ChatMCP implements four transport protocols through dedicated client classes, each optimized for specific deployment scenarios.

StdIO Transport for Local Processes

The StdIO protocol (type: "stdio") uses the StdioClient class (lib/mcp/stdio/stdio_client.dart). This transport spawns a local process using the command field as an executable path and communicates via standard input/output streams. This approach is ideal for local Python or Node.js MCP servers launched via package managers like uvx or npx.

SSE Transport for HTTP Streaming

The SSE protocol (type: "sse") utilizes SSEClient (lib/mcp/sse/sse_client.dart). This implementation:

  • Opens an HTTP Server-Sent Events connection to the URL specified in command
  • Listens for an endpoint event from the server to determine the POST target
  • Transmits JSON-RPC requests to the revealed endpoint
  • Implements exponential back-off reconnection (max 5 attempts) via SSEClient._connect (lines 85-118) and SSEClient._scheduleReconnection

Streamable HTTP with Asynchronous Support

The Streamable protocol (type: "streamable") employs StreamableClient (lib/mcp/streamable/streamable_client.dart). This hybrid approach:

  • Sends JSON-RPC requests via standard HTTP POST
  • Handles 202 Accepted responses by initiating SSE streams for asynchronous server messages
  • Supports token-based resumption through StreamableClient._scheduleReconnection

InMemory Transport for Embedded Tools

The InMemory protocol (type: "inmemory") uses InMemoryClient (lib/mcp/inmemory/client.dart). This transport loads embedded mock servers entirely within the Dart application, requiring no network connectivity or external processes. This mode powers built-in tools like mathematical functions or testing scenarios.

The McpClient Interface and Communication Flow

All transport implementations share a common contract defined in lib/mcp/client/mcp_client_interface.dart:

Future<void> initialize();
Future<JSONRPCMessage> sendInitialize();
Future<JSONRPCMessage> sendToolList();
Future<JSONRPCMessage> sendToolCall(...);
Future<JSONRPCMessage> sendMessage(...);
Future<void> dispose();

Standard MCP Workflow

Every connection follows this sequence:

  1. Transport Initializationinitialize() establishes the physical connection (process spawn, HTTP connection, or memory instantiation)
  2. MCP HandshakesendInitialize() performs protocol negotiation
  3. Tool DiscoverysendToolList() retrieves available callable tools from the server
  4. ExecutionsendToolCall() dispatches JSON-RPC requests; responses return through the same client instance
  5. Cleanupdispose() releases resources and terminates connections

Configuring and Connecting to MCP Servers

JSON Configuration Example

Define your MCP server in assets/mcp_server.json:

{
  "my-sse-server": {
    "name": "My SSE Server",
    "type": "sse",
    "command": "https://api.example.com/mcp",
    "args": [],
    "env": {},
    "tools": []
  }
}

Programmatic Server Initialization

Connect to a configured server in Dart:

final config = await provider.getServerConfig('my-sse-server');
final client = await initializeMcpServer(config);
await client.initialize();
await client.sendInitialize();
final toolList = await client.sendToolList();

Executing Tools

Call remote tools using the unified interface:

final response = await client.sendToolCall(
  name: 'search',
  arguments: {'query': 'flutter documentation'},
);
print(response.result);

This method works identically across StdIO, SSE, and Streamable transports.

Handling Asynchronous Messages

Monitor connection state for SSE and Streamable protocols:

client.processStateStream.listen((state) {
  if (state.type == ProcessStateType.running) {
    // Connection ready for message exchange
  }
});

Summary

  • ChatMCP treats MCP servers as plugins configured via mcp_server.json and managed by McpServerProvider.
  • Four transport protocols are supported: StdIO (local processes), SSE (HTTP streaming), Streamable (HTTP POST with optional SSE), and InMemory (embedded).
  • The initializeMcpServer function in lib/mcp/mcp.dart routes to appropriate McpClient implementations based on the type field.
  • All transports share the McpClient interface, providing consistent methods for initialization, tool discovery, and execution.
  • SSE and Streamable clients implement automatic reconnection with exponential back-off for resilient remote connections.

Frequently Asked Questions

What transport protocol should I use for a local Python MCP server?

Use StdIO transport. Set "type": "stdio" in your configuration and specify the Python executable or uvx command in the command field. The StdioClient spawns the process and communicates directly through stdin/stdout streams, making it ideal for local development environments.

How does ChatMCP handle reconnection when an SSE server drops?

The SSEClient implementation in lib/mcp/sse/sse_client.dart includes SSEClient._scheduleReconnection, which implements exponential back-off retry logic with a maximum of 5 attempts. The client automatically re-establishes the HTTP connection and resynchronizes the JSON-RPC endpoint when network connectivity returns.

Can ChatMCP connect to MCP servers without network access?

Yes. Use InMemory transport by setting "type": "inmemory". The InMemoryClient loads the server logic directly within the Dart application, requiring no external network connection or separate process. This is ideal for built-in utilities like mathematical calculation tools or offline testing scenarios.

What authentication mechanisms does ChatMCP support for MCP connections?

ChatMCP automatically adds OAuth Bearer tokens and custom headers when OAuth configuration is present in the server definition. The ServerConfig model includes an oauth field that, when populated, injects authentication headers into all HTTP-based transports (SSE and Streamable) before establishing connections.

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 →