LspCpp supports several ways to move JSON-RPC messages between client and server.
The standard LSP transport: the editor launches your server as a subprocess and communicates over stdin/stdout. stderr is free for logging.
lsp::LanguageSession server;
server.startStdio(); // equivalent to start(make_stdin_stream(), make_stdout_stream())Or with explicit streams:
server.start(lsp::make_stdin_stream(), lsp::make_stdout_stream());Messages use the LSP content-length header framing defined by the protocol. LspCpp handles framing, parsing, and serialization automatically.
For unit tests, use feedable input and capturing output streams. See tests/test_helpers.h for FeedableIStream and StringOStream:
auto input = std::make_shared<test::FeedableIStream>();
auto output = std::make_shared<test::StringOStream>();
server.start(input, output);
input->append(test::MakeLspFrame(R"({"jsonrpc":"2.0","id":1,"method":"initialize","params":{}})"));
// inspect output->snapshot()For remote or multi-client scenarios, use TcpServer from LibLsp/JsonRpc/TcpServer.h. See examples/TcpServerExample.cpp:
#include "LibLsp/JsonRpc/TcpServer.h"
lsp::TcpServer server("127.0.0.1", "9333", protocol_json_handler, endpoint, log);
server.point.registerHandler(/* ... */);
server.run(); // blocks until stop() is calledThe example listens on 127.0.0.1:9333 by default and runs server.run() on a background thread so the test client can connect in the same process.
Build with the default configuration; TCP support is always included.
WebSocket transport is built when LSPCPP_BUILD_WEBSOCKETS=ON (the default). See examples/WebsocketExample.cpp.
WebSocket support depends on the bundled IXWebSocket library (or a system/vcpkg copy when USE_EXTERNAL_IXWEBSOCKET=ON).
Disable WebSocket support to reduce dependencies:
cmake -DLSPCPP_BUILD_WEBSOCKETS=OFF ..LanguageSession and RemoteEndPoint accept a JSONStreamStyle parameter:
lsp::LanguageSession server(lsp::JSONStreamStyle::Standard);Use Standard for normal LSP content-length framing. Other styles exist for legacy or test scenarios; most users should keep the default.
Both LanguageSession and RemoteEndPoint accept a max_workers parameter (default 2) controlling how many threads process incoming messages concurrently. Increase this if handlers are CPU-bound and independent; keep it low if handlers share mutable state without synchronization.
| Transport | Best for |
|---|---|
| stdio | Editor integration (VS Code, Neovim, etc.) |
| TCP | Custom clients, debugging with netcat, remote development |
| WebSocket | Browser-based editors, web IDEs |
| Custom streams | Automated tests, embedding in another process |
For production editor plugins, stdio is almost always the right choice.