diff --git a/.env.example b/.env.example index 4612fa9..a6bbfee 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,7 @@ # Database Configuration # For local development: ./data/nodes.db # For Docker: /app/data/nodes.db +# Custom paths supported in v2.7.16+ (must end with .db) NODE_DB_PATH=./data/nodes.db # Logging Level (debug, info, warn, error) diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 363ee5e..0311468 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -1,39 +1,75 @@ #!/bin/sh set -e +# Helper function for safe logging (prevents stdio mode corruption) +log_message() { + [ "$MCP_MODE" != "stdio" ] && echo "$@" +} + # Environment variable validation if [ "$MCP_MODE" = "http" ] && [ -z "$AUTH_TOKEN" ] && [ -z "$AUTH_TOKEN_FILE" ]; then - echo "ERROR: AUTH_TOKEN or AUTH_TOKEN_FILE is required for HTTP mode" + log_message "ERROR: AUTH_TOKEN or AUTH_TOKEN_FILE is required for HTTP mode" >&2 exit 1 fi # Validate AUTH_TOKEN_FILE if provided if [ -n "$AUTH_TOKEN_FILE" ] && [ ! -f "$AUTH_TOKEN_FILE" ]; then - echo "ERROR: AUTH_TOKEN_FILE specified but file not found: $AUTH_TOKEN_FILE" + log_message "ERROR: AUTH_TOKEN_FILE specified but file not found: $AUTH_TOKEN_FILE" >&2 exit 1 fi +# Database path configuration - respect NODE_DB_PATH if set +if [ -n "$NODE_DB_PATH" ]; then + # Basic validation - must end with .db + case "$NODE_DB_PATH" in + *.db) ;; + *) log_message "ERROR: NODE_DB_PATH must end with .db" >&2; exit 1 ;; + esac + + # Use the path as-is (Docker paths should be absolute anyway) + DB_PATH="$NODE_DB_PATH" +else + DB_PATH="/app/data/nodes.db" +fi + +DB_DIR=$(dirname "$DB_PATH") + +# Ensure database directory exists with correct ownership +if [ ! -d "$DB_DIR" ]; then + log_message "Creating database directory: $DB_DIR" + if [ "$(id -u)" = "0" ]; then + # Create as root but immediately fix ownership + mkdir -p "$DB_DIR" && chown nodejs:nodejs "$DB_DIR" + else + mkdir -p "$DB_DIR" + fi +fi + # Database initialization with file locking to prevent race conditions -if [ ! -f "/app/data/nodes.db" ]; then - echo "Database not found. Initializing..." +if [ ! -f "$DB_PATH" ]; then + log_message "Database not found at $DB_PATH. Initializing..." # Use a lock file to prevent multiple containers from initializing simultaneously ( flock -x 200 # Double-check inside the lock - if [ ! -f "/app/data/nodes.db" ]; then - echo "Initializing database..." - cd /app && node dist/scripts/rebuild.js || { - echo "ERROR: Database initialization failed" + if [ ! -f "$DB_PATH" ]; then + log_message "Initializing database at $DB_PATH..." + cd /app && NODE_DB_PATH="$DB_PATH" node dist/scripts/rebuild.js || { + log_message "ERROR: Database initialization failed" >&2 exit 1 } fi - ) 200>/app/data/.db.lock + ) 200>"$DB_DIR/.db.lock" fi # Fix permissions if running as root (for development) if [ "$(id -u)" = "0" ]; then - echo "Running as root, fixing permissions..." - chown -R nodejs:nodejs /app/data + log_message "Running as root, fixing permissions..." + chown -R nodejs:nodejs "$DB_DIR" + # Also ensure /app/data exists for backward compatibility + if [ -d "/app/data" ]; then + chown -R nodejs:nodejs /app/data + fi # Switch to nodejs user (using Alpine's native su) exec su nodejs -c "$*" fi diff --git a/docs/DOCKER_README.md b/docs/DOCKER_README.md index d773579..d31d385 100644 --- a/docs/DOCKER_README.md +++ b/docs/DOCKER_README.md @@ -64,6 +64,7 @@ docker run -d \ | `PORT` | HTTP server port | `3000` | No | | `NODE_ENV` | Environment: `development` or `production` | `production` | No | | `LOG_LEVEL` | Logging level: `debug`, `info`, `warn`, `error` | `info` | No | +| `NODE_DB_PATH` | Custom database path (v2.7.16+) | `/app/data/nodes.db` | No | *Either `AUTH_TOKEN` or `AUTH_TOKEN_FILE` must be set for HTTP mode. If both are set, `AUTH_TOKEN` takes precedence. @@ -342,6 +343,28 @@ docker run --rm \ alpine tar xzf /backup/n8n-mcp-backup.tar.gz -C /target ``` +### Custom Database Path (v2.7.16+) + +You can specify a custom database location using `NODE_DB_PATH`: + +```bash +# Use custom path within mounted volume +docker run -d \ + --name n8n-mcp \ + -e MCP_MODE=http \ + -e AUTH_TOKEN=your-token \ + -e NODE_DB_PATH=/app/data/custom/my-nodes.db \ + -v n8n-mcp-data:/app/data \ + -p 3000:3000 \ + ghcr.io/czlonkowski/n8n-mcp:latest +``` + +**Important Notes:** +- The path must end with `.db` +- For data persistence, ensure the path is within a mounted volume +- Paths outside mounted volumes will be lost on container restart +- The directory will be created automatically if it doesn't exist + ## 🐛 Troubleshooting ### Common Issues @@ -506,4 +529,4 @@ services: --- -*Last updated: June 2025 - Docker implementation v1.0* \ No newline at end of file +*Last updated: July 2025 - Docker implementation v1.1* \ No newline at end of file diff --git a/docs/DOCKER_TROUBLESHOOTING.md b/docs/DOCKER_TROUBLESHOOTING.md index e4f60b5..d0adc23 100644 --- a/docs/DOCKER_TROUBLESHOOTING.md +++ b/docs/DOCKER_TROUBLESHOOTING.md @@ -5,6 +5,7 @@ This guide helps resolve common issues when running n8n-mcp with Docker, especia ## Table of Contents - [Common Issues](#common-issues) - [502 Bad Gateway Errors](#502-bad-gateway-errors) + - [Custom Database Path Not Working](#custom-database-path-not-working-v27160) - [Container Name Conflicts](#container-name-conflicts) - [n8n API Connection Issues](#n8n-api-connection-issues) - [Docker Networking](#docker-networking) @@ -13,6 +14,41 @@ This guide helps resolve common issues when running n8n-mcp with Docker, especia ## Common Issues +### Custom Database Path Not Working (v2.7.16+) + +**Symptoms:** +- `NODE_DB_PATH` environment variable is set but ignored +- Database always created at `/app/data/nodes.db` +- Custom path setting has no effect + +**Root Cause:** Fixed in v2.7.16. Earlier versions had hardcoded paths in docker-entrypoint.sh. + +**Solutions:** + +1. **Update to v2.7.16 or later:** +```bash +docker pull ghcr.io/czlonkowski/n8n-mcp:latest +``` + +2. **Ensure path ends with .db:** +```bash +# Correct +NODE_DB_PATH=/app/data/custom/my-nodes.db + +# Incorrect (will be rejected) +NODE_DB_PATH=/app/data/custom/my-nodes +``` + +3. **Use path within mounted volume for persistence:** +```yaml +services: + n8n-mcp: + environment: + NODE_DB_PATH: /app/data/custom/nodes.db + volumes: + - n8n-mcp-data:/app/data # Ensure parent directory is mounted +``` + ### 502 Bad Gateway Errors **Symptoms:** diff --git a/package.json b/package.json index 58d3a74..2485542 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-mcp", - "version": "2.7.15", + "version": "2.7.16", "description": "Integration between n8n workflow automation and Model Context Protocol (MCP)", "main": "dist/index.js", "bin": { diff --git a/src/scripts/rebuild.ts b/src/scripts/rebuild.ts index f4629bf..8ac6e73 100644 --- a/src/scripts/rebuild.ts +++ b/src/scripts/rebuild.ts @@ -15,7 +15,8 @@ import * as path from 'path'; async function rebuild() { console.log('🔄 Rebuilding n8n node database...\n'); - const db = await createDatabaseAdapter('./data/nodes.db'); + const dbPath = process.env.NODE_DB_PATH || './data/nodes.db'; + const db = await createDatabaseAdapter(dbPath); const loader = new N8nNodeLoader(); const parser = new NodeParser(); const mapper = new DocsMapper();