Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d63d323
[Build] Add MinGW toolchain and Windows build configuration (#136)
viksachdev Dec 15, 2025
e6df71b
[C++] Add Windows socket interface compatibility (#136)
viksachdev Dec 15, 2025
822a460
[C++] Add Windows includes for connection handling (#136)
viksachdev Dec 15, 2025
2bfbcb2
[C++] Add Windows compatibility for network listeners (#136)
viksachdev Dec 15, 2025
a674921
[C++] Add Windows pipe and socket handling in dispatcher (#136)
viksachdev Dec 15, 2025
b761cd6
[C++] Add Windows stdio pipe transport implementation (#136)
viksachdev Dec 15, 2025
00603d9
[C++] Add Windows TCP transport socket compatibility (#136)
viksachdev Dec 15, 2025
dd584cf
[C++] Fix Windows DELETE macro conflict in HttpMethod enum (#136)
viksachdev Dec 15, 2025
d654a4a
[C++] Add Windows signal handling for example server (#136)
viksachdev Dec 15, 2025
390367f
[C++] Fix Windows send() and memmem() compatibility (#136)
viksachdev Dec 15, 2025
ef02116
[C++] Add Windows includes for MCP connection manager (#136)
viksachdev Dec 15, 2025
85eccb8
[C++] Fix Windows ERROR macro conflict in McpProtocolState enum (#136)
viksachdev Dec 15, 2025
737d149
[C++] Unify socket FD types to use SOCKET on Windows (#136)
viksachdev Dec 18, 2025
9216bfe
[C++] Add Windows libevent dispatcher fixes and debug logging (#136)
viksachdev Dec 18, 2025
40304b5
[C++] Use level-triggered events on Windows (#136)
viksachdev Dec 18, 2025
74e1f83
[C++] Fix TCP listener accept() return type on Windows (#136)
viksachdev Dec 18, 2025
046f098
[C++] Add setNonBlocking error handling (#136)
viksachdev Dec 18, 2025
65c125d
[C++] Add Windows stdio transport compatibility (#136)
viksachdev Dec 18, 2025
407aaa7
[C++] Fix Windows ERROR macro conflict in FilterEventSeverity (#136)
viksachdev Dec 18, 2025
bacb094
[C++] Add missing array include for address.h (#136)
viksachdev Dec 18, 2025
34cc7c9
[C++] Fix Windows connection manager pipe address handling (#136)
viksachdev Dec 18, 2025
04f951c
[C++] Add debug logging for socket operations (#136)
viksachdev Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,30 @@ endif()
#Find packages
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(yaml-cpp REQUIRED)

# Try to find yaml-cpp, fetch if not found
find_package(yaml-cpp QUIET)
if(NOT yaml-cpp_FOUND)
message(STATUS "yaml-cpp not found, fetching from source...")
include(FetchContent)
# Set policy for older cmake_minimum_required in yaml-cpp
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif()
FetchContent_Declare(
yaml-cpp
GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git
GIT_TAG yaml-cpp-0.7.0
)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "" FORCE)
set(YAML_CPP_FORMAT_SOURCE OFF CACHE BOOL "" FORCE)
set(YAML_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(yaml-cpp)
message(STATUS "yaml-cpp fetched successfully")
endif()

#Find libevent
find_package(PkgConfig)
Expand Down Expand Up @@ -297,9 +320,23 @@ else()
message(STATUS "nghttp2 support disabled")
endif()

# fmt library for formatting - use system-installed version
find_package(fmt REQUIRED)
message(STATUS "Found fmt: ${fmt_VERSION}")
# fmt library for formatting - try system, fetch if not found
find_package(fmt QUIET)
if(NOT fmt_FOUND)
message(STATUS "fmt not found, fetching from source...")
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
set(FMT_TEST OFF CACHE BOOL "" FORCE)
set(FMT_DOC OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(fmt)
message(STATUS "fmt fetched successfully")
else()
message(STATUS "Found fmt: ${fmt_VERSION}")
endif()

# nlohmann/json
FetchContent_Declare(
Expand Down
59 changes: 59 additions & 0 deletions build-mingw.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/bin/bash
# Build script for GopherMCP with MinGW on Cygwin
# Usage: ./build-mingw.sh [clean|release|debug]

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="${SCRIPT_DIR}/build-mingw"
BUILD_TYPE="${1:-Release}"

# Handle clean option
if [ "$1" == "clean" ]; then
echo "Cleaning build directory..."
rm -rf "${BUILD_DIR}"
echo "Done."
exit 0
fi

# Set build type
if [ "$1" == "debug" ]; then
BUILD_TYPE="Debug"
elif [ "$1" == "release" ]; then
BUILD_TYPE="Release"
fi

echo "========================================"
echo "GopherMCP MinGW Build Script"
echo "========================================"
echo "Build type: ${BUILD_TYPE}"
echo "Build directory: ${BUILD_DIR}"
echo ""

# Create build directory
mkdir -p "${BUILD_DIR}"
cd "${BUILD_DIR}"

# Configure with CMake
echo "Configuring with CMake..."
cmake -DCMAKE_TOOLCHAIN_FILE="${SCRIPT_DIR}/cmake/mingw-w64-toolchain.cmake" \
-DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DBUILD_EXAMPLES=ON \
-DBUILD_TESTS=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_STATIC_LIBS=ON \
"${SCRIPT_DIR}"

# Build
echo ""
echo "Building mcp_example_server..."
cmake --build . --target mcp_example_server -j$(nproc 2>/dev/null || echo 4)

echo ""
echo "========================================"
echo "Build complete!"
echo "========================================"
echo "Executable: ${BUILD_DIR}/examples/mcp/mcp_example_server.exe"
echo ""
echo "To run: ${BUILD_DIR}/examples/mcp/mcp_example_server.exe --help"
73 changes: 73 additions & 0 deletions cmake/mingw-w64-toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# MinGW-w64 Cross-Compilation Toolchain for Cygwin
# Usage: cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/mingw-w64-toolchain.cmake ..

set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)

# Specify the cross compilers
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc.exe)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++.exe)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres.exe)
set(CMAKE_AR x86_64-w64-mingw32-ar.exe)
set(CMAKE_RANLIB x86_64-w64-mingw32-ranlib.exe)

# MinGW sysroot paths
set(MINGW_SYSROOT /usr/x86_64-w64-mingw32/sys-root/mingw)

# Target environment - Cygwin's MinGW sysroot
set(CMAKE_FIND_ROOT_PATH
${MINGW_SYSROOT}
/usr/x86_64-w64-mingw32
)

# Search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search for libraries and headers in the target directories
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

# OpenSSL paths for MinGW
set(OPENSSL_ROOT_DIR ${MINGW_SYSROOT})
set(OPENSSL_INCLUDE_DIR ${MINGW_SYSROOT}/include)
set(OPENSSL_CRYPTO_LIBRARY ${MINGW_SYSROOT}/lib/libcrypto.dll.a)
set(OPENSSL_SSL_LIBRARY ${MINGW_SYSROOT}/lib/libssl.dll.a)

# yaml-cpp paths for MinGW
set(yaml-cpp_DIR ${MINGW_SYSROOT}/lib/cmake/yaml-cpp)
set(YAML_CPP_INCLUDE_DIR ${MINGW_SYSROOT}/include)
set(YAML_CPP_LIBRARIES ${MINGW_SYSROOT}/lib/libyaml-cpp.dll.a)

# libevent paths for MinGW
set(LIBEVENT_INCLUDE_DIRS ${MINGW_SYSROOT}/include)
set(LIBEVENT_LIBRARIES
${MINGW_SYSROOT}/lib/libevent.dll.a
${MINGW_SYSROOT}/lib/libevent_core.dll.a
)

# fmt library paths
set(fmt_DIR ${MINGW_SYSROOT}/lib/cmake/fmt)

# pkg-config path for MinGW packages
set(ENV{PKG_CONFIG_PATH} "${MINGW_SYSROOT}/lib/pkgconfig")
set(PKG_CONFIG_EXECUTABLE /usr/bin/x86_64-w64-mingw32-pkg-config)

# Additional include/library paths
set(CMAKE_INCLUDE_PATH ${MINGW_SYSROOT}/include)
set(CMAKE_LIBRARY_PATH ${MINGW_SYSROOT}/lib)
set(CMAKE_PREFIX_PATH ${MINGW_SYSROOT})

# Windows platform definitions
set(WIN32 TRUE)
set(MINGW TRUE)
add_definitions(-D_WIN32 -DWIN32 -D_WINDOWS -DMINGW)

# Ensure Windows socket libraries are linked
link_libraries(ws2_32 mswsock)

# Disable features that may cause issues with cross-compilation
set(CMAKE_CROSSCOMPILING TRUE)

# Static linking of libgcc and libstdc++ for easier distribution
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++")
9 changes: 8 additions & 1 deletion examples/mcp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,11 @@ install(TARGETS mcp_example_client mcp_example_server mcp_config_example_server
install(FILES
../configs/mcp_server_example.json
DESTINATION share/mcp/examples/configs
)
)

# Windows-specific libraries
if(WIN32)
target_link_libraries(mcp_example_client ws2_32 mswsock)
target_link_libraries(mcp_example_server ws2_32 mswsock)
target_link_libraries(mcp_config_example_server ws2_32 mswsock)
endif()
48 changes: 46 additions & 2 deletions examples/mcp/mcp_example_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,16 @@
#include <fstream>
#include <iostream>
#include <mutex>
#include <signal.h>
#include <sstream>
#include <thread>

// Platform-specific includes for signal/shutdown handling
#ifdef _WIN32
#include <windows.h>
#else
#include <signal.h>
#include <unistd.h> // for _exit
#endif

#include "mcp/json/json_bridge.h"
#include "mcp/server/mcp_server.h"
Expand Down Expand Up @@ -134,6 +140,39 @@ struct ServerOptions {
std::string http_health_path = "/health";
};

// Platform-specific shutdown handler
#ifdef _WIN32
// Windows Console Control Handler
BOOL WINAPI ConsoleCtrlHandler(DWORD ctrlType) {
switch (ctrlType) {
case CTRL_C_EVENT:
case CTRL_BREAK_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_SHUTDOWN_EVENT:
std::cerr << "\n[INFO] Received console control event " << ctrlType
<< ", initiating graceful shutdown..." << std::endl;

// Set the shutdown flag
g_shutdown = true;

// Notify the shutdown monitor thread
g_shutdown_cv.notify_all();

// For safety, if we receive multiple signals, force exit
static std::atomic<int> signal_count(0);
signal_count++;
if (signal_count > 1) {
std::cerr << "\n[INFO] Force shutdown after multiple signals..."
<< std::endl;
ExitProcess(0);
}
return TRUE;
default:
return FALSE;
}
}
#else
// Unix signal handler
void signal_handler(int signal) {
// Signal handlers should do minimal work
// Just set the flag and notify - actual shutdown happens in main thread
Expand All @@ -158,6 +197,7 @@ void signal_handler(int signal) {
_exit(0);
}
}
#endif

void printUsage(const char* program) {
std::cerr << "USAGE: " << program << " [options]\n\n";
Expand Down Expand Up @@ -847,9 +887,13 @@ void printStatistics(const McpServer& server) {
}

int main(int argc, char* argv[]) {
// Install signal handlers
// Install platform-specific signal/shutdown handlers
#ifdef _WIN32
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
#else
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
#endif

// Parse command-line options
ServerOptions options = parseArguments(argc, argv);
Expand Down
27 changes: 21 additions & 6 deletions include/mcp/event/event_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,24 @@
#include <thread>
#include <vector>

#ifdef _WIN32
#include <winsock2.h>
#endif

#include "mcp/core/compat.h"

namespace mcp {
namespace event {

// Platform-specific socket/fd type for event monitoring
// On Windows, socket handles are SOCKET type
// On Unix/Linux, file descriptors are 32-bit int
#ifdef _WIN32
using os_fd_t = SOCKET;
#else
using os_fd_t = int;
#endif

// Forward declaration
class WatermarkFactory {
public:
Expand Down Expand Up @@ -84,11 +97,12 @@ enum class FileTriggerType {

// Determine platform-preferred event type
constexpr FileTriggerType determinePlatformPreferredEventType() {
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
defined(FORCE_LEVEL_EVENTS)
// Windows doesn't support native edge triggers, use emulated
// FORCE_LEVEL_EVENTS allows testing Windows behavior on POSIX
return FileTriggerType::EmulatedEdge;
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
// Windows select() only supports level-triggered mode
return FileTriggerType::Level;
#elif defined(FORCE_LEVEL_EVENTS)
// FORCE_LEVEL_EVENTS allows testing level-triggered behavior on POSIX
return FileTriggerType::Level;
#elif defined(__APPLE__) || defined(__FreeBSD__)
// macOS/BSD: Use level-triggered to avoid issues
// Edge-triggered with EV_CLEAR causes problems with our event handling
Expand Down Expand Up @@ -309,8 +323,9 @@ class Dispatcher : public DispatcherBase {

/**
* Create a file event that monitors a file descriptor.
* @param fd Platform-specific socket/fd (os_fd_t: int on Unix, uintptr_t on Windows)
*/
virtual FileEventPtr createFileEvent(int fd,
virtual FileEventPtr createFileEvent(os_fd_t fd,
FileReadyCb cb,
FileTriggerType trigger,
uint32_t events) = 0;
Expand Down
Loading