From 619472fdb510d10f6b25c69124a2e06b56c2e01e Mon Sep 17 00:00:00 2001 From: Peter Kahn Date: Wed, 17 Sep 2025 09:47:06 -0400 Subject: [PATCH 1/2] DS-4858 update from internal: mcp support, drop prompt reload on app init, derive env variables when we can do so --- README.md | 3 ++- doc/mcp-proxy-setup.md | 18 ++++++++++++++++++ docker-compose.yml | 39 +++++++++++++++++++++++++++------------ env.example | 28 +++++++++++++++++++++++++--- scripts/swirl-load-job.sh | 6 ++++++ scripts/swirl-load.sh | 21 +++++++++++++++++++++ scripts/swirl-service.sh | 9 +++++---- 7 files changed, 104 insertions(+), 20 deletions(-) create mode 100644 doc/mcp-proxy-setup.md diff --git a/README.md b/README.md index 5900e71..767da6d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Please contact [hello@swirlaiconnect.com](mailto:hello@swirlaiconnect.com) for m - [PostgreSQL](doc/service-setup.md#postgresql) - [Connecting Swirl to the Enterprise](doc/service-setup.md#connecting-swirl-to-the-enterprise) - [Connecting to Microsoft IDP](doc/service-setup.md#connecting-to-microsoft-idp) - +- [Configuring MCP Proxy (optional)](doc/mcp-proxy-setup.md) ## Controlling and Monitoring Swirl Service - [Controlling Swirl Service](doc/controlling-swirl-service.md) - Start, stop, and restart the Swirl service. @@ -52,6 +52,7 @@ Please contact [hello@swirlaiconnect.com](mailto:hello@swirlaiconnect.com) for m # Downloads - [Swirl Enterprise Edition Docker Environment 4.3.0 tar file](https://github.com/swirlai/docker-compose/archive/refs/tags/v4_3_0_0.tar.gz) +- [Swirl Enterprise Edition Docker Environment 4.3.0 with MCP 1.0.5 tar file](https://github.com/swirlai/docker-compose/archive/refs/tags/v4_3_0_0_mcp_v1_0_5.tar.gz) # Additional Documentation diff --git a/doc/mcp-proxy-setup.md b/doc/mcp-proxy-setup.md new file mode 100644 index 0000000..cebcf3d --- /dev/null +++ b/doc/mcp-proxy-setup.md @@ -0,0 +1,18 @@ +# Overview +The MCP server proxy acts as a TCP server that exposes a set of tools, resources, and prompts for clients to interact with. +It handles authentication, manages client sessions, and delegates requests (such as listing tools, resources, and prompts, or +executing commands) to internal logic. It serves as a gateway for clients to access and interact with the +MCP (Multi-Component Platform) backend services over TCP. + +## Setting up MCP Proxy +* MCP_ENABLED: Set to "true" to enable the MCP proxy service. +* MCP_VERSION: Specifies the version of the MCP proxy to use (e.g., "v1_0_5"). +* MCP_PORT: The port on which the MCP proxy will listen for incoming connections (defaults "9000"). +* SWIRL_MCP_USERNAME: Required authentication username for accessing the MCP proxy. +* SWIRL_MCP_PASSWORD: Required authentication password for accessing the MCP proxy. +* SWIRL_API_USERNAME: The username for the Swirl API, used for MCP to Swirl authentication. +* SWIRL_API_PASSWORD: The password for the Swirl API, used for MCP to Swirl authentication. +* MCP_SWIRL_BASE_PATH: The base URL path for the Swirl API that MCP will interact with (e.g., "https://swirl.example.com/api"). +* MCP_TIMEOUT: Optional timeout setting for MCP operations (in seconds, defaults to "30"). + +The MCP proxy runs as a container named `swirl_mcp` and is controlled via the same systemd service as Swirl. \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 88eb149..3530603 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,6 +77,33 @@ services: timeout: 5s retries: 5 + mcp: + profiles: [ "all", "svc", "mcp" ] + image: swirlai/release-mcp-proxy-enterprise:${MCP_VERSION} + container_name: swirl_mcp + networks: [ "swirl" ] + ports: + - "${MCP_PORT}:${MCP_PORT}" # expose the MCP port + environment: + SWIRL_API_BASE: "${MCP_SWIRL_BASE_URL}" + SWIRL_API_BASE_PATH: "${MCP_SWIRL_BASE_PATH}" + SWIRL_API_PASSWORD: "${SWIRL_API_PASSWORD}" + SWIRL_API_USERNAME: "${SWIRL_API_USERNAME}" + SWIRL_MCP_SERVER_HOST: "0.0.0.0" + SWIRL_MCP_SERVER_PORT: "${MCP_PORT}" + SWIRL_MCP_SWIRL_API_TIMEOUT: "${MCP_TIMEOUT}" + SWIRL_MCP_PASSWORD: "${SWIRL_MCP_PASSWORD}" + SWIRL_MCP_USERNAME: "${SWIRL_MCP_USERNAME}" + healthcheck: + test: [ "CMD-SHELL", "/app/scripts/check-mcp-health.sh || exit 1" ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + depends_on: + swirl: + condition: service_healthy + tika: profiles: ["all", "svc", "tika"] image: swirlai/release-tika-enterprise:${TIKA_VERSION} @@ -113,18 +140,6 @@ services: networks: ["swirl"] command: ["python", "swirl.py", "migrate", "-v"] - swirl-reload: - profiles: ["all", "reload"] - image: swirlai/release-swirl-search-enterprise:${SWIRL_VERSION} - container_name: swirl_app_reload_prompts - env_file: - - .env - networks: ["swirl"] - command: ["python", "swirl.py", "reload_ai_prompts"] - depends_on: - swirl: - condition: service_healthy - swirl: profiles: ["all", "svc", "swirl"] image: swirlai/release-swirl-search-enterprise:${SWIRL_VERSION} diff --git a/env.example b/env.example index f09880c..5df1d5b 100755 --- a/env.example +++ b/env.example @@ -2,11 +2,12 @@ CERTBOT_EMAIL=admin@swirl.today # CHANGE_ME CERTBOT_VERSION= NGINX_VERSION= -POSTGRES_VERSION= -REDIS_VERSION= +POSTGRES_VERSION=16 +REDIS_VERSION=8 SWIRL_VERSION= TIKA_VERSION= TTM_VERSION= +MCP_VERSION= USE_CERT=false # CHANGE_ME USE_LOCAL_POSTGRES=true # CHANGE_ME USE_NGINX=false # CHANGE_ME @@ -14,13 +15,19 @@ USE_TLS=false # CHANGE_ME # Swirl ADMIN_USER_EMAIL="admin@swirl.today" # CHANGE_ME + +# SWIRL_FQDN will be automatically added to this list ALLOWED_HOSTS="localhost,127.0.0.1,swirl," # CHANGE_ME + AXES_CLIENT_IP_CALLABLE="" AZ_GOV_COMPATIBLE=false # CHANGE_ME CACHE_REDIS_URL="redis://redis:6379/1" CELERY_BROKER_URL="redis://redis:6379/0" CELERY_RESULT_BACKEND="redis://redis:6379/0" -CSRF_TRUSTED_ORIGINS="http://localhost:8000" # CHANGE_ME + +# If undefined, autopopulated by PROTOCOL, SWIRL_FQDN, and PROT +# CSRF_TRUSTED_ORIGINS="http://localhost:8000" # CHANGE_ME + GOOGLE_APPLICATION_CREDENTIALS="/app/secrets/google-credentials.json" # Uncomment and set the GOOGLE_AUTH_CLIENT_ID if you want to use Google authentication @@ -33,6 +40,12 @@ MICROSOFT_CLIENT_ID="" MICROSOFT_CLIENT_SECRET="" MICROSOFT_REDIRECT_URI="" +MCP_ENABLED=false +MCP_PORT="9000" +MCP_SWIRL_BASE_URL="0.0.0.0" +MCP_SWIRL_BASE_PATH="/api/swirl" +MCP_TIMEOUT="30" + # Uncomment and set the following if you want to use Microsoft authentication # MS_AUTH_CLIENT_ID="" # MS_TENANT_ID="" @@ -59,6 +72,7 @@ SQL_ENGINE="django.db.backends.postgresql" SQL_HOST="postgres" # CHANGE_ME SQL_PORT="5432" # CHANGE_ME SQL_SSLMODE="prefer" # CHANGE_ME + SWIRL_ES_VERSION="8" SWIRL_EXPLAIN="True" SWIRL_FQDN="localhost" # CHANGE_ME @@ -73,6 +87,14 @@ SWIRL_TEXT_SUMMARIZATION_URL="http://ttm:7029" TIKA_SERVER_ENDPOINT="http://tika:9998" # Secrets +## MCP Endpoint Authentication +SWIRL_MCP_USERNAME="" # CHANGE_ME +SWIRL_MCP_PASSWORD="" # CHANGE_ME +## User in Swirl to which MCP requests will be authenticated +SWIRL_API_USERNAME="" +SWIRL_API_PASSWORD="" + +## CHANGE THESE VALUES ADMIN_PASSWORD="" # CHANGE_ME GOOGLE_CREDENTIALS="" # CHANGE_ME SQL_PASSWORD="" # CHANGE_ME diff --git a/scripts/swirl-load-job.sh b/scripts/swirl-load-job.sh index df87a58..d69a75c 100755 --- a/scripts/swirl-load-job.sh +++ b/scripts/swirl-load-job.sh @@ -61,3 +61,9 @@ fi python swirl.py load_data python swirl.py reload_ai_prompts python swirl.py load_branding + +# Optionally Swirl API User if environment variables are set +if [ -n "$SWIRL_API_USERNAME" ] && [ -n "$SWIRL_API_PASSWORD" ]; then + echo "MCP Support: Creating Swirl API user: $SWIRL_API_USERNAME" + python manage.py shell -c "from django.contrib.auth.models import User, Group; user=User.objects.create_user(username='${SWIRL_API_USERNAME}', password='${SWIRL_API_PASSWORD}'); group, _ = Group.objects.get_or_create(name='swirl_auto_provisioned_group'); user.groups.add(group)" +fi diff --git a/scripts/swirl-load.sh b/scripts/swirl-load.sh index 5fbc40d..0fe3867 100755 --- a/scripts/swirl-load.sh +++ b/scripts/swirl-load.sh @@ -3,6 +3,27 @@ set -e # Exit the script immediately if any command fails echo "Setting up swirl" +# auto populating Variables +export PROTOCOL=${PROTOCOL:-http} +if [ -z "$PORT" ]; then + export CSRF_TRUSTED_ORIGINS=${CSRF_TRUSTED_ORIGINS:-$PROTOCOL://$SWIRL_FQDN} +else + export CSRF_TRUSTED_ORIGINS=${CSRF_TRUSTED_ORIGINS:-$PROTOCOL://$SWIRL_FQDN:$PORT} +fi + +# if $SWIRL_FQDN is not in ALLOWED_HOSTS, add it +if [[ -z "$ALLOWED_HOSTS" ]]; then + export ALLOWED_HOSTS=localhost,$SWIRL_FQDN +elif [[ ! "$ALLOWED_HOSTS" =~ (^|,)$SWIRL_FQDN(,|$) ]]; then + export ALLOWED_HOSTS=$ALLOWED_HOSTS,$SWIRL_FQDN +fi + +echo "SWIRL_FQDN is set to: $SWIRL_FQDN" +echo "ALLOWED_HOSTS is set to: $ALLOWED_HOSTS" +echo "CSRF_TRUSTED_ORIGINS is set to: $CSRF_TRUSTED_ORIGINS" + + + # Remove any existing swirl configuration directory rm -rf .swirl diff --git a/scripts/swirl-service.sh b/scripts/swirl-service.sh index 6916794..19cd3fc 100755 --- a/scripts/swirl-service.sh +++ b/scripts/swirl-service.sh @@ -295,11 +295,12 @@ else cp $PARENT_DIR/nginx/nginx-template.notls $PARENT_DIR/nginx/nginx.template fi +if [ "$MCP_ENABLED" == "true" ]; then + COMPOSE_PROFILES="$COMPOSE_PROFILES,mcp" +fi + ONETIME_JOB_FLAG="$PARENT_DIR/.swirl-application-setup-job-complete.flag" -if [ -f "$ONETIME_JOB_FLAG" ]; then - log "Application setup job already completed. Skipping initial setup." - COMPOSE_PROFILES="$COMPOSE_PROFILES,reload" -else +if [ ! -f "$ONETIME_JOB_FLAG" ]; then log "Setting up run one-time application setup job..." # Run the initial setup job COMPOSE_PROFILES="$COMPOSE_PROFILES,setup" From a23bcb77eb6a78199e2de80a3472befa74ef956c Mon Sep 17 00:00:00 2001 From: Peter Kahn Date: Wed, 17 Sep 2025 09:50:09 -0400 Subject: [PATCH 2/2] set for latest in example --- env.example | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/env.example b/env.example index 5df1d5b..a4ec94e 100755 --- a/env.example +++ b/env.example @@ -4,10 +4,10 @@ CERTBOT_VERSION= NGINX_VERSION= POSTGRES_VERSION=16 REDIS_VERSION=8 -SWIRL_VERSION= -TIKA_VERSION= -TTM_VERSION= -MCP_VERSION= +SWIRL_VERSION=v4_3_0_0 +TIKA_VERSION=v4_3_0_0 +TTM_VERSION=v4_3_0_0 +MCP_VERSION=v1_0_5 USE_CERT=false # CHANGE_ME USE_LOCAL_POSTGRES=true # CHANGE_ME USE_NGINX=false # CHANGE_ME