Header: include/mcp/Transport.h
Transports provide JSON-RPC message delivery and connection lifecycle. Concrete implementations:
- In-memory: include/mcp/InMemoryTransport.hpp
- Stdio: include/mcp/StdioTransport.hpp
- Shared memory: include/mcp/SharedMemoryTransport.hpp
- HTTP/HTTPS client: include/mcp/HTTPTransport.hpp
- HTTP/HTTPS server acceptor: include/mcp/HTTPServer.hpp
- std::future Start()
- Starts the transport I/O loop.
- std::future Close()
- Closes the transport and releases resources.
- bool IsConnected() const
- Returns true if the transport is connected.
- std::string GetSessionId() const
- Returns a session identifier for diagnostics.
- std::future<std::unique_ptr> SendRequest(std::unique_ptr request)
- Sends a JSON-RPC request and resolves to the response.
- std::future SendNotification(std::unique_ptr notification)
- Sends a JSON-RPC notification (no response expected).
- using NotificationHandler = std::function<void(std::unique_ptr)>;
- void SetNotificationHandler(NotificationHandler handler)
- Receives incoming notifications.
- using RequestHandler = std::function<std::unique_ptr(const JSONRPCRequest&)>;
- void SetRequestHandler(RequestHandler handler)
- Server-side hook to handle incoming requests; return value is sent back to the peer.
- using ErrorHandler = std::function<void(const std::string& error)>;
- void SetErrorHandler(ErrorHandler handler)
- Receives transport-level errors.
- std::unique_ptr CreateTransport(const std::string& config)
- Creates a transport instance (e.g., stdio config). See concrete factory headers for parameters.
Header: include/mcp/StdioTransport.hpp
The stdio transport can be created via StdioTransportFactory::CreateTransport(config) where config is a key=value list separated by ; or whitespace. Recognized keys:
timeout_ms— request timeout for in-flight JSON-RPC requests.0disables request timeouts.idle_read_timeout_ms— closes the transport if no bytes are received for this duration while connected.write_timeout_ms— maximum duration for non-blocking writes to progress; exceeded time closes the transport.write_queue_max_bytes— upper bound on buffered outgoing frames; overflow emits error and closes the transport.
Example:
#include "mcp/StdioTransport.hpp"
mcp::StdioTransportFactory f;
auto transport = f.CreateTransport("timeout_ms=30000; idle_read_timeout_ms=200; write_timeout_ms=2000; write_queue_max_bytes=1048576");Programmatic setters are also available on StdioTransport:
SetRequestTimeoutMs(uint64_t ms)SetIdleReadTimeoutMs(uint64_t ms)SetWriteTimeoutMs(uint64_t ms)SetWriteQueueMaxBytes(std::size_t bytes)
Header: include/mcp/InMemoryTransport.hpp
The in-memory transport is primarily for tests and in-process demos. It supports creating paired endpoints or a single endpoint via the factory.
Examples:
#include "mcp/InMemoryTransport.hpp"
// Paired endpoints (test/demo)
auto pair = mcp::InMemoryTransport::CreatePair();
auto client = std::move(pair.first);
auto server = std::move(pair.second);
(void)client->Start().get();
(void)server->Start().get();
// Factory-created single endpoint
mcp::InMemoryTransportFactory f;
auto t = f.CreateTransport(""); // no config requiredHeader: include/mcp/SharedMemoryTransport.hpp
The shared-memory transport enables cross-process JSON-RPC using Boost.Interprocess queues. Two queues are created per channel: <channel>_c2s and <channel>_s2c.
Config formats:
shm://<channelName>?create=true&maxSize=<bytes>&maxCount=<n>(creator/server side)<channelName>(peer/client side;create=falseby default)
Examples:
#include "mcp/SharedMemoryTransport.hpp"
// Server side (creator)
mcp::SharedMemoryTransportFactory f;
auto serverT = f.CreateTransport("shm://mcp-shm?create=true&maxSize=65536&maxCount=64");
// Client side
auto clientT = f.CreateTransport("mcp-shm");Header: include/mcp/HTTPTransport.hpp
Factory config is a ;-separated key=value list. Recognized keys: scheme, host, port, rpcPath, notifyPath, serverName, caFile, caPath, connectTimeoutMs, readTimeoutMs.
Example:
#include "mcp/HTTPTransport.hpp"
mcp::HTTPTransportFactory f;
auto t = f.CreateTransport("scheme=http; host=127.0.0.1; port=9443; rpcPath=/mcp/rpc; notifyPath=/mcp/notify; serverName=127.0.0.1");Optional authentication for the HTTP client transport is supported.
See also: Authentication for flows, demos, and server-side Bearer enforcement.
- Config keys (semicolon‑delimited
key=value):auth—none(default) |bearer|oauth2bearerTokenortoken— static bearer token whenauth=beareroauthUrloroauthTokenUrl— OAuth 2.1 token endpoint URL (e.g.,https://auth.example.com/oauth2/token)clientId— OAuth 2.1 client id (client‑credentials grant)clientSecret— OAuth 2.1 client secret (client‑credentials grant)scope— optional space‑delimited scopestokenRefreshSkewSecondsortokenSkew— pre‑expiry refresh skew (seconds, default 60)
Examples:
// Static Bearer token
auto t1 = f.CreateTransport(
"scheme=https; host=api.example.com; port=443; rpcPath=/mcp/rpc; notifyPath=/mcp/notify;"
" auth=bearer; bearerToken=XYZ"
);
// OAuth 2.1 client‑credentials (token cached and refreshed proactively)
auto t2 = f.CreateTransport(
"scheme=https; host=api.example.com; port=443; rpcPath=/mcp/rpc; notifyPath=/mcp/notify;"
" auth=oauth2; oauthUrl=https://auth.example.com/oauth2/token; clientId=myid; clientSecret=mysecret; scope=a b c; tokenSkew=60"
);Notes:
- HTTPS uses TLS 1.3 and hostname verification (SNI) by default, sharing the same trust configuration as normal requests.
- Secrets are not logged. Prefer environment variables or secure config handling in your process to populate
clientSecret. - The token endpoint response must include
access_tokenand may includeexpires_in(seconds). When absent, a default 1‑hour lifetime is assumed.
You can also inject an auth provider programmatically using IAuth.
#include "mcp/HTTPTransport.hpp"
#include "mcp/auth/BearerAuth.hpp"
mcp::HTTPTransportFactory f;
auto t = f.CreateTransport(
"scheme=http; host=127.0.0.1; port=8080; rpcPath=/rpc; notifyPath=/notify; auth=none"
);
auto* http = dynamic_cast<mcp::HTTPTransport*>(t.get());
if (http) {
mcp::auth::BearerAuth bearer("XYZ");
http->SetAuth(bearer); // non-owning reference injection
}Shared ownership is supported when you want the transport to manage lifetime:
auto auth = std::make_shared<mcp::auth::BearerAuth>("XYZ");
http->SetAuth(auth); // shared ownership injectionWhen SetAuth(...) is used, it takes precedence over any auth= factory configuration.
The HTTP client surfaces transport‑level diagnostics via HTTPTransport::SetErrorHandler(). When an error handler is registered, the transport emits concise stage markers to help troubleshoot request lifecycles and shutdown behavior.
Examples of messages you may see when debug is enabled:
HTTP DEBUG: resolved <host>:<port> path=<path>HTTP DEBUG: http connected/https connectedHTTP DEBUG: http wrote request/https wrote requestHTTP DEBUG: http read response bytes=<N>(orhttps ...)HTTPTransport: coPostJson done; body.size=<N>; key=<id>HTTPTransport: parsed response id=<id|null>HTTPTransport: deliver lookup key=<id> hit|miss; pending=<count>HTTPTransport: set_value start/HTTPTransport: set_value doneHTTP Close: begin / ioc.stop() called / joining ioThread / ioThread joined / failing pending size=<count>
Quick usage with shorter timeouts for fast failure in tests:
#include "mcp/HTTPTransport.hpp"
mcp::HTTPTransportFactory f;
auto t = f.CreateTransport(
"scheme=http; host=127.0.0.1; port=8080; rpcPath=/rpc; notifyPath=/notify; "
"connectTimeoutMs=500; readTimeoutMs=1500; auth=bearer; bearerToken=XYZ"
);
t->SetErrorHandler([](const std::string& msg){
std::cerr << "[HTTPTransport] " << msg << std::endl;
});
(void)t->Start().get();
// ... SendRequest / SendNotification ...
(void)t->Close().get();Notes:
- Debug messages are emitted only when an error handler is set. They are intended for diagnostics and may evolve; do not assert on their exact strings in production.
connectTimeoutMscontrols the connect phase deadline;readTimeoutMsbounds the write+read phase per request.
Header: include/mcp/Transport.h (ITransportAcceptor) and include/mcp/HTTPServer.hpp
Use an acceptor to run a server that receives JSON-RPC over HTTP/HTTPS. The acceptor invokes server-provided handlers directly.
Header: include/mcp/HTTPServer.hpp
- Config is a URL-like string with optional query parameters:
http://<host>[:port]https://<host>[:port]?cert=<path>&key=<path>- Default port: 9443
Example server wiring using Server::HandleJSONRPC(...) bridge:
#include "mcp/HTTPServer.hpp"
#include "mcp/Server.h"
mcp::ServerFactory sf;
auto server = sf.CreateServer({"Demo","1.0.0"});
mcp::HTTPServerFactory hf;
auto acceptor = hf.CreateTransportAcceptor("http://127.0.0.1:9443");
acceptor->SetRequestHandler([&server](const mcp::JSONRPCRequest& req){
return server->HandleJSONRPC(req);
});
acceptor->SetNotificationHandler([](std::unique_ptr<mcp::JSONRPCNotification> note){ /* optional */ });
acceptor->SetErrorHandler([](const std::string& err){ /* log */ });
acceptor->Start().get();MCP_STDIOTRANSPORT_TIMEOUT_MS— default request timeout in milliseconds. See usage in src/mcp/StdioTransport.cpp.MCP_STDIO_CONFIG— demo/server env to pass the samekey=valuelist as the factory config. See examples in BUILD+TEST.MD.
The demo client and server now support selecting transports via command-line arguments.
- Server: examples/mcp_server/main.cpp
- Client: examples/mcp_client/main.cpp
Usage examples:
# stdio (default)
./mcp_server --transport=stdio --stdiocfg="timeout_ms=30000"
./mcp_client --transport=stdio --stdiocfg="timeout_ms=30000"
# shared memory (creator/server uses create=true)
./mcp_server --transport=shm --channel=mcp-shm
./mcp_client --transport=shm --channel=mcp-shm
# http (single request per connection; demo acceptor)
./mcp_server --transport=http --listen=http://127.0.0.1:9443
./mcp_client --transport=http --url=http://127.0.0.1:9443WSL on Windows (PowerShell):
# Run the same commands via WSL (assumes binaries are in the current working directory inside WSL)
wsl -d Ubuntu -- bash -lc "./mcp_server --transport=stdio --stdiocfg=\"timeout_ms=30000\""
wsl -d Ubuntu -- bash -lc "./mcp_client --transport=stdio --stdiocfg=\"timeout_ms=30000\""
wsl -d Ubuntu -- bash -lc "./mcp_server --transport=shm --channel=mcp-shm"
wsl -d Ubuntu -- bash -lc "./mcp_client --transport=shm --channel=mcp-shm"
wsl -d Ubuntu -- bash -lc "./mcp_server --transport=http --listen=http://127.0.0.1:9443"
wsl -d Ubuntu -- bash -lc "./mcp_client --transport=http --url=http://127.0.0.1:9443"The stdio transport includes defensive behaviors to ensure robustness. Implementation lives in src/mcp/StdioTransport.cpp:
- Idle read timeout triggers close when no bytes arrive for
idle_read_timeout_ms. - Write queue overflow (exceeding
write_queue_max_bytes) emits a transport error and transitions to disconnected. - Write timeout (no forward progress for
write_timeout_ms) emits a transport error and closes. - Malformed frames (e.g., excessive
Content-Length, header/body mismatch) are rejected and logged; the transport terminates.
Negative-path scripts for these behaviors are exercised by CTest targets defined in tests/CMakeLists.txt via scripts/test_stdio_hardening.sh.
- Linux: uses
epollwith a wake eventfd to interrupt waits; non-blocking I/O for stdin/stdout. - macOS/*BSD: uses
pollplus a self-pipe for wakeups; non-blocking I/O for stdin/stdout. - Windows: uses
WaitForMultipleObjectson a manual-reset event to wake the reader;PeekNamedPipefor readiness; overlapped semantics are avoided to keep the implementation minimal.