AI  

Implementing a Custom Transport Layer in MCP

Abstract

The Model Context Protocol (MCP) is designed to be transport-agnostic. While most implementations support stdio and SSE by default, real-world enterprise systems often require custom transports such as:

  • HTTP (JSON-RPC over POST)

  • WebSocket (bidirectional streaming)

  • Message queues (Kafka, RabbitMQ)

This article explains how MCP works internally and how to implement a custom transport layer (HTTP and WebSocket) in Python.

Understanding MCP Architecture

MCP separates two concerns:

LayerResponsibility
Protocol LayerJSON-RPC message format
Transport LayerHow messages are delivered

MCP defines:

  • Tool registration

  • JSON-RPC methods (tools/list, tools/call)

  • Structured request/response format

It does not mandate how messages are transported. That’s why you can plug in custom transports.

MCP Internal Flow

Client - Transport Layer - MCP Message Handler - Tool Execution - Transport Response

The key idea:

Your custom transport only needs to forward JSON messages to the MCP handler.

Implementing HTTP Transport (JSON-RPC over POST)

This is ideal when:

  • Integrating with API gateways

  • Deploying in microservices

  • Running behind load balancers

Install Dependencies

pip install fastapi uvicorn mcp

Example HTTP MCP Server

# http_mcp_server.py

from fastapi import FastAPI, Request
from mcp.server.fastmcp import FastMCP

app = FastAPI()
mcp = FastMCP("HTTP MCP Server")

# Define tool
@mcp.tool()
def leave_balance(employee_name: str) -> int:
    return {"Jayant": 15}.get(employee_name, 0)

@app.post("/mcp")
async def handle_mcp(request: Request):
    body = await request.json()
    # Pass raw JSON-RPC message to MCP
    response = await mcp.handle_message(body)
    return response

Run Server

uvicorn http_mcp_server:app --reload

Endpoint:

POST http://localhost:8000/mcp

Characteristics of HTTP Transport

  • Stateless

  • Simple to deploy

  • No streaming support (unless chunked)

  • Easy to secure via middleware

Implementing WebSocket Transport

WebSocket is ideal for:

  • Streaming LLM responses

  • Real-time agents

  • Long-lived sessions

WebSocket MCP Server

# websocket_mcp_server.py

from fastapi import FastAPI, WebSocket
from mcp.server.fastmcp import FastMCP
import json

app = FastAPI()
mcp = FastMCP("WebSocket MCP Server")

@mcp.tool()
def leave_balance(employee_name: str) -> int:
    return {"Jayant": 15}.get(employee_name, 0)

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        message = json.loads(data)
        response = await mcp.handle_message(message)
        await websocket.send_text(json.dumps(response))

Run Server

uvicorn websocket_mcp_server:app --reload

WebSocket endpoint:

ws://localhost:8000/ws

Supporting Streaming Responses

If your MCP server supports streaming tokens:

  • HTTP - requires chunked responses

  • WebSocket - naturally supports streaming

  • SSE - designed for streaming

Transport Comparison

TransportStreamingStatefulBest Use
stdioYesLocal onlyDev testing
SSEYesSemiBrowser streaming
HTTP POSTNoStatelessAPIs
WebSocketYesYesAgents, real-time

Conclusion

MCP is protocol-focused, not transport-bound.

To implement a custom transport:

  1. Accept JSON-RPC message

  2. Forward to handle_message()

  3. Return response

  4. Keep transport layer separate

By implementing HTTP or WebSocket adapters, you gain:

  • Flexibility

  • Scalability

  • Enterprise readiness

  • Real-time capability

Custom transport is not about changing MCP — it’s about extending infrastructure around it.