# Centralized third-party dependencies (FetchContent) for mcp_cpp
# This file will try to find system-provided packages first, then fall back to FetchContent.
include(FetchContent)
# Set FetchContent base directory
set(FETCHCONTENT_BASE_DIR "${CMAKE_SOURCE_DIR}/third_party" CACHE PATH "" FORCE)
add_library(third_party_headers INTERFACE)
# Generic function to fetch or find third-party packages
# Usage: fetch_or_find_package(
# NAME <package_name>
# GIT_REPO <git_repository_url>
# GIT_TAG <git_tag>
# [SYSTEM_PACKAGE_NAME <name>] # Optional, defaults to NAME
# [TARGET_NAME <name>] # Optional, expected target name after fetch
# [OPTIONS <var1> <val1> <var2> <val2> ...] # Optional build options
# )
function(fetch_or_find_package)
set(options "")
set(oneValueArgs NAME GIT_REPO GIT_TAG SYSTEM_PACKAGE_NAME TARGET_NAME)
set(multiValueArgs OPTIONS)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Validate required arguments
if(NOT ARG_NAME)
message(FATAL_ERROR "fetch_or_find_package: NAME is required")
endif()
if(NOT ARG_GIT_REPO)
message(FATAL_ERROR "fetch_or_find_package: GIT_REPO is required")
endif()
if(NOT ARG_GIT_TAG)
message(FATAL_ERROR "fetch_or_find_package: GIT_TAG is required")
endif()
# Use NAME as system package name if not specified
if(NOT ARG_SYSTEM_PACKAGE_NAME)
set(ARG_SYSTEM_PACKAGE_NAME ${ARG_NAME})
endif()
# Convert package name to uppercase for option variable
string(TOUPPER ${ARG_NAME} PACKAGE_UPPER)
string(REPLACE "-" "_" PACKAGE_UPPER ${PACKAGE_UPPER})
# Create option to control whether to use system package
set(OPTION_VAR "MCP_USE_SYSTEM_${PACKAGE_UPPER}")
option(${OPTION_VAR} "Try to use system ${ARG_NAME} before fetching" ON)
# Try to find system package first
set(PACKAGE_FOUND FALSE)
if(${OPTION_VAR})
message(STATUS "try to find system ${ARG_SYSTEM_PACKAGE_NAME}")
# 1 Try find _package first
find_package(${ARG_SYSTEM_PACKAGE_NAME} QUIET)
if (${ARG_SYSTEM_PACKAGE_NAME}_FOUND)
set(PACKAGE_FOUND TRUE)
message(STATUS "find package success")
endif()
# 2 Try pkgconfig to find
if (NOT PACKAGE_FOUND)
message(STATUS "find package failed")
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
set(_mcp_pkg_names "${ARG_SYSTEM_PACKAGE_NAME}")
string(TOLOWER "${ARG_SYSTEM_PACKAGE_NAME}" _mcp_pkg_lower)
list(APPEND _mcp_pkg_names "${_mcp_pkg_lower}")
list(REMOVE_DUPLICATES _mcp_pkg_names)
foreach(_mcp_pkg_name IN LISTS _mcp_pkg_names)
pkg_check_modules(_mcp_pkg QUIET IMPORTED_TARGET "${_mcp_pkg_name}")
if(_mcp_pkg_FOUND AND _mcp_pkg_INCLUDE_DIRS AND _mcp_pkg_LIBRARIES)
if (NOT "${_mcp_pkg_INCLUDE_DIRS}" STREQUAL "NOTFOUND" AND NOT "${_mcp_pkg_LIBRARIES}" STREQUAL "NOTFOUND")
set(PACKAGE_FOUND TRUE)
message(STATUS "pkgconfig success")
message(STATUS "_mcp_pkg_INCLUDE_DIRS": ${_mcp_pkg_INCLUDE_DIRS})
message(STATUS "_mcp_pkg_LIBRARIES": ${_mcp_pkg_LIBRARIES})
if(NOT TARGET "${ARG_SYSTEM_PACKAGE_NAME}::${ARG_SYSTEM_PACKAGE_NAME}")
add_library("${ARG_SYSTEM_PACKAGE_NAME}::${ARG_SYSTEM_PACKAGE_NAME}" INTERFACE IMPORTED)
set_target_properties("${ARG_SYSTEM_PACKAGE_NAME}::${ARG_SYSTEM_PACKAGE_NAME}" PROPERTIES
INTERFACE_LINK_LIBRARIES "PkgConfig::_mcp_pkg"
)
endif()
if ("${ARG_SYSTEM_PACKAGE_NAME}" STREQUAL "libevent")
if(NOT TARGET event)
add_library(event INTERFACE IMPORTED)
set_target_properties(event PROPERTIES
INTERFACE_LINK_LIBRARIES "PkgConfig::_mcp_pkg"
)
endif()
elseif("${ARG_SYSTEM_PACKAGE_NAME}" STREQUAL "nlohmann_json")
if(NOT TARGET nlohmann_json::nlohmann_json)
add_library(nlohmann_json::nlohmann_json INTERFACE IMPORTED)
set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
INTERFACE_LINK_LIBRARIES "PkgConfig::_mcp_pkg"
)
endif()
endif()
endif()
endif()
endforeach()
endif()
endif()
# 3 find_path and find_library finally
if (NOT PACKAGE_FOUND)
message(STATUS "pkgconfig fail")
if ("${ARG_NAME}" STREQUAL "libevent")
message(STATUS "try find_path and find_library")
find_path(_mcp_libevent_include_dir
NAMES event.h
PATHS /usr/include /usr/local/include
)
find_library(_mcp_libevent_lib
NAMES event
PATHS /usr/lib /usr/lib64
)
find_library(_mcp_libevent_pthreads_lib
NAMES event_pthreads
PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64
)
if(_mcp_libevent_include_dir AND _mcp_libevent_lib AND _mcp_libevent_pthreads_lib)
set(PACKAGE_FOUND TRUE)
message(STATUS "Using system libevent: include=${_mcp_libevent_include_dir}, lib=${_mcp_libevent_lib}, pthread_lib=${_mcp_libevent_pthreads_lib}")
if(NOT TARGET libevent)
add_library(libevent SHARED IMPORTED)
set_target_properties(libevent PROPERTIES
IMPORTED_LOCATION "${_mcp_libevent_lib}"
INTERFACE_INCLUDE_DIRECTORIES "${_mcp_libevent_include_dir}"
)
endif()
if(NOT TARGET libevent_headers)
add_library(libevent_headers INTERFACE)
target_include_directories(libevent_headers
INTERFACE
"${_mcp_libevent_include_dir}"
)
endif()
if(NOT TARGET libevent_pthreads)
add_library(libevent_pthreads SHARED IMPORTED)
set_target_properties(libevent_pthreads PROPERTIES
IMPORTED_LOCATION "${_mcp_libevent_pthreads_lib}"
INTERFACE_INCLUDE_DIRECTORIES "${_mcp_libevent_include_dir}"
)
endif()
else()
message(STATUS "fail to using system libevent: include=${_mcp_libevent_include_dir}, lib=${_mcp_libevent_lib}, pthread_lib=${_mcp_libevent_pthrad_lib}")
endif()
if (NOT PACKAGE_FOUND)
message(STATUS "find_path and find_library fail")
endif()
endif()
endif()
endif()
set(${PACKAGE_UPPER}_SRC_DIR "${CMAKE_SOURCE_DIR}/third_party/${ARG_SYSTEM_PACKAGE_NAME}-src")
# If not found, fetch from repository
if(NOT PACKAGE_FOUND)
message(STATUS "find ${${PACKAGE_UPPER}_SRC_DIR}/CMakeLists.txt")
if(EXISTS "${${PACKAGE_UPPER}_SRC_DIR}/CMakeLists.txt")
message(STATUS "[${ARG_NAME}] Found existing source at ${${PACKAGE_UPPER}_SRC_DIR}, using it")
FetchContent_Declare(
${ARG_NAME}
SOURCE_DIR "${${PACKAGE_UPPER}_SRC_DIR}"
)
else()
message(STATUS "[${ARG_NAME}] Source not found, will fetch from repository")
# download
FetchContent_Declare(
${ARG_NAME}
GIT_REPOSITORY ${ARG_GIT_REPO}
GIT_TAG ${ARG_GIT_TAG}
)
endif()
# Apply custom build options if provided
if(ARG_OPTIONS)
list(LENGTH ARG_OPTIONS options_length)
math(EXPR options_pairs "${options_length} / 2")
math(EXPR last_index "${options_length} - 1")
foreach(i RANGE 0 ${last_index} 2)
list(GET ARG_OPTIONS ${i} opt_name)
math(EXPR val_index "${i} + 1")
list(GET ARG_OPTIONS ${val_index} opt_value)
set(${opt_name} ${opt_value} CACHE BOOL "Auto-configured by fetch_or_find_package" FORCE)
endforeach()
endif()
FetchContent_MakeAvailable(${ARG_NAME})
# Print source and binary directories
message(STATUS "${ARG_NAME} source dir: ${${ARG_NAME}_SOURCE_DIR}")
message(STATUS "${ARG_NAME} binary dir: ${${ARG_NAME}_BINARY_DIR}")
# Handle target name mismatch if TARGET_NAME is specified
if(ARG_TARGET_NAME AND NOT TARGET ${ARG_NAME})
if(TARGET ${ARG_TARGET_NAME})
add_library(${ARG_NAME} ALIAS ${ARG_TARGET_NAME})
message(STATUS "Created alias ${ARG_NAME} -> ${ARG_TARGET_NAME}")
endif()
endif()
if (${ARG_NAME} STREQUAL "http_parser")
target_include_directories(third_party_headers INTERFACE
"${${ARG_NAME}_BINARY_DIR}/"
"${${ARG_NAME}_SOURCE_DIR}/"
)
else()
target_include_directories(third_party_headers INTERFACE
"${${ARG_NAME}_BINARY_DIR}/include/"
"${${ARG_NAME}_SOURCE_DIR}/include/"
)
endif()
else()
message(STATUS "Using system ${ARG_NAME}")
endif()
endfunction()
# Fetch libevent
fetch_or_find_package(
NAME libevent
GIT_REPO https://github.com/libevent/libevent.git
GIT_TAG release-2.1.12-stable
SYSTEM_PACKAGE_NAME Libevent
OPTIONS
EVENT__LIBRARY_TYPE SHARED
EVENT__DISABLE_OPENSSL ON
EVENT__DISABLE_TESTS ON
EVENT__DISABLE_BENCHMARK ON
)
# Fetch nlohmann/json (header-only library)
fetch_or_find_package(
NAME nlohmann_json
GIT_REPO https://github.com/nlohmann/json.git
GIT_TAG v3.11.2
OPTIONS
JSON_BuildTests OFF
JSON_Install OFF
)
# Fetch json-schema-validator (with existing source priority)
# Ensure /usr/local is in the search path for find_package
list(APPEND CMAKE_PREFIX_PATH "/usr/local")
fetch_or_find_package(
NAME nlohmann_json_schema_validator
GIT_REPO https://github.com/pboettch/json-schema-validator.git
GIT_TAG 2.3.0
SYSTEM_PACKAGE_NAME nlohmann_json_schema_validator
TARGET_NAME nlohmann_json_schema_validator
)
# Normalize and link json-schema-validator target
# Handle common system target name variants and create a unified alias if needed
if(NOT TARGET nlohmann_json_schema_validator)
# Check for common exported target names from the CMake config
if(TARGET nlohmann_json_schema_validator::nlohmann_json_schema_validator)
add_library(nlohmann_json_schema_validator ALIAS nlohmann_json_schema_validator::nlohmann_json_schema_validator)
message(STATUS "Aliased nlohmann_json_schema_validator::nlohmann_json_schema_validator -> nlohmann_json_schema_validator")
elseif(TARGET json-schema-validator)
add_library(nlohmann_json_schema_validator ALIAS json-schema-validator)
message(STATUS "Aliased json-schema-validator -> nlohmann_json_schema_validator")
elseif(TARGET nlohmann_json_schema_validator::json-schema-validator)
add_library(nlohmann_json_schema_validator ALIAS nlohmann_json_schema_validator::json-schema-validator)
message(STATUS "Aliased nlohmann_json_schema_validator::json-schema-validator -> nlohmann_json_schema_validator")
elseif(DEFINED nlohmann_json_schema_validator_LIBRARIES OR DEFINED nlohmann_json_schema_validator_INCLUDE_DIRS)
# Create imported interface target from find_package variables
add_library(nlohmann_json_schema_validator INTERFACE IMPORTED)
if(DEFINED nlohmann_json_schema_validator_LIBRARIES)
set_target_properties(nlohmann_json_schema_validator PROPERTIES
INTERFACE_LINK_LIBRARIES "${nlohmann_json_schema_validator_LIBRARIES}"
)
endif()
if(DEFINED nlohmann_json_schema_validator_INCLUDE_DIRS)
set_target_properties(nlohmann_json_schema_validator PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${nlohmann_json_schema_validator_INCLUDE_DIRS}"
)
endif()
message(STATUS "Created imported target nlohmann_json_schema_validator from find_package variables")
endif()
endif()
# Fallback: try to manually locate headers and library if package provided no target/variables
if(NOT TARGET nlohmann_json_schema_validator)
find_path(_mcp_json_schema_validator_include_dir
NAMES json-schema.hpp
PATHS /usr/include /usr/local/include
PATH_SUFFIXES nlohmann
)
find_library(_mcp_json_schema_validator_lib
NAMES nlohmann_json_schema_validator json-schema-validator
PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/local/lib64
)
if(_mcp_json_schema_validator_include_dir AND _mcp_json_schema_validator_lib)
add_library(nlohmann_json_schema_validator SHARED IMPORTED)
set_target_properties(nlohmann_json_schema_validator PROPERTIES
IMPORTED_LOCATION "${_mcp_json_schema_validator_lib}"
INTERFACE_INCLUDE_DIRECTORIES "${_mcp_json_schema_validator_include_dir}"
)
message(STATUS "Created imported target nlohmann_json_schema_validator from manual discovery: lib=${_mcp_json_schema_validator_lib}, include=${_mcp_json_schema_validator_include_dir}")
else()
message(STATUS "Manual discovery failed: include=${_mcp_json_schema_validator_include_dir}, lib=${_mcp_json_schema_validator_lib}")
endif()
endif()
if(TARGET nlohmann_json_schema_validator)
set_target_properties(nlohmann_json_schema_validator PROPERTIES
POSITION_INDEPENDENT_CODE ON
)
target_link_libraries(third_party_headers INTERFACE nlohmann_json_schema_validator)
else()
message(FATAL_ERROR "json-schema-validator target not found; tried variants, variables, and manual discovery")
endif()
find_path(_mcp_http_parser_include_dir
NAMES http_parser.h
)
find_library(_mcp_http_parser_lib
NAMES http_parser
)
if(_mcp_http_parser_include_dir AND _mcp_http_parser_lib)
message(STATUS "Using system http_parser: include=${_mcp_http_parser_include_dir}, lib=${_mcp_http_parser_lib}")
if(NOT TARGET http_parser)
add_library(http_parser STATIC IMPORTED)
set_target_properties(http_parser PROPERTIES
IMPORTED_LOCATION "${_mcp_http_parser_lib}"
INTERFACE_INCLUDE_DIRECTORIES "${_mcp_http_parser_include_dir}"
)
endif()
if(NOT TARGET http_parser_headers)
add_library(http_parser_headers INTERFACE)
target_include_directories(http_parser_headers
INTERFACE
"${_mcp_http_parser_include_dir}"
)
endif()
else()
if (NOT EXISTS "${FETCHCONTENT_BASE_DIR}/http_parser-src/http_parser.c")
message(STATUS "download http_parser")
FetchContent_Declare(
http_parser
GIT_REPOSITORY https://github.com/nodejs/http-parser.git
GIT_TAG v2.9.4
)
# Fetch http_parser
FetchContent_MakeAvailable(http_parser)
message(STATUS "finish download http_parser")
endif()
if(NOT TARGET http_parser)
add_library(http_parser
STATIC
${FETCHCONTENT_BASE_DIR}/http_parser-src/http_parser.c
)
set_target_properties(http_parser PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(http_parser
PUBLIC
${FETCHCONTENT_BASE_DIR}/http_parser-src/
)
endif()
if(NOT TARGET http_parser_headers)
add_library(http_parser_headers INTERFACE)
target_include_directories(http_parser_headers
INTERFACE
${FETCHCONTENT_BASE_DIR}/http_parser-src/
)
endif()
endif()