Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ae36a12
[Build] Add MinGW toolchain and Windows build configuration (#136)
viksachdev Dec 15, 2025
f68c000
[C++] Add Windows socket interface compatibility (#136)
viksachdev Dec 15, 2025
386b846
[C++] Add Windows includes for connection handling (#136)
viksachdev Dec 15, 2025
3b27f6c
[C++] Add Windows compatibility for network listeners (#136)
viksachdev Dec 15, 2025
2cfc4e4
[C++] Add Windows pipe and socket handling in dispatcher (#136)
viksachdev Dec 15, 2025
da2a33e
[C++] Add Windows stdio pipe transport implementation (#136)
viksachdev Dec 15, 2025
66a9ad0
[C++] Add Windows TCP transport socket compatibility (#136)
viksachdev Dec 15, 2025
dfd4acf
[C++] Fix Windows DELETE macro conflict in HttpMethod enum (#136)
viksachdev Dec 15, 2025
dc4d990
[C++] Add Windows signal handling for example server (#136)
viksachdev Dec 15, 2025
e2d5e7b
[C++] Fix Windows send() and memmem() compatibility (#136)
viksachdev Dec 15, 2025
e9e7708
[C++] Add Windows includes for MCP connection manager (#136)
viksachdev Dec 15, 2025
00ed9ff
[C++] Fix Windows ERROR macro conflict in McpProtocolState enum (#136)
viksachdev Dec 15, 2025
21805db
[C++] Unify socket FD types to use SOCKET on Windows (#136)
viksachdev Dec 18, 2025
84f151f
[C++] Add Windows libevent dispatcher fixes and debug logging (#136)
viksachdev Dec 18, 2025
d56cd08
[C++] Use level-triggered events on Windows (#136)
viksachdev Dec 18, 2025
f1af5c9
[C++] Fix TCP listener accept() return type on Windows (#136)
viksachdev Dec 18, 2025
5c6ae31
[C++] Add setNonBlocking error handling (#136)
viksachdev Dec 18, 2025
3e07a0e
[C++] Add Windows stdio transport compatibility (#136)
viksachdev Dec 18, 2025
68e2eb7
[C++] Fix Windows ERROR macro conflict in FilterEventSeverity (#136)
viksachdev Dec 18, 2025
33f3db4
[C++] Add missing array include for address.h (#136)
viksachdev Dec 18, 2025
63d491b
[C++] Fix Windows connection manager pipe address handling (#136)
viksachdev Dec 18, 2025
f65deee
[C++] Add debug logging for socket operations (#136)
viksachdev Dec 18, 2025
469187e
[C++] Use libevent evutil_socket_t for callback compatibility (#136)
viksachdev Dec 23, 2025
fa9a02b
[C++] Use CRT functions for Windows pipe I/O operations (#136)
viksachdev Dec 23, 2025
744a186
[C++] Clean up libevent dispatcher debug logging (#136)
viksachdev Dec 23, 2025
c3a7a29
[C++] Clean up TCP listener and add portable error handling (#136)
viksachdev Dec 23, 2025
3b6202c
[C++] Fix Windows ERROR macro conflict with FilterEventSeverity enum …
viksachdev Dec 23, 2025
91802d2
[C++] Apply clang-format to Windows platform support code (#136)
viksachdev Dec 24, 2025
49d89a3
[C++] Make format CPP code to apply clang-format (#136)
Dec 24, 2025
4f86b10
[C++] Conditionally include libevent header for Windows only (#136)
viksachdev Dec 24, 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
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
28 changes: 22 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,10 @@ 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
25 changes: 17 additions & 8 deletions include/mcp/event/libevent_dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ namespace event {
// Rename to avoid conflict with struct event
using libevent_event = struct event;

// Use libevent's socket type for callback signature compatibility.
// On Windows, libevent uses intptr_t (SOCKET), on POSIX it uses int.
#ifdef _WIN32
#include <event2/util.h>
using libevent_socket_t = evutil_socket_t;
#else
using libevent_socket_t = int;
#endif

/**
* @brief Libevent-based implementation of the Dispatcher interface
*
Expand All @@ -40,7 +49,7 @@ class LibeventDispatcher : public Dispatcher {
void registerWatchdog(const WatchDogSharedPtr& watchdog,
std::chrono::milliseconds min_touch_interval) override;

FileEventPtr createFileEvent(int fd,
FileEventPtr createFileEvent(os_fd_t fd,
FileReadyCb cb,
FileTriggerType trigger,
uint32_t events) override;
Expand Down Expand Up @@ -87,7 +96,7 @@ class LibeventDispatcher : public Dispatcher {
class FileEventImpl : public FileEvent {
public:
FileEventImpl(LibeventDispatcher& dispatcher,
int fd,
os_fd_t fd,
FileReadyCb cb,
FileTriggerType trigger,
uint32_t events);
Expand All @@ -99,13 +108,13 @@ class LibeventDispatcher : public Dispatcher {
void registerEventIfEmulatedEdge(uint32_t event) override;

private:
static void eventCallback(int fd, short events, void* arg);
static void eventCallback(libevent_socket_t fd, short events, void* arg);
void assignEvents(uint32_t events);
void updateEvents(uint32_t events);
void mergeInjectedEventsAndRunCb(uint32_t events);

LibeventDispatcher& dispatcher_;
int fd_;
os_fd_t fd_;
FileReadyCb cb_;
FileTriggerType trigger_;
libevent_event* event_;
Expand All @@ -129,7 +138,7 @@ class LibeventDispatcher : public Dispatcher {
bool enabled() override;

private:
static void timerCallback(int fd, short events, void* arg);
static void timerCallback(libevent_socket_t fd, short events, void* arg);

LibeventDispatcher& dispatcher_;
TimerCb cb_;
Expand Down Expand Up @@ -165,7 +174,7 @@ class LibeventDispatcher : public Dispatcher {
~SignalEventImpl() override;

private:
static void signalCallback(int fd, short events, void* arg);
static void signalCallback(libevent_socket_t fd, short events, void* arg);

LibeventDispatcher& dispatcher_;
int signal_num_;
Expand All @@ -185,7 +194,7 @@ class LibeventDispatcher : public Dispatcher {
void runDeferredDeletes();
void touchWatchdog();
void initializeLibevent();
static void postWakeupCallback(int fd, short events, void* arg);
static void postWakeupCallback(libevent_socket_t fd, short events, void* arg);

// Member variables
const std::string name_;
Expand All @@ -196,7 +205,7 @@ class LibeventDispatcher : public Dispatcher {
// Post callback handling
std::mutex post_mutex_;
std::queue<PostCb> post_callbacks_;
int wakeup_fd_[2]; // Pipe for waking up event loop
libevent_socket_t wakeup_fd_[2]; // Pipe for waking up event loop
libevent_event* wakeup_event_;

// Deferred deletion
Expand Down
4 changes: 4 additions & 0 deletions include/mcp/filter/filter_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ enum class FilterEventType {
*
* Severity classification for filtering and prioritization of events.
*/
// Windows defines ERROR as a macro in wingdi.h, undef it to avoid conflict
#ifdef ERROR
#undef ERROR
#endif
enum class FilterEventSeverity {
TRACE = 0, ///< Trace-level diagnostic information
DEBUG = 1, ///< Debug-level information
Expand Down
4 changes: 4 additions & 0 deletions include/mcp/http/http_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ enum class HttpStatusCode : uint16_t {
};

// HTTP methods
// Note: Windows defines DELETE as a macro in winnt.h, so we need to undefine it
#ifdef DELETE
#undef DELETE
#endif
enum class HttpMethod {
GET,
POST,
Expand Down
Loading
Loading