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>
163 lines
4.3 KiB
Markdown
163 lines
4.3 KiB
Markdown
# 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:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```bash
|
|
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:
|
|
```typescript
|
|
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:
|
|
```dockerfile
|
|
ENV IS_DOCKER=true
|
|
```
|
|
|
|
### Step 3: System-Level Unbuffering (Last Resort)
|
|
|
|
Only if Steps 1-2 fail, implement stdbuf wrapper:
|
|
|
|
**File: `docker-entrypoint.sh`**
|
|
```bash
|
|
#!/bin/sh
|
|
# Force line buffering for stdio communication
|
|
exec stdbuf -oL -eL node /app/dist/mcp/index.js
|
|
```
|
|
|
|
**File: `Dockerfile`**
|
|
```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
|
|
```bash
|
|
# 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
|
|
1. Update `claude_desktop_config.json` with new configuration
|
|
2. Restart Claude Desktop
|
|
3. Check Developer tab for "running" status
|
|
4. Test a simple MCP command
|
|
|
|
### 3. Debug if Needed
|
|
```bash
|
|
# 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 `-i` flag configuration works
|
|
|
|
## Rollout Plan
|
|
|
|
1. **Test locally** with simple `-i` flag first
|
|
2. **Update Docker image** only if code changes needed
|
|
3. **Update README** with working configuration
|
|
4. **Community announcement** with simple Docker instructions
|
|
|
|
## Key Insights from Research
|
|
|
|
- Most MCP Docker deployments work with just `-i` flag
|
|
- 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. |