diff --git a/examples/server-deployment/docker/deploy/caddy/Caddyfile b/examples/server-deployment/docker/deploy/caddy/Caddyfile new file mode 100644 index 0000000..4438936 --- /dev/null +++ b/examples/server-deployment/docker/deploy/caddy/Caddyfile @@ -0,0 +1,51 @@ +{$DOMAIN} { + tls { + issuer acme { + disable_tlsalpn_challenge # force HTTP-01 on port 80 + # ca https://acme-staging-v02.api.letsencrypt.org/directory # optional during testing + } + } + + encode gzip + + @mcp { + path /mcp /mcp/ /mcp/* + } + handle @mcp { + # Ensure trailing slash for /mcp -> /mcp/ + uri path_regexp ^/mcp$ /mcp/ + reverse_proxy http://mcp:8001 { + # SSE/MCP streaming configuration + flush_interval -1 + header_up X-Real-IP {remote_host} + # Disable buffering for SSE + @sse { + header Accept *text/event-stream* + } + } + } + + # Serve favicon and icon files with proper caching + handle /favicon.ico { + root * /srv/site + header Cache-Control "public, max-age=86400, immutable" + header Content-Type "image/x-icon" + file_server + } + handle /favicon.png { + root * /srv/site + header Cache-Control "public, max-age=86400, immutable" + header Content-Type "image/png" + file_server + } + handle /site.webmanifest { + root * /srv/site + header Cache-Control "public, max-age=3600" + header Content-Type "application/manifest+json" + file_server + } + handle { + root * /srv/site + file_server + } +} \ No newline at end of file diff --git a/examples/server-deployment/docker/docker-compose.yml b/examples/server-deployment/docker/docker-compose.yml new file mode 100644 index 0000000..201f845 --- /dev/null +++ b/examples/server-deployment/docker/docker-compose.yml @@ -0,0 +1,56 @@ +networks: + internal: + driver: bridge + +volumes: + caddy_data: + caddy_config: + +services: + mcp: + # Option A: build from this repo's Dockerfile (assumes you are working from the repo's examples/server-deployment/docker directory) + build: + context: ../../../ + dockerfile: Dockerfile + # Option B: if you publish an image, replace with: + # image: ghcr.io/teradata/teradata-mcp-server:latest + container_name: teradata-mcp + env_file: .env + expose: + - "8001" # only visible inside the compose network + command: + - teradata-mcp-server + - --mcp_transport + - streamable-http + - --mcp_host + - 0.0.0.0 + - --mcp_port + - "8001" + healthcheck: + test: ["CMD", "python", "-c", "import socket; s = socket.socket(); s.settimeout(5); s.connect(('127.0.0.1', 8001)); s.close()"] + interval: 15s + timeout: 5s + retries: 10 + start_period: 10s + networks: + - internal + + caddy: + image: caddy:2 + container_name: caddy + depends_on: + mcp: + condition: service_healthy + environment: + # Set this to your DuckDNS (or real) domain, e.g. teradata-mcp.duckdns.org + - DOMAIN=${DOMAIN} + ports: + - "80:80" + - "443:443" + volumes: + - ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data # certs / ACME storage + - caddy_config:/config + - ./site:/srv/site:ro # optional favicon/landing page + networks: + - internal \ No newline at end of file diff --git a/examples/server-deployment/docker/site/favicon.ico b/examples/server-deployment/docker/site/favicon.ico new file mode 100644 index 0000000..541b77c Binary files /dev/null and b/examples/server-deployment/docker/site/favicon.ico differ diff --git a/examples/server-deployment/docker/site/favicon.png b/examples/server-deployment/docker/site/favicon.png new file mode 100644 index 0000000..23edea8 Binary files /dev/null and b/examples/server-deployment/docker/site/favicon.png differ diff --git a/examples/server-deployment/docker/site/index.html b/examples/server-deployment/docker/site/index.html new file mode 100644 index 0000000..df2b93d --- /dev/null +++ b/examples/server-deployment/docker/site/index.html @@ -0,0 +1,26 @@ + + + + Teradata MCP Server + + + + + + + + + + + + Teradata +

Teradata MCP Server is running

+

Secure endpoint: /mcp/

+

+ ⚙️ Need to configure your client? See the + + MCP Client Guide + . +

+ + diff --git a/examples/server-deployment/docker/site/site.webmanifest b/examples/server-deployment/docker/site/site.webmanifest new file mode 100644 index 0000000..fbf43c0 --- /dev/null +++ b/examples/server-deployment/docker/site/site.webmanifest @@ -0,0 +1,14 @@ +{ + "name": "Teradata MCP Server", + "short_name": "Teradata MCP", + "icons": [ + { + "src": "/favicon.png", + "sizes": "192x192", + "type": "image/png" + } + ], + "theme_color": "#FF6200", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/examples/server-deployment/quickstart-docker.md b/examples/server-deployment/quickstart-docker.md new file mode 100644 index 0000000..505879a --- /dev/null +++ b/examples/server-deployment/quickstart-docker.md @@ -0,0 +1,153 @@ +# Quickstart — Docker Compose (MCP + Caddy HTTPS) + +This guide shows how to run the **Teradata MCP server** behind **Caddy** with automatic **HTTPS (Let’s Encrypt)** using **Docker Compose**. It keeps port **8001** private and exposes only **80/443**. + +--- + +## 0) Prerequisites + +- Docker + Docker Compose plugin installed. +- An EC2 instance or host with public IPv4. +- A DNS record pointing to your host (e.g. `teradata-mcp.duckdns.org → `). + *(you can use a free dynamic DNS service such as DuckDNS for testing...)* +- Security Group / firewall allows inbound **TCP 80** and **TCP 443** (keep **22** for SSH). + +--- + +## 1) Folder layout + +Copy the [docker](examples/server-deployment/docker) directory outside this code directory and move into it this is the structure: + +``` +examples/server-deployment/docker/ +├─ docker-compose.yml +├─ .env +├─ Dockerfile +├─ deploy/ +│ └─ caddy/ +│ └─ Caddyfile +└─ site/ + ├─ favicon.ico # optional + ├─ favicon.png # optional + └─ index.html # optional landing page +``` + +Eg. +```bash +#If you haven't, clone this repo +git clone https://github.com//teradata-mcp-server.git + +cp -R teradata-mcp-server/examples/server-deployment/docker teradata-mcp-service +cp teradata-mcp-server/Dockerfile teradata-mcp-service +cd teradata-mcp-service +``` + + +--- + +## 2) `.env` — runtime configuration + +Update the `.env` file with your database connection string for `DATABASE_URI` and domain name for `DOMAIN`: + +```dotenv +# --- MCP server (FastMCP) --- +DATABASE_URI=teradata://USER:PASS@HOST:1025/DEFAULT_DB_SCHEMA # <-- Update here +MCP_TRANSPORT=streamable-http +MCP_HOST=0.0.0.0 +MCP_PORT=8001 + +# --- Caddy / HTTPS --- +# Set this to the DNS name that points at your server +DOMAIN=teradata-mcp.duckdns.org # <-- Update here +``` + +> Keep secrets out of command history by using `.env`. For all supported vars, see **docs/server_guide/CONFIGURATION.md**. + +--- + +## 3) (Optional) landing page / favicon + +Add files under `examples/server-deployment/docker/site/` if you want a root page or custom icon: + +- Update `favicon.ico` and `favicon.png` with your preferred icon +- Update `index.html` (minimal example) + +--- + +## 4) Start the stack + +From your directory: + +```bash +docker compose up -d --build +docker compose ps +``` + +First-time HTTPS may take ~30–90 seconds. Watch Caddy: + +```bash +docker compose logs -f caddy +``` + +--- + +## 7) Verify + +```bash +# Expect 200/401/405 from the app (not HTML) +curl -i https://${DOMAIN}/mcp/ + +# Inside the network (debug): curl from caddy to mcp +docker compose exec caddy sh -lc 'wget -qO- http://mcp:8001/mcp/ | head' +``` + +If you added a landing page: visit `https://${DOMAIN}/`. + +--- + +## 8) Use with Claude Desktop + +With HTTPS you **do not** need `--allow-http`: + +```json +{ + "mcpServers": { + "teradata_mcp_remote_https": { + "command": "npx", + "args": ["mcp-remote", "https://YOUR_DOMAIN/mcp/"] + } + } +} +``` + +Add headers if you enforce auth at the app or proxy (e.g., `--header "Authorization: Basic ${AUTH_TOKEN}"`). + +--- + +## 9) Operations + +```bash +# Update/recreate +docker compose pull && docker compose up -d --build + +# Logs +docker compose logs -f mcp +docker compose logs -f caddy + +# Stop +docker compose down +``` + +--- + +## 10) Troubleshooting + +- **TLS errors right after deploy** → DNS propagation or first ACME run; retry in a minute and check `docker compose logs -f caddy`. +- **HTML/404 served at `/mcp/`** → ensure the `@mcp` matcher appears *before* the static handlers in `Caddyfile`. +- **80/443 blocked** → open in Security Group / firewall; confirm public IPv4 resolves for `${DOMAIN}`. +- **Icon still shows DuckDNS duck** → Claude may brand by base domain (`duckdns.org`). Use your own domain to fully customize. + +--- + +**That’s it.** You now have a clean, HTTPS-protected MCP server via Docker Compose with Caddy. +-----