diff --git a/MCP/cpp-sdk/.gitignore b/MCP/cpp-sdk/.gitignore index e53f6d7..e4adb2a 100644 --- a/MCP/cpp-sdk/.gitignore +++ b/MCP/cpp-sdk/.gitignore @@ -33,6 +33,7 @@ # Build directories build/ +build-*/ output/ # Test directories diff --git a/MCP/cpp-sdk/CMakeLists.txt b/MCP/cpp-sdk/CMakeLists.txt index 854c478..753e0f3 100644 --- a/MCP/cpp-sdk/CMakeLists.txt +++ b/MCP/cpp-sdk/CMakeLists.txt @@ -35,13 +35,21 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -D_FORTIFY_SOURCE=2") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") # Common compiler flags -add_compile_options(-Wall -Werror -Wno-error=unused-variable -Wfloat-equal -Wtrampolines -fno-strict-aliasing -fstack-protector-strong) +if(APPLE) + add_compile_options(-Wall -Werror -Wno-error=unused-variable -Wfloat-equal -fno-strict-aliasing -fstack-protector-strong) +else() + add_compile_options(-Wall -Werror -Wno-error=unused-variable -Wfloat-equal -Wtrampolines -fno-strict-aliasing -fstack-protector-strong) +endif() # Linker flags for security hardening -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now -rdynamic -pie") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now -rdynamic") +if(APPLE) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-pie") +else() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now -rdynamic -pie") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now -rdynamic") +endif() -if( CMAKE_BUILD_TYPE STREQUAL "Release") +if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s") endif() diff --git a/MCP/cpp-sdk/example/client_example/prompt_example/CMakeLists.txt b/MCP/cpp-sdk/example/client_example/prompt_example/CMakeLists.txt index 19a556d..ae1785f 100644 --- a/MCP/cpp-sdk/example/client_example/prompt_example/CMakeLists.txt +++ b/MCP/cpp-sdk/example/client_example/prompt_example/CMakeLists.txt @@ -6,10 +6,17 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(PromptExample prompt_example.cpp) +set(MCP_OUTPUT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../output") +set(MCP_LIBRARY "${MCP_OUTPUT_DIR}/libmcp${CMAKE_SHARED_LIBRARY_SUFFIX}") + target_include_directories(PromptExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../../output/mcp + ${MCP_OUTPUT_DIR}/mcp ) target_link_libraries(PromptExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../../output/libmcp.so + ${MCP_LIBRARY} +) + +set_target_properties(PromptExample PROPERTIES + BUILD_RPATH "${MCP_OUTPUT_DIR}" ) diff --git a/MCP/cpp-sdk/example/client_example/resource_example/CMakeLists.txt b/MCP/cpp-sdk/example/client_example/resource_example/CMakeLists.txt index b7a4051..a740878 100644 --- a/MCP/cpp-sdk/example/client_example/resource_example/CMakeLists.txt +++ b/MCP/cpp-sdk/example/client_example/resource_example/CMakeLists.txt @@ -6,10 +6,17 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(ResourceExample resource_example.cpp) +set(MCP_OUTPUT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../output") +set(MCP_LIBRARY "${MCP_OUTPUT_DIR}/libmcp${CMAKE_SHARED_LIBRARY_SUFFIX}") + target_include_directories(ResourceExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../../output/mcp + ${MCP_OUTPUT_DIR}/mcp ) target_link_libraries(ResourceExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../../output/libmcp.so + ${MCP_LIBRARY} +) + +set_target_properties(ResourceExample PROPERTIES + BUILD_RPATH "${MCP_OUTPUT_DIR}" ) diff --git a/MCP/cpp-sdk/example/client_example/tool_example/CMakeLists.txt b/MCP/cpp-sdk/example/client_example/tool_example/CMakeLists.txt index 2fb79d2..15b0873 100644 --- a/MCP/cpp-sdk/example/client_example/tool_example/CMakeLists.txt +++ b/MCP/cpp-sdk/example/client_example/tool_example/CMakeLists.txt @@ -6,10 +6,17 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(ToolExample tool_example.cpp) +set(MCP_OUTPUT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../output") +set(MCP_LIBRARY "${MCP_OUTPUT_DIR}/libmcp${CMAKE_SHARED_LIBRARY_SUFFIX}") + target_include_directories(ToolExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../../output/mcp + ${MCP_OUTPUT_DIR}/mcp ) target_link_libraries(ToolExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../../output/libmcp.so + ${MCP_LIBRARY} +) + +set_target_properties(ToolExample PROPERTIES + BUILD_RPATH "${MCP_OUTPUT_DIR}" ) diff --git a/MCP/cpp-sdk/example/server_example/CMakeLists.txt b/MCP/cpp-sdk/example/server_example/CMakeLists.txt index 7c30ac0..e673480 100644 --- a/MCP/cpp-sdk/example/server_example/CMakeLists.txt +++ b/MCP/cpp-sdk/example/server_example/CMakeLists.txt @@ -6,10 +6,17 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(ServerExample server_example.cpp) +set(MCP_OUTPUT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../output") +set(MCP_LIBRARY "${MCP_OUTPUT_DIR}/libmcp${CMAKE_SHARED_LIBRARY_SUFFIX}") + target_include_directories(ServerExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../output/mcp + ${MCP_OUTPUT_DIR}/mcp ) target_link_libraries(ServerExample PRIVATE - ${CMAKE_CURRENT_LIST_DIR}/../../output/libmcp.so -) \ No newline at end of file + ${MCP_LIBRARY} +) + +set_target_properties(ServerExample PROPERTIES + BUILD_RPATH "${MCP_OUTPUT_DIR}" +) diff --git a/MCP/cpp-sdk/example/server_example/run_example.sh b/MCP/cpp-sdk/example/server_example/run_example.sh old mode 100644 new mode 100755 diff --git a/MCP/cpp-sdk/include/mcp/mcp_log.h b/MCP/cpp-sdk/include/mcp/mcp_log.h index 6d27829..1871851 100644 --- a/MCP/cpp-sdk/include/mcp/mcp_log.h +++ b/MCP/cpp-sdk/include/mcp/mcp_log.h @@ -9,11 +9,16 @@ #include #include #include -#include #include #include #include +#if defined(__APPLE__) +#include +#elif defined(__linux__) +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -51,6 +56,19 @@ static inline void GetCurrentTimeStamp(char* buffer, size_t bufferSize) snprintf(buffer + len, bufferSize - len, ".%03ld", ts.tv_nsec / 1000000); } +static inline long long McpGetCurrentThreadId(void) +{ +#if defined(__APPLE__) + uint64_t tid = 0; + pthread_threadid_np(NULL, &tid); + return (long long)tid; +#elif defined(__linux__) + return (long long)syscall(SYS_gettid); +#else + return (long long)getpid(); +#endif +} + #define MCP_LOG_COMMON(logLevel, format, ...) \ do { \ if (g_logCallback != NULL) { \ @@ -58,7 +76,7 @@ static inline void GetCurrentTimeStamp(char* buffer, size_t bufferSize) GetCurrentTimeStamp(timestamp, sizeof(timestamp)); \ const char* filename = strrchr(__FILE__, '/'); \ filename = filename ? filename + 1 : __FILE__; \ - g_logCallback(logLevel, "[%s] [%ld] %s::%s:[%d] " format "\n", timestamp, syscall(SYS_gettid), filename, \ + g_logCallback(logLevel, "[%s] [%lld] %s::%s:[%d] " format "\n", timestamp, McpGetCurrentThreadId(), filename, \ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ } \ } while (0) diff --git a/MCP/cpp-sdk/scripts/build.sh b/MCP/cpp-sdk/scripts/build.sh index 7404b9f..81f4465 100755 --- a/MCP/cpp-sdk/scripts/build.sh +++ b/MCP/cpp-sdk/scripts/build.sh @@ -96,8 +96,15 @@ if [[ ${WITH_COVERAGE} -eq 1 && "${BUILD_TYPE}" != "Debug" ]]; then echo "[INFO] Continuing with ${BUILD_TYPE}..." fi -# Determine optimal job count for Linux (CPU cores + 1, but max 8 to avoid memory issues) -CPU_CORES=$(nproc) +# Determine optimal job count (CPU cores + 1, but max 8 to avoid memory issues) +if command -v nproc >/dev/null 2>&1; then + CPU_CORES=$(nproc) +elif command -v sysctl >/dev/null 2>&1; then + CPU_CORES=$(sysctl -n hw.ncpu) +else + CPU_CORES=4 +fi + OPTIMAL_JOBS=$((CPU_CORES + 1)) if [[ ${OPTIMAL_JOBS} -gt 8 ]]; then OPTIMAL_JOBS=8 @@ -112,6 +119,17 @@ cd "${BUILD_DIR_ABS}" CMAKE_ARGS=("${SOURCE_DIR}" "-DCMAKE_BUILD_TYPE=${BUILD_TYPE}") +if [[ "$(uname -s)" == "Darwin" ]] && command -v brew >/dev/null 2>&1; then + HOMEBREW_PREFIXES=( + "$(brew --prefix openssl@3)" + "$(brew --prefix curl)" + "$(brew --prefix libevent)" + "$(brew --prefix nlohmann-json)" + ) + CMAKE_PREFIX_PATH=$(IFS=';'; echo "${HOMEBREW_PREFIXES[*]}") + CMAKE_ARGS+=("-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}") +fi + if [[ ${WITH_TESTS} -eq 1 ]]; then CMAKE_ARGS+=("-DMCP_ENABLE_TESTS=ON") else diff --git a/MCP/cpp-sdk/scripts/install_deps.sh b/MCP/cpp-sdk/scripts/install_deps.sh index 1f03bdb..1c9e865 100755 --- a/MCP/cpp-sdk/scripts/install_deps.sh +++ b/MCP/cpp-sdk/scripts/install_deps.sh @@ -12,7 +12,10 @@ set -euo pipefail echo "==> Checking system dependencies for mcp_cpp..." # Detect OS type -if [ -f /etc/os-release ]; then +if [ "$(uname -s)" = "Darwin" ]; then + OS="macos" + OS_VERSION="$(sw_vers -productVersion)" +elif [ -f /etc/os-release ]; then . /etc/os-release OS=$ID OS_VERSION=$VERSION_ID @@ -136,6 +139,40 @@ install_dependencies() { fi ;; + macos) + if ! command_exists brew; then + echo " - Homebrew: NOT FOUND" + echo "Please install Homebrew first: https://brew.sh/" + exit 1 + fi + + if brew list --versions curl >/dev/null 2>&1; then + echo " - curl (Homebrew): OK" + else + echo " - curl (Homebrew): NOT FOUND" + need_curl=1 + fi + + if brew list --versions openssl@3 >/dev/null 2>&1; then + echo " - openssl@3: OK" + else + echo " - openssl@3: NOT FOUND" + need_openssl=1 + fi + + if brew list --versions cmake >/dev/null 2>&1; then + echo " - cmake: OK" + else + echo " - cmake: NOT FOUND" + fi + + if brew list --versions pkgconf >/dev/null 2>&1; then + echo " - pkgconf: OK" + else + echo " - pkgconf: NOT FOUND" + fi + ;; + *) # Fallback: check for header files if command_exists curl-config; then @@ -215,6 +252,21 @@ install_dependencies() { fi ;; + macos) + echo "Detected macOS system" + + local packages=(cmake pkgconf libevent nlohmann-json) + if [ $need_curl -eq 1 ]; then + packages+=(curl) + fi + if [ $need_openssl -eq 1 ]; then + packages+=(openssl@3) + fi + + echo "Installing Homebrew packages: ${packages[*]}" + brew install "${packages[@]}" + ;; + *) echo "Error: Unsupported OS: $OS" echo "Please install libcurl-devel and openssl manually" @@ -232,7 +284,12 @@ install_dependencies # Verify installation echo "" echo "==> Verifying installation..." -if command_exists curl-config && command_exists openssl; then +if [ "$OS" = "macos" ]; then + if brew list --versions cmake pkgconf curl openssl@3 libevent nlohmann-json >/dev/null 2>&1; then + echo "✓ All dependencies verified successfully!" + exit 0 + fi +elif command_exists curl-config && command_exists openssl; then echo "✓ All dependencies verified successfully!" exit 0 else diff --git a/MCP/cpp-sdk/src/client/transport/streamable_http_client_transport.cpp b/MCP/cpp-sdk/src/client/transport/streamable_http_client_transport.cpp index b134e59..8bf0907 100644 --- a/MCP/cpp-sdk/src/client/transport/streamable_http_client_transport.cpp +++ b/MCP/cpp-sdk/src/client/transport/streamable_http_client_transport.cpp @@ -15,9 +15,9 @@ namespace Mcp { // SSE field prefix lengths -constexpr size_t SSE_EVENT_PREFIX_LEN = std::strlen("event:"); // "event:" -constexpr size_t SSE_ID_PREFIX_LEN = std::strlen("id:"); // "id:" -constexpr size_t SSE_DATA_PREFIX_LEN = std::strlen("data:"); // "data:" +constexpr size_t SSE_EVENT_PREFIX_LEN = sizeof("event:") - 1; +constexpr size_t SSE_ID_PREFIX_LEN = sizeof("id:") - 1; +constexpr size_t SSE_DATA_PREFIX_LEN = sizeof("data:") - 1; constexpr std::chrono::milliseconds MAX_TIMEOUT_MS{30 * 60 * 1000}; // 30 minutes in milliseconds StreamableHttpClientTransport::StreamableHttpClientTransport(std::string url, @@ -301,7 +301,7 @@ void StreamableHttpClientTransport::HandleSseResponse(const HttpResponse& respon // Empty line indicates end of event if (line.empty()) { - if (!currentEvent.data.index() == 0 || !currentEvent.event.empty()) { + if (currentEvent.data.index() != 0 || !currentEvent.event.empty()) { HandleSseEvent(currentEvent, isInitialization); } currentEvent = EventData(); diff --git a/MCP/cpp-sdk/src/event/event_system.cpp b/MCP/cpp-sdk/src/event/event_system.cpp index f99e890..ce83230 100644 --- a/MCP/cpp-sdk/src/event/event_system.cpp +++ b/MCP/cpp-sdk/src/event/event_system.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -19,6 +18,10 @@ #include "mcp_log.h" +#if defined(__linux__) +#include +#endif + namespace Mcp { using EventMask = std::underlying_type_t; @@ -26,6 +29,31 @@ using EventMask = std::underlying_type_t; constexpr long MS_PER_SECOND = 1000; constexpr long US_PER_MS = 1000; +namespace { + +int SetFdFlags(int fd) +{ + int fileStatusFlags = fcntl(fd, F_GETFL, 0); + if (fileStatusFlags < 0) { + return -1; + } + if (fcntl(fd, F_SETFL, fileStatusFlags | O_NONBLOCK) < 0) { + return -1; + } + + int descriptorFlags = fcntl(fd, F_GETFD, 0); + if (descriptorFlags < 0) { + return -1; + } + if (fcntl(fd, F_SETFD, descriptorFlags | FD_CLOEXEC) < 0) { + return -1; + } + + return 0; +} + +} // namespace + // Translate the internal EventType bitmask into libevent flags. short ToLibeventFlags(EventType events) { @@ -92,6 +120,8 @@ struct EventSystem::Impl { std::unique_ptr handle{nullptr, &event_free}; short flags{0}; bool persistent{false}; + int notifyWriteFd{-1}; + std::atomic notifyCounter{0}; static void eventCallbackAdapter(evutil_socket_t eventFd, short events, void* arg) { @@ -153,6 +183,16 @@ EventSystem::~EventSystem() event_del(entry.second->handle.get()); entry.second->handle.reset(); } + if (entry.second != nullptr) { + if (entry.second->notifyWriteFd != -1 && entry.second->notifyWriteFd != entry.second->fd) { + ::close(entry.second->notifyWriteFd); + entry.second->notifyWriteFd = -1; + } + if (entry.second->fd != -1) { + ::close(entry.second->fd); + entry.second->fd = -1; + } + } } if (event_base_ != nullptr) { @@ -250,12 +290,32 @@ int EventSystem::CreateNotifyEventId(EventCallback callback, void* arg, bool per return -1; } - // Create an eventfd with non-blocking and close-on-exec flags. - int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); - if (efd < 0) { + int readFd = -1; + int writeFd = -1; + +#if defined(__linux__) + readFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + writeFd = readFd; + if (readFd < 0) { MCP_LOG(MCP_LOG_LEVEL_ERROR, "Create eventfd failed."); return -1; } +#else + int pipeFds[2] = {-1, -1}; + if (pipe(pipeFds) != 0) { + MCP_LOG(MCP_LOG_LEVEL_ERROR, "Create notify pipe failed."); + return -1; + } + + readFd = pipeFds[0]; + writeFd = pipeFds[1]; + if (SetFdFlags(readFd) != 0 || SetFdFlags(writeFd) != 0) { + MCP_LOG(MCP_LOG_LEVEL_ERROR, "Configure notify pipe failed."); + ::close(readFd); + ::close(writeFd); + return -1; + } +#endif // Register the eventfd for read events. Make it persistent by default unless caller says otherwise. EventType type = EventType::READ; @@ -263,32 +323,53 @@ int EventSystem::CreateNotifyEventId(EventCallback callback, void* arg, bool per type = static_cast(static_cast(type) | static_cast(EventType::PERSIST)); } - int eventId = AddEvent(efd, type, std::move(callback), arg, 0); + int eventId = AddEvent(readFd, type, std::move(callback), arg, 0); if (eventId < 0) { MCP_LOG(MCP_LOG_LEVEL_ERROR, "Failed to add eventFd to the event system."); - ::close(efd); + ::close(readFd); + if (writeFd != readFd) { + ::close(writeFd); + } return -1; } + auto it = impl_->events.find(eventId); + if (it != impl_->events.end() && it->second != nullptr) { + it->second->notifyWriteFd = writeFd; + } + return eventId; } bool EventSystem::NotifyEventId(int eventId, uint64_t increment) { - // Use eventfd write to increment the counter atomically. - ssize_t written = 0; - const uint64_t val = increment; - - int eventFd = ToEventFd(eventId); - if (eventFd == -1) { + auto it = impl_->events.find(eventId); + if (it == impl_->events.end() || it->second == nullptr) { MCP_LOG(MCP_LOG_LEVEL_ERROR, "The eventId not found."); return false; } + + ssize_t written = 0; + while (true) { - written = ::write(eventFd, &val, sizeof(val)); +#if defined(__linux__) + const uint64_t val = increment; + written = ::write(it->second->fd, &val, sizeof(val)); if (written == static_cast(sizeof(val))) { return true; } +#else + const uint64_t previous = it->second->notifyCounter.fetch_add(increment, std::memory_order_acq_rel); + if (previous > 0) { + return true; + } + + const uint8_t wakeByte = 1; + written = ::write(it->second->notifyWriteFd, &wakeByte, sizeof(wakeByte)); + if (written == static_cast(sizeof(wakeByte))) { + return true; + } +#endif if (written < 0) { if (errno == EINTR) { continue; // retry @@ -303,6 +384,7 @@ bool EventSystem::NotifyEventId(int eventId, uint64_t increment) bool EventSystem::ReadEventFd(int fd, uint64_t& outValue) { +#if defined(__linux__) uint64_t val = 0; ssize_t rd = 0; while (true) { @@ -320,6 +402,46 @@ bool EventSystem::ReadEventFd(int fd, uint64_t& outValue) // Partial read or EOF return false; } +#else + Impl::EventData* notifyEvent = nullptr; + for (auto& entry : impl_->events) { + if (entry.second != nullptr && entry.second->fd == fd && entry.second->notifyWriteFd != -1) { + notifyEvent = entry.second.get(); + break; + } + } + + if (notifyEvent == nullptr) { + return false; + } + + uint8_t buffer[64]; + while (true) { + const ssize_t rd = ::read(fd, buffer, sizeof(buffer)); + if (rd > 0) { + continue; + } + if (rd < 0) { + if (errno == EINTR) { + continue; + } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + return false; + } + break; + } + + const uint64_t count = notifyEvent->notifyCounter.exchange(0, std::memory_order_acq_rel); + if (count == 0) { + errno = EAGAIN; + return false; + } + + outValue = count; + return true; +#endif } bool EventSystem::CloseNotifyEventId(int eventId) @@ -335,9 +457,18 @@ bool EventSystem::CloseNotifyEventId(int eventId) return false; } + int writeFd = -1; + auto it = impl_->events.find(eventId); + if (it != impl_->events.end() && it->second != nullptr) { + writeFd = it->second->notifyWriteFd; + } + // Remove the libevent watcher and close the fd. RemoveEvent(eventId); ::close(eventFd); + if (writeFd != -1 && writeFd != eventFd) { + ::close(writeFd); + } return true; } diff --git a/MCP/cpp-sdk/src/net/tcp_socket.cpp b/MCP/cpp-sdk/src/net/tcp_socket.cpp index 72b3160..dc04af3 100644 --- a/MCP/cpp-sdk/src/net/tcp_socket.cpp +++ b/MCP/cpp-sdk/src/net/tcp_socket.cpp @@ -283,25 +283,35 @@ void TcpSocket::SetTcpOptions(const TcpSocketOptions& opts) MCP_LOG(MCP_LOG_LEVEL_WARN, "setsockopt(SO_KEEPALIVE) failed: %s (%d)", std::strerror(errno), errno); } if (opts.keepAliveIdleSec > 0) { - rc = ::setsockopt(fd_, IPPROTO_TCP, TCP_KEEPIDLE, &opts.keepAliveIdleSec, + int idleOpt = 0; +#if defined(__APPLE__) + idleOpt = TCP_KEEPALIVE; +#else + idleOpt = TCP_KEEPIDLE; +#endif + rc = ::setsockopt(fd_, IPPROTO_TCP, idleOpt, &opts.keepAliveIdleSec, static_cast(sizeof(opts.keepAliveIdleSec))); if (rc != 0) { - MCP_LOG(MCP_LOG_LEVEL_WARN, "setsockopt(TCP_KEEPIDLE) failed: %s (%d)", std::strerror(errno), errno); + MCP_LOG(MCP_LOG_LEVEL_WARN, "setsockopt(keepalive idle) failed: %s (%d)", std::strerror(errno), errno); } } if (opts.keepAliveIntvlSec > 0) { +#ifdef TCP_KEEPINTVL rc = ::setsockopt(fd_, IPPROTO_TCP, TCP_KEEPINTVL, &opts.keepAliveIntvlSec, static_cast(sizeof(opts.keepAliveIntvlSec))); if (rc != 0) { MCP_LOG(MCP_LOG_LEVEL_WARN, "setsockopt(TCP_KEEPINTVL) failed: %s (%d)", std::strerror(errno), errno); } +#endif } if (opts.keepAliveCnt > 0) { +#ifdef TCP_KEEPCNT rc = ::setsockopt(fd_, IPPROTO_TCP, TCP_KEEPCNT, &opts.keepAliveCnt, static_cast(sizeof(opts.keepAliveCnt))); if (rc != 0) { MCP_LOG(MCP_LOG_LEVEL_WARN, "setsockopt(TCP_KEEPCNT) failed: %s (%d)", std::strerror(errno), errno); } +#endif } } } diff --git a/MCP/cpp-sdk/src/server/server_manager.cpp b/MCP/cpp-sdk/src/server/server_manager.cpp index 1794dd4..abdcdd2 100644 --- a/MCP/cpp-sdk/src/server/server_manager.cpp +++ b/MCP/cpp-sdk/src/server/server_manager.cpp @@ -178,6 +178,7 @@ void ServerManager::Start() // Create and store notifyArg to keep it alive auto notifyArg = std::make_shared(); notifyArg->threadId = static_cast(i); + notifyArg->eventId = -1; notifyArg->eventSystem = eventSystem.get(); notifyArgs_.push_back(notifyArg); @@ -192,6 +193,7 @@ void ServerManager::Start() } threadQueueEventIds_[i] = eventId; + notifyArg->eventId = eventId; eventSystems_.push_back(std::move(eventSystem)); threadQueues_.push_back(std::move(queue)); } @@ -428,7 +430,7 @@ void ServerManager::HandleQueueNotification(int fd, short events, void* arg) // If we processed MAX_BATCH_SIZE messages and the queue is still not empty, // re-trigger the notification to continue processing in the next event loop iteration if (processedCount >= MAX_BATCH_SIZE && !queue->Empty()) { - notifyEventArg->eventSystem->NotifyEventId(fd); + notifyEventArg->eventSystem->NotifyEventId(notifyEventArg->eventId); MCP_LOG(MCP_LOG_LEVEL_DEBUG, "Thread %d re-triggered notification for remaining messages", notifyEventArg->threadId); } diff --git a/MCP/cpp-sdk/src/server/server_manager.h b/MCP/cpp-sdk/src/server/server_manager.h index c03041e..4659912 100644 --- a/MCP/cpp-sdk/src/server/server_manager.h +++ b/MCP/cpp-sdk/src/server/server_manager.h @@ -27,6 +27,7 @@ struct DispatchRequestMsg { struct NotifyEventArg { int threadId; + int eventId; EventSystem* eventSystem; }; diff --git a/MCP/cpp-sdk/src/server/transport/streamable_http_server_transport.cpp b/MCP/cpp-sdk/src/server/transport/streamable_http_server_transport.cpp index f40bac0..e527f5e 100644 --- a/MCP/cpp-sdk/src/server/transport/streamable_http_server_transport.cpp +++ b/MCP/cpp-sdk/src/server/transport/streamable_http_server_transport.cpp @@ -18,10 +18,10 @@ namespace Mcp { using Http::HttpResponse; // Header names -constexpr const char* LAST_EVENT_ID_HEADER = "last-event-id"; +[[maybe_unused]] constexpr const char* LAST_EVENT_ID_HEADER = "last-event-id"; // Special key for the standalone GET stream -constexpr const char* GET_STREAM_KEY = "_GET_stream"; +[[maybe_unused]] constexpr const char* GET_STREAM_KEY = "_GET_stream"; // Session ID validation pattern (visible ASCII characters ranging from 0x21 to 0x7E) static const std::regex SESSION_ID_PATTERN("^[\\x21-\\x7E]+$"); @@ -150,7 +150,7 @@ std::string StreamableHttpServerTransport::CreateEventData(const EventMessage& e void StreamableHttpServerTransport::HandleRequest(const HttpRequest& request, RequestContext& ctx) { MCP_LOG(MCP_LOG_LEVEL_DEBUG, "Hanle request for session %s, request.sessionid is %s", ctx.sessionId.c_str(), - GetSessionId(request)); + GetSessionId(request).c_str()); if (ctx.httpSendFunc == nullptr) { throw std::runtime_error("HTTP callback not set"); } diff --git a/MCP/cpp-sdk/third_party/third_party.cmake b/MCP/cpp-sdk/third_party/third_party.cmake index cd2cec2..03adc1c 100644 --- a/MCP/cpp-sdk/third_party/third_party.cmake +++ b/MCP/cpp-sdk/third_party/third_party.cmake @@ -150,6 +150,18 @@ function(fetch_or_find_package) else() message(STATUS "Using system ${ARG_NAME}") + + if(APPLE) + if(TARGET "${ARG_SYSTEM_PACKAGE_NAME}::${ARG_SYSTEM_PACKAGE_NAME}") + target_link_libraries(third_party_headers INTERFACE "${ARG_SYSTEM_PACKAGE_NAME}::${ARG_SYSTEM_PACKAGE_NAME}") + elseif(TARGET "${ARG_NAME}::${ARG_NAME}") + target_link_libraries(third_party_headers INTERFACE "${ARG_NAME}::${ARG_NAME}") + elseif("${ARG_SYSTEM_PACKAGE_NAME}" STREQUAL "Libevent" AND TARGET event) + target_link_libraries(third_party_headers INTERFACE event) + elseif("${ARG_SYSTEM_PACKAGE_NAME}" STREQUAL "nlohmann_json" AND TARGET nlohmann_json::nlohmann_json) + target_link_libraries(third_party_headers INTERFACE nlohmann_json::nlohmann_json) + endif() + endif() endif() endfunction()