cmake_minimum_required(VERSION 3.15)
project(mcp LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
option(MCP_ENABLE_TESTS "Build unit tests" OFF)
option(MCP_ENABLE_EXAMPLES "Build examples" OFF)
option(MCP_ENABLE_COVERAGE "Enable code coverage" OFF)
option(MCP_ENABLE_STDIO "Enable stdio UT" OFF)
option(MCP_ENABLE_ST_TESTS "Build ST integration tests (tests/ut/st); requires client, server, and HTTP" OFF)
option(MCP_BUILD_CLIENT "Build MCP client components" ON)
option(MCP_BUILD_SERVER "Build MCP server components" ON)
option(MCP_WITH_HTTP "Build HTTP client transport components" ON)
add_compile_definitions(MCP_WITH_HTTP=$<BOOL:${MCP_WITH_HTTP}>)
include(third_party/third_party.cmake)
find_package(OpenSSL REQUIRED)
if(MCP_BUILD_CLIENT AND MCP_WITH_HTTP)
find_package(CURL REQUIRED)
endif()
add_compile_options(-fPIC)
add_compile_options(-ftrapv)
if(MCP_ENABLE_COVERAGE)
message(STATUS "Code coverage is enabled")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(-fprofile-arcs -ftest-coverage)
add_link_options(-fprofile-arcs -ftest-coverage)
else()
message(WARNING "Code coverage not supported for compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()
endif()
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -D_FORTIFY_SOURCE=2")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
set(CMAKE_FLAGS_ASAN
-fsanitize=address
-fsanitize-recover=address
-fsanitize=leak
-fno-omit-frame-pointer
)
add_compile_options(-Wall -Werror -Wno-error=unused-variable -Wfloat-equal -Wtrampolines -fno-strict-aliasing -fstack-protector-strong)
if (ASAN STREQUAL "enable")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(${CMAKE_FLAGS_ASAN})
add_link_options(${CMAKE_FLAGS_ASAN})
else()
message(WARNING "ASAN not supported for compiler: ${CMAKE_CXX_COMPILER_ID}")
endif()
endif()
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(CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -s")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_SKIP_RPATH TRUE)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Building in Debug mode with full debug symbols")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
message(STATUS "Building in Release with Debug Info mode")
endif()
set(_mcp_event_pthreads_target "")
if(TARGET event_pthreads)
set(_mcp_event_pthreads_target event_pthreads)
elseif(TARGET libevent_pthreads)
set(_mcp_event_pthreads_target libevent_pthreads)
elseif(TARGET "libevent::pthread")
set(_mcp_event_pthreads_target libevent::pthread)
else()
find_library(_mcp_event_pthreads_lib NAMES event_pthreads libevent_pthreads)
if(_mcp_event_pthreads_lib)
set(_mcp_event_pthreads_target ${_mcp_event_pthreads_lib})
else()
message(WARNING "libevent pthread helper target not found; thread support may fail at link time.")
endif()
endif()
add_subdirectory(src)
set(_mcp_libevent_target "")
if(TARGET event)
set(_mcp_libevent_target event)
elseif(TARGET libevent)
set(_mcp_libevent_target libevent)
elseif(TARGET "libevent::event")
set(_mcp_libevent_target libevent::event)
elseif(TARGET libevent_core)
set(_mcp_libevent_target libevent_core)
else()
message(WARNING "No imported libevent target found; attempting to link 'event' (may fail).")
set(_mcp_libevent_target event)
endif()
target_link_libraries(mcp PUBLIC ${_mcp_libevent_target} nlohmann_json::nlohmann_json nlohmann_json_schema_validator)
target_link_libraries(mcp PRIVATE http_parser)
if(MCP_BUILD_CLIENT AND MCP_WITH_HTTP)
target_link_libraries(mcp PUBLIC CURL::libcurl)
endif()
target_link_libraries(mcp PUBLIC OpenSSL::SSL OpenSSL::Crypto)
target_link_libraries(mcp PUBLIC ${_mcp_event_pthreads_target})
set_target_properties(mcp PROPERTIES
OUTPUT_NAME "mcp"
)
if(MCP_ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
if(MCP_ENABLE_EXAMPLES)
add_subdirectory(examples)
endif()
include(GNUInstallDirs)
install(TARGETS mcp
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ
DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
FILES_MATCHING PATTERN "*.h"
)
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()