diff --git a/GNUmakefile.in b/GNUmakefile.in index d836f7ba525..751b1c7fd3d 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -37,6 +37,9 @@ ifeq ($(enable_pax), yes) endif $(MAKE) -C gpMgmt all $(MAKE) -C gpcontrib all +ifeq ($(enable_mcp_server), yes) + $(MAKE) -C mcp-server all +endif +@echo "All of Apache Cloudberry successfully made. Ready to install." docs: @@ -84,6 +87,9 @@ ifeq ($(with_openssl), yes) endif $(MAKE) -C gpMgmt $@ $(MAKE) -C gpcontrib $@ +ifeq ($(enable_mcp_server), yes) + $(MAKE) -C mcp-server $@ +endif +@echo "Apache Cloudberry installation complete." install-docs: @@ -116,6 +122,9 @@ clean: # gpAux/Makefile is the entry point for the enterprise build, which ends up # calling top-level configure and this Makefile $(MAKE) -C gpMgmt $@ +ifeq ($(enable_mcp_server), yes) + $(MAKE) -C mcp-server $@ +endif # Important: distclean `src' last, otherwise Makefile.global # will be gone too soon. @@ -125,6 +134,9 @@ distclean maintainer-clean: $(MAKE) -C gpcontrib $@ $(MAKE) -C config $@ $(MAKE) -C gpMgmt $@ +ifeq ($(enable_mcp_server), yes) + $(MAKE) -C mcp-server $@ +endif $(MAKE) -C src $@ rm -rf tmp_install/ # Garbage from autoconf: diff --git a/configure b/configure index 44d3fae95b6..e65bc2636db 100755 --- a/configure +++ b/configure @@ -744,7 +744,6 @@ with_gssapi with_pythonsrc_ext PIP3 CURL -PYTHON3 with_python with_perl with_tcl @@ -759,6 +758,9 @@ ZSTD_LIBS ZSTD_CFLAGS PROTOBUF_LIBS PROTOBUF_CFLAGS +UV +PYTHON3 +enable_mcp_server enable_preload_ic_module enable_ic_proxy enable_external_fts @@ -911,6 +913,7 @@ enable_gpcloud enable_external_fts enable_ic_proxy enable_preload_ic_module +enable_mcp_server enable_pax enable_thread_safety with_icu @@ -1633,6 +1636,7 @@ Optional Features: library) --disable-preload-ic-module disable preload interconnect module + --enable-mcp-server enable MCP server support --enable-pax enable PAX support --disable-thread-safety disable thread-safety in client libraries --enable-openssl-redirect @@ -9245,6 +9249,131 @@ fi $as_echo "checking whether to build with preload ic module ... $enable_preload_ic_module" >&6; } +# +# mcp-server +# + + +# Check whether --enable-mcp-server was given. +if test "${enable_mcp_server+set}" = set; then : + enableval=$enable_mcp_server; + case $enableval in + yes) + +$as_echo "#define ENABLE_MCP_SERVER 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --enable-mcp-server option" "$LINENO" 5 + ;; + esac + +else + enable_mcp_server=no + +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: checking whether to build with MCP server... $enable_mcp_server" >&5 +$as_echo "checking whether to build with MCP server... $enable_mcp_server" >&6; } + + +# Check for required dependencies when mcp-server is enabled +if test "$enable_mcp_server" = yes; then + # Extract the first word of "python3", so it can be a program name with args. +set dummy python3; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PYTHON3+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PYTHON3 in + [\\/]* | ?:[\\/]*) + ac_cv_path_PYTHON3="$PYTHON3" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PYTHON3="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PYTHON3=$ac_cv_path_PYTHON3 +if test -n "$PYTHON3"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON3" >&5 +$as_echo "$PYTHON3" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -z "$PYTHON3"; then + as_fn_error $? "python3 is required for --enable-mcp-server but was not found" "$LINENO" 5 + fi + + # Check for uv (Python package manager) + # Extract the first word of "uv", so it can be a program name with args. +set dummy uv; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_UV+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $UV in + [\\/]* | ?:[\\/]*) + ac_cv_path_UV="$UV" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_UV="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +UV=$ac_cv_path_UV +if test -n "$UV"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $UV" >&5 +$as_echo "$UV" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -z "$UV"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: uv not found. MCP server build will attempt to use pip as fallback." >&5 +$as_echo "$as_me: WARNING: uv not found. MCP server build will attempt to use pip as fallback." >&2;} + fi +fi + # # pax support # diff --git a/configure.ac b/configure.ac index 0d0529fc35f..ccb90852597 100644 --- a/configure.ac +++ b/configure.ac @@ -942,6 +942,30 @@ PGAC_ARG_BOOL(enable, preload-ic-module, yes, AC_MSG_RESULT([checking whether to build with preload ic module ... $enable_preload_ic_module]) AC_SUBST(enable_preload_ic_module) +# +# mcp-server +# +PGAC_ARG_BOOL(enable, mcp-server, no, + [enable MCP server support], + [AC_DEFINE(ENABLE_MCP_SERVER, 1, + [Define to 1 to build with MCP server support (--enable-mcp-server)])]) +AC_MSG_RESULT([checking whether to build with MCP server... $enable_mcp_server]) +AC_SUBST(enable_mcp_server) + +# Check for required dependencies when mcp-server is enabled +if test "$enable_mcp_server" = yes; then + AC_PATH_PROG([PYTHON3], [python3]) + if test -z "$PYTHON3"; then + AC_MSG_ERROR([python3 is required for --enable-mcp-server but was not found]) + fi + + # Check for uv (Python package manager) + AC_PATH_PROG([UV], [uv]) + if test -z "$UV"; then + AC_MSG_WARN([uv not found. MCP server build will attempt to use pip as fallback.]) + fi +fi + # # pax support # diff --git a/mcp-server/Makefile b/mcp-server/Makefile new file mode 100644 index 00000000000..bb30e97ec62 --- /dev/null +++ b/mcp-server/Makefile @@ -0,0 +1,145 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Makefile for Apache Cloudberry MCP Server +# +# This Makefile integrates the Python-based MCP server into the +# Cloudberry build system. It uses uv (preferred) or pip for +# dependency management and installation. +# + +# Get build configuration from top-level Makefile +subdir = mcp-server +top_builddir = .. +include $(top_builddir)/src/Makefile.global + +# Installation directories +# Note: Use $(prefix) directly to avoid automatic /postgresql suffix added by $(datadir) +MCP_SERVER_BIN_DIR = $(prefix)/bin + +# Python and package manager +# Note: fastmcp requires Python 3.10+ +PYTHON3 ?= python3.11 +UV ?= uv +PIP3 ?= pip3.11 + +# Determine which package manager to use +USE_UV := $(shell command -v $(UV) 2> /dev/null) + +# Build directory +BUILD_DIR = dist +VENV_DIR = .venv + +.PHONY: all build install installdirs uninstall clean distclean + +all: build + +# Build the MCP server package +build: + @echo "Building Apache Cloudberry MCP Server..." +ifdef USE_UV + @echo "Using uv for build..." + $(UV) build +else + @echo "Using pip for build..." + $(PYTHON3) -m pip install --upgrade build + $(PYTHON3) -m build +endif + @echo "MCP Server build complete." + +# Install the MCP server +install: build installdirs + @echo "Installing Apache Cloudberry MCP Server..." +ifdef USE_UV + @echo "Installing with uv..." + $(UV) pip install --system --prefix=$(DESTDIR)$(prefix) $(BUILD_DIR)/*.whl +else + @echo "Installing with pip..." + $(PIP3) install --target=$(DESTDIR)$(prefix)/lib/python$$($(PYTHON3) -c "import sys; print('%d.%d' % (sys.version_info.major, sys.version_info.minor))")/site-packages $(BUILD_DIR)/*.whl +endif + @# Create a wrapper script for easier invocation + @echo '#!/bin/sh' > '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo '# Wrapper script for Apache Cloudberry MCP Server' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo '' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo '# Detect Python version and set PYTHONPATH' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo 'PYTHON_VERSION=$$($(PYTHON3) -c "import sys; print(\"python%d.%d\" % (sys.version_info.major, sys.version_info.minor))")' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo 'GPHOME="$$(cd "$$(dirname "$$0")/.." && pwd)"' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo 'export PYTHONPATH="$$GPHOME/lib/$$PYTHON_VERSION/site-packages:$$PYTHONPATH"' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo '' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo 'exec $(PYTHON3) -m cbmcp.server "$$@"' >> '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @chmod +x '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' + @echo "MCP Server installation complete." + @echo "Installed to: $(DESTDIR)$(prefix)" + @echo "You can run it with: cloudberry-mcp-server" + +# Create installation directories +installdirs: + $(MKDIR_P) '$(DESTDIR)$(MCP_SERVER_BIN_DIR)' + +# Uninstall the MCP server +uninstall: + @echo "Uninstalling Apache Cloudberry MCP Server..." + rm -f '$(DESTDIR)$(MCP_SERVER_BIN_DIR)/cloudberry-mcp-server' +ifdef USE_UV + -$(UV) pip uninstall --system cloudberry-mcp-server 2>/dev/null || true +else + -$(PIP3) uninstall -y cloudberry-mcp-server 2>/dev/null || true +endif + @echo "MCP Server uninstalled." + +# Clean build artifacts +clean: + @echo "Cleaning MCP Server build artifacts..." + rm -rf $(BUILD_DIR) + rm -rf $(VENV_DIR) + rm -rf build + rm -rf *.egg-info + rm -rf src/*.egg-info + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + find . -type f -name '*.pyc' -delete 2>/dev/null || true + find . -type f -name '*.pyo' -delete 2>/dev/null || true + @echo "MCP Server cleaned." + +# Distclean - remove all generated files +distclean: clean + @echo "Deep cleaning MCP Server..." + rm -rf .pytest_cache + rm -rf .coverage + rm -rf htmlcov + @echo "MCP Server distclean complete." + +# Help target +help: + @echo "Apache Cloudberry MCP Server Makefile" + @echo "" + @echo "Available targets:" + @echo " all - Build the MCP server (default)" + @echo " build - Build the MCP server package" + @echo " install - Install the MCP server" + @echo " uninstall - Uninstall the MCP server" + @echo " clean - Remove build artifacts" + @echo " distclean - Remove all generated files" + @echo " help - Show this help message" + @echo "" + @echo "Configuration:" + @echo " PYTHON3 = $(PYTHON3)" +ifdef USE_UV + @echo " UV = $(UV) (using uv)" +else + @echo " PIP3 = $(PIP3) (using pip)" +endif + @echo " Install to = $(prefix)"