Fixed the initialization timeout issue with minimal changes: 1. Added stdout flush after server connection to combat Docker buffering 2. Fixed docker-entrypoint.sh to not output to stdout in stdio mode 3. Added process.stdin.resume() to keep server alive 4. Added IS_DOCKER environment variable for future use 5. Updated README to prioritize Docker with correct -i flag configuration The core issue was Docker's block buffering preventing immediate JSON-RPC responses. The -i flag maintains stdin connection, and explicit flushing ensures responses reach Claude Desktop immediately. Also fixed "Shutting down..." message that was breaking JSON-RPC protocol by redirecting it to stderr in stdio mode. Docker is now the recommended installation method as originally intended. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
4.3 KiB
Docker stdio Fix Implementation Plan for n8n-MCP
Based on community research and successful MCP Docker deployments, here's a streamlined fix for the initialization timeout issue.
Root Cause
Docker treats container stdout as a pipe (not TTY), causing block buffering. The MCP server's JSON-RPC responses sit in the buffer instead of being immediately sent to Claude Desktop, causing a 60-second timeout.
Implementation Steps
Step 1: Test Simple Interactive Mode
First, verify if just using -i flag solves the issue:
Update README.md:
{
"mcpServers": {
"n8n-mcp": {
"command": "docker",
"args": [
"run",
"-i", // Interactive mode - keeps stdin open
"--rm",
"-e", "MCP_MODE=stdio",
"-e", "LOG_LEVEL=error",
"-e", "DISABLE_CONSOLE_OUTPUT=true",
"ghcr.io/czlonkowski/n8n-mcp:latest"
]
}
}
}
Test command:
echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05"},"id":1}' | \
docker run -i --rm \
-e MCP_MODE=stdio \
-e LOG_LEVEL=error \
-e DISABLE_CONSOLE_OUTPUT=true \
ghcr.io/czlonkowski/n8n-mcp:latest
Expected: Should receive a JSON response immediately.
Step 2: Add Explicit Stdout Flushing (If Needed)
If Step 1 doesn't work, add minimal flushing to the Node.js server:
File: src/mcp/server-update.ts
Update the run() method:
async run(): Promise<void> {
await this.ensureInitialized();
const transport = new StdioServerTransport();
await this.server.connect(transport);
// Ensure stdout is not buffered in Docker
if (!process.stdout.isTTY && process.env.IS_DOCKER) {
// Force unbuffered stdout
process.stdout.write('');
}
logger.info('n8n Documentation MCP Server running on stdio transport');
// Keep process alive
process.stdin.resume();
}
File: Dockerfile
Add environment variable:
ENV IS_DOCKER=true
Step 3: System-Level Unbuffering (Last Resort)
Only if Steps 1-2 fail, implement stdbuf wrapper:
File: docker-entrypoint.sh
#!/bin/sh
# Force line buffering for stdio communication
exec stdbuf -oL -eL node /app/dist/mcp/index.js
File: Dockerfile
# Add stdbuf utility
RUN apk add --no-cache coreutils
# Copy and setup entrypoint
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
Testing Protocol
1. Local Docker Test
# Build test image
docker build -t n8n-mcp:test .
# Test with echo pipe
echo '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05"},"id":1}' | \
docker run -i --rm n8n-mcp:test | head -1
# Should see immediate JSON response
2. Claude Desktop Test
- Update
claude_desktop_config.jsonwith new configuration - Restart Claude Desktop
- Check Developer tab for "running" status
- Test a simple MCP command
3. Debug if Needed
# Run with stderr output for debugging
docker run -i --rm \
-e MCP_MODE=stdio \
-e LOG_LEVEL=debug \
ghcr.io/czlonkowski/n8n-mcp:latest 2>debug.log
Success Criteria
- No timeout errors in Claude Desktop logs
- MCP tools are accessible immediately
- No "Shutting down..." messages in stdout
- Simple
-iflag configuration works
Rollout Plan
- Test locally with simple
-iflag first - Update Docker image only if code changes needed
- Update README with working configuration
- Community announcement with simple Docker instructions
Key Insights from Research
- Most MCP Docker deployments work with just
-iflag - Complex solutions often unnecessary
- Node.js typically doesn't need explicit unbuffering (unlike Python with
-u) - Claude Desktop only supports stdio for local servers (not HTTP)
- Proper testing can quickly identify if buffering is the actual issue
What NOT to Do
- Don't add TTY flag (
-t) - it's for terminal UI, not needed for MCP - Don't implement complex multi-phase solutions
- Don't switch to HTTP transport (Claude Desktop doesn't support it locally)
- Don't modify MCP protocol handling
- Don't add unnecessary wrapper scripts unless proven necessary
The solution should be as simple as possible - likely just the -i flag in the Docker command.