Compare commits
13 Commits
fix/issue-
...
v2.10.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e71c71698 | ||
|
|
df4066022f | ||
|
|
7a71c3c3f8 | ||
|
|
3bfad51519 | ||
|
|
907d3846a9 | ||
|
|
6de82cd2b9 | ||
|
|
6856add177 | ||
|
|
3eecda4bd5 | ||
|
|
1c6bff7d42 | ||
|
|
8864d6fa5c | ||
|
|
f6906d7971 | ||
|
|
7fbab3ec49 | ||
|
|
23327f5dc7 |
16
.github/workflows/docker-build-n8n.yml
vendored
16
.github/workflows/docker-build-n8n.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.n8n
|
||||
file: ./Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -82,13 +82,16 @@ jobs:
|
||||
|
||||
- name: Test Docker image
|
||||
run: |
|
||||
# Test that the image starts correctly with N8N_MODE
|
||||
docker run --rm \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e N8N_API_URL=http://localhost:5678 \
|
||||
-e N8N_API_KEY=test \
|
||||
-e MCP_AUTH_TOKEN=test \
|
||||
-e MCP_AUTH_TOKEN=test-token-minimum-32-chars-long \
|
||||
-e AUTH_TOKEN=test-token-minimum-32-chars-long \
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest \
|
||||
node dist/index.js n8n --version
|
||||
node -e "console.log('N8N_MODE:', process.env.N8N_MODE); process.exit(0);"
|
||||
|
||||
- name: Test health endpoint
|
||||
run: |
|
||||
@@ -97,9 +100,11 @@ jobs:
|
||||
--name n8n-mcp-test \
|
||||
-p 3000:3000 \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e N8N_API_URL=http://localhost:5678 \
|
||||
-e N8N_API_KEY=test \
|
||||
-e MCP_AUTH_TOKEN=test \
|
||||
-e MCP_AUTH_TOKEN=test-token-minimum-32-chars-long \
|
||||
-e AUTH_TOKEN=test-token-minimum-32-chars-long \
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
||||
|
||||
# Wait for container to start
|
||||
@@ -108,6 +113,9 @@ jobs:
|
||||
# Test health endpoint
|
||||
curl -f http://localhost:3000/health || exit 1
|
||||
|
||||
# Test MCP endpoint
|
||||
curl -f http://localhost:3000/mcp || exit 1
|
||||
|
||||
# Cleanup
|
||||
docker stop n8n-mcp-test
|
||||
docker rm n8n-mcp-test
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
# Multi-stage Dockerfile optimized for n8n integration
|
||||
# Stage 1: Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache python3 make g++ git
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install all dependencies (including dev deps for building)
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Production stage
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache \
|
||||
curl \
|
||||
tini \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Create non-root user with unpredictable UID/GID
|
||||
# Using a hash of the build time to generate unpredictable IDs
|
||||
RUN BUILD_HASH=$(date +%s | sha256sum | head -c 8) && \
|
||||
UID=$((10000 + 0x${BUILD_HASH} % 50000)) && \
|
||||
GID=$((10000 + 0x${BUILD_HASH} % 50000)) && \
|
||||
addgroup -g ${GID} n8n-mcp && \
|
||||
adduser -u ${UID} -G n8n-mcp -s /bin/sh -D n8n-mcp
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files (use runtime-only dependencies)
|
||||
COPY package.runtime.json package.json
|
||||
|
||||
# Install production dependencies only
|
||||
RUN npm install --production --no-audit --no-fund && \
|
||||
npm cache clean --force
|
||||
|
||||
# Copy built application from builder stage
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/data ./data
|
||||
|
||||
# Create necessary directories and set permissions
|
||||
RUN mkdir -p /app/logs /app/data && \
|
||||
chown -R n8n-mcp:n8n-mcp /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER n8n-mcp
|
||||
|
||||
# Set environment variables for n8n mode
|
||||
ENV NODE_ENV=production \
|
||||
N8N_MODE=true \
|
||||
N8N_API_URL="" \
|
||||
N8N_API_KEY="" \
|
||||
PORT=3000
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
||||
CMD curl -f http://localhost:${PORT}/health || exit 1
|
||||
|
||||
# Use tini for proper signal handling
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
||||
# Start the application in n8n mode
|
||||
CMD ["node", "dist/index.js", "n8n"]
|
||||
22
README.md
22
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp)
|
||||
[](https://www.npmjs.com/package/n8n-mcp)
|
||||
[](https://codecov.io/gh/czlonkowski/n8n-mcp)
|
||||
[](https://github.com/czlonkowski/n8n-mcp/actions)
|
||||
@@ -781,6 +781,26 @@ Contributions are welcome! Please:
|
||||
3. Run tests (`npm test`)
|
||||
4. Submit a pull request
|
||||
|
||||
### 🚀 For Maintainers: Automated Releases
|
||||
|
||||
This project uses automated releases triggered by version changes:
|
||||
|
||||
```bash
|
||||
# Guided release preparation
|
||||
npm run prepare:release
|
||||
|
||||
# Test release automation
|
||||
npm run test:release-automation
|
||||
```
|
||||
|
||||
The system automatically handles:
|
||||
- 🏷️ GitHub releases with changelog content
|
||||
- 📦 NPM package publishing
|
||||
- 🐳 Multi-platform Docker images
|
||||
- 📚 Documentation updates
|
||||
|
||||
See [Automated Release Guide](./docs/AUTOMATED_RELEASES.md) for complete details.
|
||||
|
||||
## 👏 Acknowledgments
|
||||
|
||||
- [n8n](https://n8n.io) team for the workflow automation platform
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
n8n-mcp:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.n8n
|
||||
dockerfile: Dockerfile # Uses standard Dockerfile with N8N_MODE=true env var
|
||||
image: ghcr.io/${GITHUB_REPOSITORY:-czlonkowski/n8n-mcp}/n8n-mcp:${VERSION:-latest}
|
||||
container_name: n8n-mcp
|
||||
restart: unless-stopped
|
||||
@@ -41,9 +41,11 @@ services:
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- N8N_MODE=true
|
||||
- MCP_MODE=http
|
||||
- N8N_API_URL=http://n8n:5678
|
||||
- N8N_API_KEY=${N8N_API_KEY}
|
||||
- MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
|
||||
- AUTH_TOKEN=${MCP_AUTH_TOKEN}
|
||||
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||
volumes:
|
||||
- ./data:/app/data:ro
|
||||
|
||||
@@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.10.1] - 2025-08-02
|
||||
|
||||
### Fixed
|
||||
- **Memory Leak in SimpleCache**: Fixed critical memory leak causing MCP server connection loss after several hours (fixes #118)
|
||||
- Added proper timer cleanup in `SimpleCache.destroy()` method
|
||||
- Updated MCP server shutdown to clean up cache timers
|
||||
- Enhanced HTTP server error handling with transport error handlers
|
||||
- Fixed event listener cleanup to prevent accumulation
|
||||
- Added comprehensive test coverage for memory leak prevention
|
||||
|
||||
## [2.10.0] - 2025-08-02
|
||||
|
||||
### Added
|
||||
- **Automated Release System**: Complete CI/CD pipeline for automated releases on version bump
|
||||
- GitHub Actions workflow (`.github/workflows/release.yml`) with 7 coordinated jobs
|
||||
- Automatic version detection and changelog extraction
|
||||
- Multi-artifact publishing: GitHub releases, NPM package, Docker images
|
||||
- Interactive release preparation tool (`npm run prepare:release`)
|
||||
- Comprehensive release testing tool (`npm run test:release-automation`)
|
||||
- Full documentation in `docs/AUTOMATED_RELEASES.md`
|
||||
- Zero-touch releases: version bump → automatic everything
|
||||
|
||||
### Security
|
||||
- **CI/CD Security Enhancements**:
|
||||
- Replaced deprecated `actions/create-release@v1` with secure `gh` CLI
|
||||
- Fixed git checkout vulnerability using safe `git show` commands
|
||||
- Fixed command injection risk using proper argument arrays
|
||||
- Added concurrency control to prevent simultaneous releases
|
||||
- Added disk space checks before resource-intensive operations
|
||||
- Implemented confirmation gates for destructive operations
|
||||
|
||||
### Changed
|
||||
- **Dockerfile Consolidation**: Removed redundant `Dockerfile.n8n` in favor of single optimized `Dockerfile`
|
||||
- n8n packages are not required at runtime for N8N_MODE functionality
|
||||
- Standard image works perfectly with `N8N_MODE=true` environment variable
|
||||
- Reduces build complexity and maintenance overhead
|
||||
- Image size reduced by 500MB+ (no unnecessary n8n packages)
|
||||
- Build time improved from 8+ minutes to 1-2 minutes
|
||||
|
||||
### Added (CI/CD Features)
|
||||
- **Developer Tools**:
|
||||
- `scripts/prepare-release.js`: Interactive guided release tool
|
||||
- `scripts/test-release-automation.js`: Validates entire release setup
|
||||
- `scripts/extract-changelog.js`: Modular changelog extraction
|
||||
- **Release Automation Features**:
|
||||
- NPM publishing with 3-retry mechanism for network resilience
|
||||
- Multi-platform Docker builds (amd64, arm64)
|
||||
- Semantic version validation and prerelease detection
|
||||
- Automatic documentation badge updates
|
||||
- Runtime-optimized NPM package (8 deps vs 50+, ~50MB vs 1GB+)
|
||||
|
||||
### Fixed
|
||||
- Fixed missing `axios` dependency in `package.runtime.json` causing Docker build failures
|
||||
|
||||
## [2.9.1] - 2025-08-02
|
||||
|
||||
### Fixed
|
||||
@@ -1028,6 +1084,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Basic n8n and MCP integration
|
||||
- Core workflow automation features
|
||||
|
||||
[2.10.1]: https://github.com/czlonkowski/n8n-mcp/compare/v2.10.0...v2.10.1
|
||||
[2.10.0]: https://github.com/czlonkowski/n8n-mcp/compare/v2.9.1...v2.10.0
|
||||
[2.9.1]: https://github.com/czlonkowski/n8n-mcp/compare/v2.9.0...v2.9.1
|
||||
[2.9.0]: https://github.com/czlonkowski/n8n-mcp/compare/v2.8.3...v2.9.0
|
||||
[2.8.3]: https://github.com/czlonkowski/n8n-mcp/compare/v2.8.2...v2.8.3
|
||||
|
||||
@@ -57,9 +57,11 @@ For development or custom testing:
|
||||
```bash
|
||||
# Set environment variables
|
||||
export N8N_MODE=true
|
||||
export MCP_MODE=http # Required for HTTP mode
|
||||
export N8N_API_URL=http://localhost:5678 # Your n8n instance URL
|
||||
export N8N_API_KEY=your-api-key-here # Your n8n API key
|
||||
export MCP_AUTH_TOKEN=test-token-minimum-32-chars-long
|
||||
export AUTH_TOKEN=test-token-minimum-32-chars-long # Same value as MCP_AUTH_TOKEN
|
||||
export PORT=3001
|
||||
|
||||
# Start the server
|
||||
@@ -71,18 +73,75 @@ npm start
|
||||
# Check health
|
||||
curl http://localhost:3001/health
|
||||
|
||||
# Check MCP protocol endpoint
|
||||
# Check MCP protocol endpoint (this is the endpoint n8n connects to)
|
||||
curl http://localhost:3001/mcp
|
||||
# Should return: {"protocolVersion":"2024-11-05"} for n8n compatibility
|
||||
```
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
| Variable | Required | Description | Example Value |
|
||||
|----------|----------|-------------|---------------|
|
||||
| `N8N_MODE` | Yes | Enables n8n integration mode | `true` |
|
||||
| `MCP_MODE` | Yes | Enables HTTP mode for n8n MCP Client | `http` |
|
||||
| `N8N_API_URL` | Yes* | URL of your n8n instance | `http://localhost:5678` |
|
||||
| `N8N_API_KEY` | Yes* | n8n API key for workflow management | `n8n_api_xxx...` |
|
||||
| `MCP_AUTH_TOKEN` | Yes | Authentication token for MCP requests | `secure-random-32-char-token` |
|
||||
| `AUTH_TOKEN` | Yes | Must match MCP_AUTH_TOKEN | `secure-random-32-char-token` |
|
||||
| `PORT` | No | Port for the HTTP server | `3000` (default) |
|
||||
| `LOG_LEVEL` | No | Logging verbosity | `info`, `debug`, `error` |
|
||||
|
||||
*Required only for workflow management features. Documentation tools work without these.
|
||||
|
||||
## Docker Build Changes (v2.9.2+)
|
||||
|
||||
Starting with version 2.9.2, we use a single optimized Dockerfile for all deployments:
|
||||
- The previous `Dockerfile.n8n` has been removed as redundant
|
||||
- N8N_MODE functionality is enabled via the `N8N_MODE=true` environment variable
|
||||
- This reduces image size by 500MB+ and improves build times from 8+ minutes to 1-2 minutes
|
||||
- All examples now use the standard `Dockerfile`
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Same Server as n8n
|
||||
|
||||
If you're running n8n-MCP on the same server as your n8n instance:
|
||||
|
||||
1. **Using Docker** (Recommended):
|
||||
### Building from Source (Recommended)
|
||||
|
||||
For the latest features and bug fixes, build from source:
|
||||
|
||||
```bash
|
||||
# Clone and build
|
||||
git clone https://github.com/czlonkowski/n8n-mcp.git
|
||||
cd n8n-mcp
|
||||
|
||||
# Build Docker image
|
||||
docker build -t n8n-mcp:latest .
|
||||
|
||||
# Create a Docker network if n8n uses one
|
||||
docker network create n8n-net
|
||||
|
||||
# Run n8n-MCP container
|
||||
docker run -d \
|
||||
--name n8n-mcp \
|
||||
--network n8n-net \
|
||||
-p 3000:3000 \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e N8N_API_URL=http://n8n:5678 \
|
||||
-e N8N_API_KEY=your-n8n-api-key \
|
||||
-e MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
|
||||
-e AUTH_TOKEN=$(openssl rand -hex 32) \
|
||||
-e LOG_LEVEL=info \
|
||||
--restart unless-stopped \
|
||||
n8n-mcp:latest
|
||||
```
|
||||
|
||||
### Using Pre-built Image (May Be Outdated)
|
||||
|
||||
⚠️ **Warning**: Pre-built images may be outdated due to CI/CD synchronization issues. Always check the [GitHub releases](https://github.com/czlonkowski/n8n-mcp/releases) for the latest version.
|
||||
|
||||
```bash
|
||||
# Create a Docker network if n8n uses one
|
||||
docker network create n8n-net
|
||||
@@ -93,15 +152,18 @@ docker run -d \
|
||||
--network n8n-net \
|
||||
-p 3000:3000 \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e N8N_API_URL=http://n8n:5678 \
|
||||
-e N8N_API_KEY=your-n8n-api-key \
|
||||
-e MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
|
||||
-e AUTH_TOKEN=$(openssl rand -hex 32) \
|
||||
-e LOG_LEVEL=info \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/czlonkowski/n8n-mcp:latest
|
||||
```
|
||||
|
||||
2. **Using systemd** (for native installation):
|
||||
### Using systemd (for native installation)
|
||||
|
||||
```bash
|
||||
# Create service file
|
||||
sudo cat > /etc/systemd/system/n8n-mcp.service << EOF
|
||||
@@ -114,9 +176,11 @@ Type=simple
|
||||
User=nodejs
|
||||
WorkingDirectory=/opt/n8n-mcp
|
||||
Environment="N8N_MODE=true"
|
||||
Environment="MCP_MODE=http"
|
||||
Environment="N8N_API_URL=http://localhost:5678"
|
||||
Environment="N8N_API_KEY=your-n8n-api-key"
|
||||
Environment="MCP_AUTH_TOKEN=your-secure-token"
|
||||
Environment="MCP_AUTH_TOKEN=your-secure-token-32-chars-min"
|
||||
Environment="AUTH_TOKEN=your-secure-token-32-chars-min"
|
||||
Environment="PORT=3000"
|
||||
ExecStart=/usr/bin/node /opt/n8n-mcp/dist/mcp/index.js
|
||||
Restart=on-failure
|
||||
@@ -134,22 +198,56 @@ sudo systemctl start n8n-mcp
|
||||
|
||||
Deploy n8n-MCP on a separate server from your n8n instance:
|
||||
|
||||
#### Quick Docker Deployment
|
||||
#### Quick Docker Deployment (Build from Source)
|
||||
|
||||
```bash
|
||||
# On your cloud server (Hetzner, AWS, DigitalOcean, etc.)
|
||||
# First, clone and build
|
||||
git clone https://github.com/czlonkowski/n8n-mcp.git
|
||||
cd n8n-mcp
|
||||
docker build -t n8n-mcp:latest .
|
||||
|
||||
# Generate auth tokens
|
||||
AUTH_TOKEN=$(openssl rand -hex 32)
|
||||
echo "Save this AUTH_TOKEN: $AUTH_TOKEN"
|
||||
|
||||
# Run the container
|
||||
docker run -d \
|
||||
--name n8n-mcp \
|
||||
-p 3000:3000 \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e N8N_API_URL=https://your-n8n-instance.com \
|
||||
-e N8N_API_KEY=your-n8n-api-key \
|
||||
-e MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
|
||||
-e MCP_AUTH_TOKEN=$AUTH_TOKEN \
|
||||
-e AUTH_TOKEN=$AUTH_TOKEN \
|
||||
-e LOG_LEVEL=info \
|
||||
--restart unless-stopped \
|
||||
n8n-mcp:latest
|
||||
```
|
||||
|
||||
#### Quick Docker Deployment (Pre-built Image)
|
||||
|
||||
⚠️ **Warning**: May be outdated. Check [releases](https://github.com/czlonkowski/n8n-mcp/releases) first.
|
||||
|
||||
```bash
|
||||
# Generate auth tokens
|
||||
AUTH_TOKEN=$(openssl rand -hex 32)
|
||||
echo "Save this AUTH_TOKEN: $AUTH_TOKEN"
|
||||
|
||||
# Run the container
|
||||
docker run -d \
|
||||
--name n8n-mcp \
|
||||
-p 3000:3000 \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e N8N_API_URL=https://your-n8n-instance.com \
|
||||
-e N8N_API_KEY=your-n8n-api-key \
|
||||
-e MCP_AUTH_TOKEN=$AUTH_TOKEN \
|
||||
-e AUTH_TOKEN=$AUTH_TOKEN \
|
||||
-e LOG_LEVEL=info \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/czlonkowski/n8n-mcp:latest
|
||||
|
||||
# Save the MCP_AUTH_TOKEN for later use!
|
||||
```
|
||||
|
||||
#### Full Production Setup (Hetzner/AWS/DigitalOcean)
|
||||
@@ -170,21 +268,32 @@ curl -fsSL https://get.docker.com | sh
|
||||
```
|
||||
|
||||
3. **Deploy n8n-MCP with SSL** (using Caddy for automatic HTTPS):
|
||||
|
||||
**Option A: Build from Source (Recommended)**
|
||||
```bash
|
||||
# Clone and prepare
|
||||
git clone https://github.com/czlonkowski/n8n-mcp.git
|
||||
cd n8n-mcp
|
||||
|
||||
# Build local image
|
||||
docker build -t n8n-mcp:latest .
|
||||
|
||||
# Create docker-compose.yml
|
||||
cat > docker-compose.yml << 'EOF'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
n8n-mcp:
|
||||
image: ghcr.io/czlonkowski/n8n-mcp:latest
|
||||
image: n8n-mcp:latest # Using locally built image
|
||||
container_name: n8n-mcp
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- N8N_MODE=true
|
||||
- MCP_MODE=http
|
||||
- N8N_API_URL=${N8N_API_URL}
|
||||
- N8N_API_KEY=${N8N_API_KEY}
|
||||
- MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
|
||||
- AUTH_TOKEN=${AUTH_TOKEN}
|
||||
- PORT=3000
|
||||
- LOG_LEVEL=info
|
||||
networks:
|
||||
@@ -212,7 +321,57 @@ volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
EOF
|
||||
```
|
||||
|
||||
**Option B: Pre-built Image (May Be Outdated)**
|
||||
```bash
|
||||
# Create docker-compose.yml
|
||||
cat > docker-compose.yml << 'EOF'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
n8n-mcp:
|
||||
image: ghcr.io/czlonkowski/n8n-mcp:latest
|
||||
container_name: n8n-mcp
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- N8N_MODE=true
|
||||
- MCP_MODE=http
|
||||
- N8N_API_URL=${N8N_API_URL}
|
||||
- N8N_API_KEY=${N8N_API_KEY}
|
||||
- MCP_AUTH_TOKEN=${MCP_AUTH_TOKEN}
|
||||
- AUTH_TOKEN=${AUTH_TOKEN}
|
||||
- PORT=3000
|
||||
- LOG_LEVEL=info
|
||||
networks:
|
||||
- web
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
container_name: caddy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
networks:
|
||||
- web
|
||||
|
||||
networks:
|
||||
web:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
EOF
|
||||
```
|
||||
|
||||
**Complete Setup (Both Options)**
|
||||
```bash
|
||||
# Create Caddyfile
|
||||
cat > Caddyfile << 'EOF'
|
||||
mcp.yourdomain.com {
|
||||
@@ -221,15 +380,17 @@ mcp.yourdomain.com {
|
||||
EOF
|
||||
|
||||
# Create .env file
|
||||
AUTH_TOKEN=$(openssl rand -hex 32)
|
||||
cat > .env << EOF
|
||||
N8N_API_URL=https://your-n8n-instance.com
|
||||
N8N_API_KEY=your-n8n-api-key-here
|
||||
MCP_AUTH_TOKEN=$(openssl rand -hex 32)
|
||||
MCP_AUTH_TOKEN=$AUTH_TOKEN
|
||||
AUTH_TOKEN=$AUTH_TOKEN
|
||||
EOF
|
||||
|
||||
# Save the MCP_AUTH_TOKEN!
|
||||
echo "Your MCP_AUTH_TOKEN is:"
|
||||
grep MCP_AUTH_TOKEN .env
|
||||
# Save the AUTH_TOKEN!
|
||||
echo "Your AUTH_TOKEN is: $AUTH_TOKEN"
|
||||
echo "Save this token - you'll need it in n8n MCP Client Tool configuration"
|
||||
|
||||
# Start services
|
||||
docker compose up -d
|
||||
@@ -258,16 +419,18 @@ docker compose up -d
|
||||
|
||||
2. **Configure the connection**:
|
||||
```
|
||||
Server URL:
|
||||
- Same server: http://localhost:3000
|
||||
- Docker network: http://n8n-mcp:3000
|
||||
- Different server: https://mcp.yourdomain.com
|
||||
Server URL (MUST include /mcp endpoint):
|
||||
- Same server: http://localhost:3000/mcp
|
||||
- Docker network: http://n8n-mcp:3000/mcp
|
||||
- Different server: https://mcp.yourdomain.com/mcp
|
||||
|
||||
Auth Token: [Your MCP_AUTH_TOKEN]
|
||||
Auth Token: [Your MCP_AUTH_TOKEN/AUTH_TOKEN value]
|
||||
|
||||
Transport: HTTP Streamable (SSE)
|
||||
```
|
||||
|
||||
⚠️ **Critical**: The Server URL must include the `/mcp` endpoint path. Without this, the connection will fail.
|
||||
|
||||
3. **Test the connection** by selecting a simple tool like `list_nodes`
|
||||
|
||||
### Available Tools
|
||||
@@ -324,70 +487,255 @@ You are an n8n workflow expert. Use the MCP tools to:
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Configuration Issues
|
||||
|
||||
**Missing `MCP_MODE=http` Environment Variable**
|
||||
- **Symptom**: n8n MCP Client Tool cannot connect, server doesn't respond on `/mcp` endpoint
|
||||
- **Solution**: Add `MCP_MODE=http` to your environment variables
|
||||
- **Why**: Without this, the server runs in stdio mode which is incompatible with n8n
|
||||
|
||||
**Server URL Missing `/mcp` Endpoint**
|
||||
- **Symptom**: "Connection refused" or "Invalid response" in n8n MCP Client Tool
|
||||
- **Solution**: Ensure your Server URL includes `/mcp` (e.g., `http://localhost:3000/mcp`)
|
||||
- **Why**: n8n connects to the `/mcp` endpoint specifically, not the root URL
|
||||
|
||||
**Mismatched Auth Tokens**
|
||||
- **Symptom**: "Authentication failed" or "Invalid auth token"
|
||||
- **Solution**: Ensure both `MCP_AUTH_TOKEN` and `AUTH_TOKEN` have the same value
|
||||
- **Why**: Both variables must match for proper authentication
|
||||
|
||||
### Connection Issues
|
||||
|
||||
**"Connection refused" in n8n MCP Client Tool**
|
||||
- Check n8n-MCP is running: `docker ps` or `systemctl status n8n-mcp`
|
||||
- Verify port is accessible: `curl http://your-server:3000/health`
|
||||
- Check firewall rules allow port 3000
|
||||
1. **Check n8n-MCP is running**:
|
||||
```bash
|
||||
# Docker
|
||||
docker ps | grep n8n-mcp
|
||||
docker logs n8n-mcp --tail 20
|
||||
|
||||
**"Invalid auth token"**
|
||||
- Ensure MCP_AUTH_TOKEN matches exactly (no extra spaces)
|
||||
- Token must be at least 32 characters long
|
||||
- Check for special characters that might need escaping
|
||||
# Systemd
|
||||
systemctl status n8n-mcp
|
||||
journalctl -u n8n-mcp --tail 20
|
||||
```
|
||||
|
||||
2. **Verify endpoints are accessible**:
|
||||
```bash
|
||||
# Health check (should return status info)
|
||||
curl http://your-server:3000/health
|
||||
|
||||
# MCP endpoint (should return protocol version)
|
||||
curl http://your-server:3000/mcp
|
||||
```
|
||||
|
||||
3. **Check firewall and networking**:
|
||||
```bash
|
||||
# Test port accessibility from n8n server
|
||||
telnet your-mcp-server 3000
|
||||
|
||||
# Check firewall rules (Ubuntu/Debian)
|
||||
sudo ufw status
|
||||
|
||||
# Check if port is bound correctly
|
||||
netstat -tlnp | grep :3000
|
||||
```
|
||||
|
||||
**"Invalid auth token" or "Authentication failed"**
|
||||
1. **Verify token format**:
|
||||
```bash
|
||||
# Check token length (should be 64 chars for hex-32)
|
||||
echo $MCP_AUTH_TOKEN | wc -c
|
||||
|
||||
# Verify both tokens match
|
||||
echo "MCP_AUTH_TOKEN: $MCP_AUTH_TOKEN"
|
||||
echo "AUTH_TOKEN: $AUTH_TOKEN"
|
||||
```
|
||||
|
||||
2. **Common token issues**:
|
||||
- Token too short (minimum 32 characters)
|
||||
- Extra whitespace or newlines in token
|
||||
- Different values for `MCP_AUTH_TOKEN` and `AUTH_TOKEN`
|
||||
- Special characters not properly escaped in environment files
|
||||
|
||||
**"Cannot connect to n8n API"**
|
||||
- Verify N8N_API_URL is correct (include http:// or https://)
|
||||
- Check n8n API key is valid and has necessary permissions
|
||||
- Ensure n8n instance is accessible from n8n-MCP server
|
||||
1. **Verify n8n configuration**:
|
||||
```bash
|
||||
# Test n8n API accessibility
|
||||
curl -H "X-N8N-API-KEY: your-api-key" \
|
||||
https://your-n8n-instance.com/api/v1/workflows
|
||||
```
|
||||
|
||||
### Protocol Issues
|
||||
2. **Common n8n API issues**:
|
||||
- `N8N_API_URL` missing protocol (http:// or https://)
|
||||
- n8n API key expired or invalid
|
||||
- n8n instance not accessible from n8n-MCP server
|
||||
- n8n API disabled in settings
|
||||
|
||||
### Version Compatibility Issues
|
||||
|
||||
**"Outdated Docker Image"**
|
||||
- **Symptom**: Missing features, old bugs, or compatibility issues
|
||||
- **Solution**: Build from source instead of using pre-built images
|
||||
- **Check**: Compare your image version with [GitHub releases](https://github.com/czlonkowski/n8n-mcp/releases)
|
||||
|
||||
**"Protocol version mismatch"**
|
||||
- n8n-MCP automatically uses version 2024-11-05 for n8n
|
||||
- n8n-MCP automatically uses version 2024-11-05 for n8n compatibility
|
||||
- Update to latest n8n-MCP version if issues persist
|
||||
- Check `/mcp` endpoint returns correct version
|
||||
- Verify `/mcp` endpoint returns correct version
|
||||
|
||||
**"Schema validation errors"**
|
||||
- Known issue with n8n's nested output handling
|
||||
- n8n-MCP includes workarounds
|
||||
- Enable debug mode to see detailed errors
|
||||
### Environment Variable Issues
|
||||
|
||||
### Debugging
|
||||
|
||||
1. **Enable debug mode**:
|
||||
**Complete Environment Variable Checklist**:
|
||||
```bash
|
||||
# Required for all deployments
|
||||
export N8N_MODE=true # Enables n8n integration
|
||||
export MCP_MODE=http # Enables HTTP mode for n8n
|
||||
export MCP_AUTH_TOKEN=your-secure-32-char-token # Auth token
|
||||
export AUTH_TOKEN=your-secure-32-char-token # Same value as MCP_AUTH_TOKEN
|
||||
|
||||
# Required for workflow management features
|
||||
export N8N_API_URL=https://your-n8n-instance.com # Your n8n URL
|
||||
export N8N_API_KEY=your-n8n-api-key # Your n8n API key
|
||||
|
||||
# Optional
|
||||
export PORT=3000 # HTTP port (default: 3000)
|
||||
export LOG_LEVEL=info # Logging level
|
||||
```
|
||||
|
||||
### Docker-Specific Issues
|
||||
|
||||
**Container Build Failures**
|
||||
```bash
|
||||
# Clear Docker cache and rebuild
|
||||
docker system prune -f
|
||||
docker build --no-cache -t n8n-mcp:latest .
|
||||
```
|
||||
|
||||
**Container Runtime Issues**
|
||||
```bash
|
||||
# Check container logs for detailed errors
|
||||
docker logs n8n-mcp -f --timestamps
|
||||
|
||||
# Inspect container environment
|
||||
docker exec n8n-mcp env | grep -E "(N8N|MCP|AUTH)"
|
||||
|
||||
# Test container connectivity
|
||||
docker exec n8n-mcp curl -f http://localhost:3000/health
|
||||
```
|
||||
|
||||
### Network and SSL Issues
|
||||
|
||||
**HTTPS/SSL Problems**
|
||||
```bash
|
||||
# Test SSL certificate
|
||||
openssl s_client -connect mcp.yourdomain.com:443
|
||||
|
||||
# Check Caddy logs
|
||||
docker logs caddy -f --tail 50
|
||||
```
|
||||
|
||||
**Docker Network Issues**
|
||||
```bash
|
||||
# Check if containers can communicate
|
||||
docker network ls
|
||||
docker network inspect bridge
|
||||
|
||||
# Test inter-container connectivity
|
||||
docker exec n8n curl http://n8n-mcp:3000/health
|
||||
```
|
||||
|
||||
### Debugging Steps
|
||||
|
||||
1. **Enable comprehensive logging**:
|
||||
```bash
|
||||
# For Docker
|
||||
docker run -d \
|
||||
--name n8n-mcp \
|
||||
-e DEBUG_MCP=true \
|
||||
-e LOG_LEVEL=debug \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
# ... other settings
|
||||
|
||||
# For systemd, add to service file:
|
||||
Environment="DEBUG_MCP=true"
|
||||
Environment="LOG_LEVEL=debug"
|
||||
```
|
||||
|
||||
2. **Check logs**:
|
||||
2. **Test all endpoints systematically**:
|
||||
```bash
|
||||
# Docker
|
||||
docker logs n8n-mcp -f --tail 100
|
||||
# 1. Health check (basic server functionality)
|
||||
curl -v http://localhost:3000/health
|
||||
|
||||
# Systemd
|
||||
journalctl -u n8n-mcp -f
|
||||
```
|
||||
# 2. MCP protocol endpoint (what n8n connects to)
|
||||
curl -v http://localhost:3000/mcp
|
||||
|
||||
3. **Test endpoints**:
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:3000/health
|
||||
|
||||
# Protocol version
|
||||
curl http://localhost:3000/mcp
|
||||
|
||||
# List tools (requires auth)
|
||||
curl -X POST http://localhost:3000 \
|
||||
-H "Authorization: Bearer YOUR_MCP_AUTH_TOKEN" \
|
||||
# 3. Test authentication (if working, returns tools list)
|
||||
curl -X POST http://localhost:3000/mcp \
|
||||
-H "Authorization: Bearer YOUR_AUTH_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
|
||||
|
||||
# 4. Test a simple tool (documentation only, no n8n API needed)
|
||||
curl -X POST http://localhost:3000/mcp \
|
||||
-H "Authorization: Bearer YOUR_AUTH_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_database_statistics","arguments":{}},"id":2}'
|
||||
```
|
||||
|
||||
3. **Common log patterns to look for**:
|
||||
```bash
|
||||
# Success patterns
|
||||
grep "Server started" /var/log/n8n-mcp.log
|
||||
grep "Protocol version" /var/log/n8n-mcp.log
|
||||
|
||||
# Error patterns
|
||||
grep -i "error\|failed\|invalid" /var/log/n8n-mcp.log
|
||||
grep -i "auth\|token" /var/log/n8n-mcp.log
|
||||
grep -i "connection\|network" /var/log/n8n-mcp.log
|
||||
```
|
||||
|
||||
### Getting Help
|
||||
|
||||
If you're still experiencing issues:
|
||||
|
||||
1. **Gather diagnostic information**:
|
||||
```bash
|
||||
# System info
|
||||
docker --version
|
||||
docker-compose --version
|
||||
uname -a
|
||||
|
||||
# n8n-MCP version
|
||||
docker exec n8n-mcp node dist/index.js --version
|
||||
|
||||
# Environment check
|
||||
docker exec n8n-mcp env | grep -E "(N8N|MCP|AUTH)" | sort
|
||||
|
||||
# Container status
|
||||
docker ps | grep n8n-mcp
|
||||
docker stats n8n-mcp --no-stream
|
||||
```
|
||||
|
||||
2. **Create a minimal test setup**:
|
||||
```bash
|
||||
# Test with minimal configuration
|
||||
docker run -d \
|
||||
--name n8n-mcp-test \
|
||||
-p 3001:3000 \
|
||||
-e N8N_MODE=true \
|
||||
-e MCP_MODE=http \
|
||||
-e MCP_AUTH_TOKEN=test-token-minimum-32-chars-long \
|
||||
-e AUTH_TOKEN=test-token-minimum-32-chars-long \
|
||||
-e LOG_LEVEL=debug \
|
||||
n8n-mcp:latest
|
||||
|
||||
# Test basic functionality
|
||||
curl http://localhost:3001/health
|
||||
curl http://localhost:3001/mcp
|
||||
```
|
||||
|
||||
3. **Report issues**: Include the diagnostic information when opening an issue on [GitHub](https://github.com/czlonkowski/n8n-mcp/issues)
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- **Minimal deployment**: 1 vCPU, 1GB RAM is sufficient
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "n8n-mcp",
|
||||
"version": "2.9.1",
|
||||
"version": "2.10.1",
|
||||
"description": "Integration between n8n workflow automation and Model Context Protocol (MCP)",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
@@ -77,7 +77,9 @@
|
||||
"sync:runtime-version": "node scripts/sync-runtime-version.js",
|
||||
"update:readme-version": "node scripts/update-readme-version.js",
|
||||
"prepare:publish": "./scripts/publish-npm.sh",
|
||||
"update:all": "./scripts/update-and-publish-prep.sh"
|
||||
"update:all": "./scripts/update-and-publish-prep.sh",
|
||||
"test:release-automation": "node scripts/test-release-automation.js",
|
||||
"prepare:release": "node scripts/prepare-release.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
{
|
||||
"name": "n8n-mcp-runtime",
|
||||
"version": "2.9.1",
|
||||
"version": "2.10.1",
|
||||
"description": "n8n MCP Server Runtime Dependencies Only",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.13.2",
|
||||
"better-sqlite3": "^11.10.0",
|
||||
"sql.js": "^1.13.0",
|
||||
"express": "^5.1.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"axios": "^1.7.2",
|
||||
"zod": "^3.23.8",
|
||||
"uuid": "^10.0.0"
|
||||
"sql.js": "^1.13.0",
|
||||
"uuid": "^10.0.0",
|
||||
"axios": "^1.7.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
|
||||
@@ -369,7 +369,7 @@ export class SingleSessionHTTPServer {
|
||||
}
|
||||
});
|
||||
|
||||
// Set up cleanup handler
|
||||
// Set up cleanup handlers
|
||||
transport.onclose = () => {
|
||||
const sid = transport.sessionId;
|
||||
if (sid) {
|
||||
@@ -378,6 +378,17 @@ export class SingleSessionHTTPServer {
|
||||
}
|
||||
};
|
||||
|
||||
// Handle transport errors to prevent connection drops
|
||||
transport.onerror = (error: Error) => {
|
||||
const sid = transport.sessionId;
|
||||
logger.error('Transport error', { sessionId: sid, error: error.message });
|
||||
if (sid) {
|
||||
this.removeSession(sid, 'transport_error').catch(err => {
|
||||
logger.error('Error during transport error cleanup', { error: err });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Connect the server to the transport BEFORE handling the request
|
||||
logger.info('handleRequest: Connecting server to new transport');
|
||||
await server.connect(transport);
|
||||
@@ -873,7 +884,7 @@ export class SingleSessionHTTPServer {
|
||||
const sessionId = req.headers['mcp-session-id'] as string | undefined;
|
||||
// Only add event listener if the request object supports it (not in test mocks)
|
||||
if (typeof req.on === 'function') {
|
||||
req.on('close', () => {
|
||||
const closeHandler = () => {
|
||||
if (!res.headersSent && sessionId) {
|
||||
logger.info('Connection closed before response sent', { sessionId });
|
||||
// Schedule immediate cleanup if connection closes unexpectedly
|
||||
@@ -883,11 +894,20 @@ export class SingleSessionHTTPServer {
|
||||
const timeSinceAccess = Date.now() - metadata.lastAccess.getTime();
|
||||
// Only remove if it's been inactive for a bit to avoid race conditions
|
||||
if (timeSinceAccess > 60000) { // 1 minute
|
||||
this.removeSession(sessionId, 'connection_closed');
|
||||
this.removeSession(sessionId, 'connection_closed').catch(err => {
|
||||
logger.error('Error during connection close cleanup', { error: err });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
req.on('close', closeHandler);
|
||||
|
||||
// Clean up event listener when response ends to prevent memory leaks
|
||||
res.on('finish', () => {
|
||||
req.removeListener('close', closeHandler);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2538,6 +2538,16 @@ Full documentation is being prepared. For now, use get_node_essentials for confi
|
||||
async shutdown(): Promise<void> {
|
||||
logger.info('Shutting down MCP server...');
|
||||
|
||||
// Clean up cache timers to prevent memory leaks
|
||||
if (this.cache) {
|
||||
try {
|
||||
this.cache.destroy();
|
||||
logger.info('Cache timers cleaned up');
|
||||
} catch (error) {
|
||||
logger.error('Error cleaning up cache:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Close database connection if it exists
|
||||
if (this.db) {
|
||||
try {
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
*/
|
||||
export class SimpleCache {
|
||||
private cache = new Map<string, { data: any; expires: number }>();
|
||||
private cleanupTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor() {
|
||||
// Clean up expired entries every minute
|
||||
setInterval(() => {
|
||||
this.cleanupTimer = setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const [key, item] of this.cache.entries()) {
|
||||
if (item.expires < now) this.cache.delete(key);
|
||||
@@ -34,4 +35,16 @@ export class SimpleCache {
|
||||
clear(): void {
|
||||
this.cache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the cache and stop the cleanup timer
|
||||
* Essential for preventing memory leaks in long-running servers
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.cleanupTimer) {
|
||||
clearInterval(this.cleanupTimer);
|
||||
this.cleanupTimer = null;
|
||||
}
|
||||
this.cache.clear();
|
||||
}
|
||||
}
|
||||
123
tests/unit/utils/simple-cache-memory-leak-fix.test.ts
Normal file
123
tests/unit/utils/simple-cache-memory-leak-fix.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { SimpleCache } from '../../../src/utils/simple-cache';
|
||||
|
||||
describe('SimpleCache Memory Leak Fix', () => {
|
||||
let cache: SimpleCache;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (cache && typeof cache.destroy === 'function') {
|
||||
cache.destroy();
|
||||
}
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should track cleanup timer', () => {
|
||||
cache = new SimpleCache();
|
||||
// Access private property for testing
|
||||
expect((cache as any).cleanupTimer).toBeDefined();
|
||||
expect((cache as any).cleanupTimer).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should clear timer on destroy', () => {
|
||||
cache = new SimpleCache();
|
||||
const timer = (cache as any).cleanupTimer;
|
||||
|
||||
cache.destroy();
|
||||
|
||||
expect((cache as any).cleanupTimer).toBeNull();
|
||||
// Verify timer was cleared
|
||||
expect(() => clearInterval(timer)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should clear cache on destroy', () => {
|
||||
cache = new SimpleCache();
|
||||
cache.set('test-key', 'test-value', 300);
|
||||
|
||||
expect(cache.get('test-key')).toBe('test-value');
|
||||
|
||||
cache.destroy();
|
||||
|
||||
expect(cache.get('test-key')).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle multiple destroy calls safely', () => {
|
||||
cache = new SimpleCache();
|
||||
|
||||
expect(() => {
|
||||
cache.destroy();
|
||||
cache.destroy();
|
||||
cache.destroy();
|
||||
}).not.toThrow();
|
||||
|
||||
expect((cache as any).cleanupTimer).toBeNull();
|
||||
});
|
||||
|
||||
it('should not create new timers after destroy', () => {
|
||||
cache = new SimpleCache();
|
||||
const originalTimer = (cache as any).cleanupTimer;
|
||||
|
||||
cache.destroy();
|
||||
|
||||
// Try to use the cache after destroy
|
||||
cache.set('key', 'value');
|
||||
cache.get('key');
|
||||
cache.clear();
|
||||
|
||||
// Timer should still be null
|
||||
expect((cache as any).cleanupTimer).toBeNull();
|
||||
expect((cache as any).cleanupTimer).not.toBe(originalTimer);
|
||||
});
|
||||
|
||||
it('should clean up expired entries periodically', () => {
|
||||
cache = new SimpleCache();
|
||||
|
||||
// Set items with different TTLs
|
||||
cache.set('short', 'value1', 1); // 1 second
|
||||
cache.set('long', 'value2', 300); // 300 seconds
|
||||
|
||||
// Advance time by 2 seconds
|
||||
vi.advanceTimersByTime(2000);
|
||||
|
||||
// Advance time to trigger cleanup (60 seconds)
|
||||
vi.advanceTimersByTime(58000);
|
||||
|
||||
// Short-lived item should be gone
|
||||
expect(cache.get('short')).toBeNull();
|
||||
// Long-lived item should still exist
|
||||
expect(cache.get('long')).toBe('value2');
|
||||
});
|
||||
|
||||
it('should prevent memory leak by clearing timer', () => {
|
||||
const timers: NodeJS.Timeout[] = [];
|
||||
const originalSetInterval = global.setInterval;
|
||||
|
||||
// Mock setInterval to track created timers
|
||||
global.setInterval = vi.fn((callback, delay) => {
|
||||
const timer = originalSetInterval(callback, delay);
|
||||
timers.push(timer);
|
||||
return timer;
|
||||
});
|
||||
|
||||
// Create and destroy multiple caches
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const tempCache = new SimpleCache();
|
||||
tempCache.set(`key${i}`, `value${i}`);
|
||||
tempCache.destroy();
|
||||
}
|
||||
|
||||
// All timers should have been cleared
|
||||
expect(timers.length).toBe(5);
|
||||
|
||||
// Restore original setInterval
|
||||
global.setInterval = originalSetInterval;
|
||||
});
|
||||
|
||||
it('should have destroy method defined', () => {
|
||||
cache = new SimpleCache();
|
||||
expect(typeof cache.destroy).toBe('function');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user