12#include <nlohmann/json.hpp>
48bool SSETransport::parse_url() {
73void SSETransport::warn_if_cleartext()
const {
74 bool is_http = url_.substr(0, 5) ==
"http:";
75 bool is_localhost = host_.find(
"localhost") != std::string::npos ||
76 host_.find(
"127.0.0.1") != std::string::npos ||
77 host_.find(
"[::1]") != std::string::npos;
79 logger->warn(
"SSE connection to {} uses HTTP (cleartext). "
80 "Tool call data will be unencrypted.", host_);
96 logger->error(
"Invalid SSE URL: {}", url_);
102 client_ = std::make_unique<httplib::Client>(host_);
103 client_->set_read_timeout(std::chrono::seconds(0));
104 client_->set_connection_timeout(std::chrono::seconds(10));
107 sse_reader_thread_ = std::thread(
108 &SSETransport::sse_reader_loop,
this);
110 std::this_thread::sleep_for(std::chrono::milliseconds(500));
112 bool ok = connected_.load();
114 logger->error(
"SSE connection to {} failed", url_);
117 logger->info(
"SSE transport connected to {}", url_);
136 pending_cv_.notify_all();
138 if (sse_reader_thread_.joinable()) {
139 sse_reader_thread_.join();
143 logger->info(
"SSE transport closed for {}", url_);
154bool SSETransport::parse_request_id(
173bool SSETransport::post_request(
const std::string&
request_json) {
174 auto result = client_->Post(
177 if (!result || result->status != 200) {
178 logger->error(
"POST to {} failed: {}",
180 result ? std::to_string(result->status)
195std::string SSETransport::await_response(
198 std::unique_lock<std::mutex> lock(pending_mutex_);
199 auto deadline = std::chrono::steady_clock::now() +
203 pending_responses_.end()) {
204 if (pending_cv_.wait_until(lock, deadline) ==
205 std::cv_status::timeout) {
206 logger->warn(
"SSE response timeout for id={}",
240 logger->info(
"SSE request: {} bytes",
request_json.size());
242 bool ready = connected_ && !message_endpoint_.empty() &&
270void SSETransport::sse_reader_loop() {
272 const char* data,
size_t len) ->
bool {
281 process_sse_line(
line);
283 return running_.load();
286 auto res = client_->Get(
288 httplib::Headers{{
"Accept",
"text/event-stream"}},
293 logger->warn(
"SSE stream disconnected from {}", url_);
303void SSETransport::process_sse_line(
const std::string&
line) {
310 if (
clean.substr(0, 6) ==
"data: ") {
311 handle_sse_data(
clean.substr(6));
312 }
else if (
clean.substr(0, 7) ==
"event: ") {
313 current_event_type_ =
clean.substr(7);
323void SSETransport::handle_sse_data(
const std::string& data) {
324 if (current_event_type_ ==
"endpoint") {
325 handle_endpoint_event(data);
326 current_event_type_.clear();
329 current_event_type_.clear();
332 auto j = nlohmann::json::parse(data);
333 int id =
j.value(
"id", 0);
335 std::lock_guard<std::mutex> lock(pending_mutex_);
336 pending_responses_[id] = data;
337 pending_cv_.notify_all();
339 }
catch (
const nlohmann::json::exception&
e) {
340 logger->warn(
"Failed to parse SSE data: {}",
e.what());
350void SSETransport::handle_endpoint_event(
const std::string& data) {
356 if (data[0] ==
'/') {
357 message_endpoint_ = data;
358 }
else if (data.find(
"://") != std::string::npos) {
360 auto path_start = data.find(
'/', data.find(
"://") + 3);
361 message_endpoint_ = (
path_start != std::string::npos)
364 message_endpoint_ =
"/" + data;
368 logger->info(
"SSE message endpoint: {}", message_endpoint_);
SSETransport(std::string url, uint32_t default_timeout_ms=30000)
Construct with SSE endpoint URL.
void close() override
Stop SSE reader and close HTTP client.
bool is_connected() const override
Check if SSE stream is connected.
std::string send_request(const std::string &request_json, uint32_t timeout_ms=0) override
POST a JSON-RPC request and wait for matching response.
bool open() override
Connect to SSE endpoint and start reader thread.
~SSETransport() override
Destructor — ensures reader thread is stopped.
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Activate model on GPU (WARM → ACTIVE).
@ ok
Tool dispatched, returned non-empty content.
@ optional
optional: every escape asks the host's path approver
SSE transport for external MCP servers.