TL;DR
MCP’s custom transport story is a mess: inconsistent documentation, one trivial example and one monster example, and an RFC-ish spec that skips the parts you actually need. While the protocol is growing in adoption every day, building custom transports is painful. Here’s what’s broken and what could be better.
Note: All thoughts are based on my minimal experience of trying to implement async custom transports for MCP at https://github.com/bh-rat/asyncmcp. And some reading.
Model Context Protocol or MCP gets a lot of criticism for security issues but there’s another related pain point that deserves attention : Custom Transports.
What are custom transports?
MCP’s official SDK ships with 2 built-in transports — stdio and streamable HTTP. They work fine when you’re working on chatbots and synchronous like communication but what if you need something different?
What if you want to run MCP over message queues, implement webhook-based communication, or integrate with existing infrastructure that doesn’t fit these patterns? That’s where custom transports come in — and where the pain begins.
Decrypting the documentation
MCP does have a guideline for building custom transports in their documentation.
RFC Specification: Inconsistent Complexity
The transport specification uses formal RFC-style language with MUST/SHOULD/MAY requirements:
- Stdio transport: 7 clear, implementable requirements
- Streamable HTTP transport: ~50 detailed requirements covering everything from connection lifecycle to DNS rebinding protection
- Custom transports: Mentions exactly 2 requirements —
- MUST preserve JSON-RPC message format
- SHOULD document your connection establishment and message exchange pattern
Implementation Guide: way too many overwhelming pointers!
Then there’s the concepts documentation, which takes a completely different approach. It cheerfully states “it should be easy to implement new custom transports” and then dumps 41 separate concerns on you:
- 5 error handling scenarios to implement
- 10 best practices to follow
- 16 security considerations to address
- 10 debugging tips to remember
The formal spec gives you almost nothing to work with, while the implementation guide overwhelms you with every possible edge case. Neither doc helps you actually build a custom transport.
Implementations : Simple vs Overwhelming
Looking at the actual SDK implementations makes the problem even clearer:
- stdio: lightweight wrapper that converts stdio/stdout text streams to SessionMessage object streams. Clean, minimal, does one thing well.
- streamable_http: A large implementation split across multiple files and classes that’s nearly impossible to use as a learning reference. It handles complete lifecycle management, error handling, json and event-stream response types, stateful and stateless sessions, concurrent client sessions, concurrent sessions from same client, resumability via event store, security settings — DNS rebinding guards, header validation, validation, and a lot more. It’s hard to map those 50 specification requirements to the actual code.
- sse , websocket: as per documentation, these are deprecated.
In addition :
- The python-sdk provides no protocol or abstract class for what a good MCP transport lifecycle/structure looks like.
- No custom examples. The 2 implementations, as discussed above, differ by great lengths.
- Similar problems exist for the client side implementations as well.
Missing structure and guidance
The fundamental issue isn’t just documentation -it’s the lack of architectural guidance — especially when MCP and the transport layer are doing SO many things. Other frameworks have solved this better. (For what it’s worth, relevant specific examples here were also pretty hard to find.)
NestJS Transporters provide a clean, consistent interface:
// Clear interface for any transport
export interface CustomTransporter {
listen(callback: () => void): void;
close(): void;
send(pattern: any, data: any): Observable<any>;
}Apache Camel Components offer another excellent model. Their component development guide provides:
- Clear base classes to extend (
DefaultComponent,DefaultEndpoint) - Step-by-step component creation process
- Comprehensive examples from simple to complex
- Consistent patterns across all transport types
Final Thoughts
MCP’s core remains solid. Most people are building on top of the current available transports. And for them, the protocol works. Custom transports are possible as well — they’re just painful to understand and implement right now. I hope the ecosystem around custom transports also gets some focus and matures sooner.
I personally have been working on asyncmcp, an experiment to write async transport layers for MCP.
If you’re also building MCP custom transports, I’d love to connect. Reach me at https://www.linkedin.com/in/bharat-geleda/