Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:00:29 +08:00
commit 3d376a9cd7
18 changed files with 4926 additions and 0 deletions

View File

@@ -0,0 +1,641 @@
# Configuration Patterns - Complete Reference
**When to use this reference:** Adding new MCP servers to MCPProxy or fixing configuration issues.
This reference provides complete configuration structures for all server types with detailed explanations.
## Configuration File Location
`~/.mcpproxy/mcp_config.json`
**Important:** After editing, trigger reload:
```bash
touch ~/.mcpproxy/mcp_config.json
# OR restart mcpproxy
pkill mcpproxy && open /Applications/mcpproxy.app
```
## Complete Configuration Patterns
### Pattern 1: uvx/npx Servers (Package Managers)
**Key rule:** Package name MUST be first argument.
```json
{
"mcpServers": {
"server-name": {
"protocol": "stdio",
"command": "uvx", // or "npx"
"args": [
"package-name", // CRITICAL: Package name FIRST
"--arg1", // Then package arguments
"value1",
"--arg2",
"value2"
],
"env": {
// Optional environment variables
"API_KEY": "secret",
"DEBUG": "true"
},
"working_dir": "/path/to/dir", // Optional working directory
"enabled": true,
"quarantined": false
}
}
}
```
**Real examples:**
#### Time Server with Timezone
```json
{
"time": {
"protocol": "stdio",
"command": "uvx",
"args": [
"mcp-server-time",
"--local-timezone",
"America/New_York"
],
"enabled": true,
"quarantined": false
}
}
```
#### SQLite Server with Database Path
```json
{
"sqlite": {
"protocol": "stdio",
"command": "uvx",
"args": [
"mcp-server-sqlite",
"--db-path",
"/Users/username/data/mydb.sqlite"
],
"enabled": true,
"quarantined": false
}
}
```
#### Filesystem Server with Paths
```json
{
"filesystem": {
"protocol": "stdio",
"command": "npx",
"args": [
"@modelcontextprotocol/server-filesystem",
"/Users/username/workspace",
"/Users/username/Documents"
],
"enabled": true,
"quarantined": false
}
}
```
**Common mistakes:**
```json
// ❌ WRONG - Arguments before package name
{
"command": "uvx",
"args": ["--local-timezone", "America/New_York"]
// Error: "unexpected argument '--local-timezone' found"
}
// ✅ CORRECT - Package name first
{
"command": "uvx",
"args": ["mcp-server-time", "--local-timezone", "America/New_York"]
}
```
### Pattern 2: Docker-Based Servers
**Key rules:**
1. Use `-i` (NOT `-it`) for stdin pipe
2. ALWAYS disable isolation to prevent Docker-in-Docker
```json
{
"docker-server": {
"protocol": "stdio",
"command": "docker",
"args": [
"run",
"-i", // CRITICAL: -i only, not -it
"--rm", // Clean up container after exit
"-e", "VAR_NAME", // Pass environment variables
"image:tag", // Docker image
"subcommand" // Optional: server entrypoint
],
"env": {
"VAR_NAME": "value" // Values for -e flags
},
"isolation": {
"enabled": false // CRITICAL: Disable to prevent Docker-in-Docker
},
"enabled": true,
"quarantined": false
}
}
```
**Real examples:**
#### Basic Docker MCP Server
```json
{
"docker-mcp": {
"protocol": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/example/mcp-server:latest"
],
"isolation": {
"enabled": false
},
"enabled": true,
"quarantined": false
}
}
```
#### Docker Server with Environment Variables
```json
{
"api-server": {
"protocol": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "API_KEY",
"-e", "API_URL",
"mycompany/api-mcp:v1.0"
],
"env": {
"API_KEY": "sk-xxxx",
"API_URL": "https://api.example.com"
},
"isolation": {
"enabled": false
},
"enabled": true,
"quarantined": false
}
}
```
#### Docker Server with Volume Mounts
```json
{
"file-processor": {
"protocol": "stdio",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v", "/Users/username/data:/data:ro",
"mycompany/file-mcp:latest",
"--data-dir", "/data"
],
"isolation": {
"enabled": false
},
"enabled": true,
"quarantined": false
}
}
```
**Common mistakes:**
```json
// ❌ WRONG - Using -it flags
{
"command": "docker",
"args": ["run", "-it", "--rm", "image:tag"]
// Error: "the input device is not a TTY"
}
// ❌ WRONG - Isolation enabled
{
"command": "docker",
"args": ["run", "-i", "--rm", "image:tag"],
"isolation": {"enabled": true}
// Error: Docker-in-Docker, "not a TTY"
}
// ✅ CORRECT - Use -i only, disable isolation
{
"command": "docker",
"args": ["run", "-i", "--rm", "image:tag"],
"isolation": {"enabled": false}
}
```
### Pattern 3: HTTP/SSE Servers
For servers accessed via HTTP or Server-Sent Events.
```json
{
"http-server": {
"protocol": "http", // or "sse"
"url": "https://api.example.com/mcp",
"headers": {
// Optional authentication headers
"Authorization": "Bearer token",
"X-API-Key": "secret"
},
"enabled": true,
"quarantined": false
}
}
```
**Real examples:**
#### HTTP Server with Bearer Token
```json
{
"remote-api": {
"protocol": "http",
"url": "https://mcp.example.com/v1",
"headers": {
"Authorization": "Bearer sk-xxxxxxxxxxxx"
},
"enabled": true,
"quarantined": false
}
}
```
#### SSE Server with API Key
```json
{
"streaming-api": {
"protocol": "sse",
"url": "https://stream.example.com/mcp",
"headers": {
"X-API-Key": "api-key-here"
},
"enabled": true,
"quarantined": false
}
}
```
#### HTTP Server Without Authentication
```json
{
"public-api": {
"protocol": "http",
"url": "http://localhost:3000/mcp",
"enabled": true,
"quarantined": false
}
}
```
### Pattern 4: Local Command Servers
For locally installed commands (not uvx/npx).
```json
{
"local-server": {
"protocol": "stdio",
"command": "/usr/local/bin/mcp-server", // Full path or command in PATH
"args": [
"--config", "/path/to/config.json",
"--verbose"
],
"env": {
"HOME": "/Users/username",
"PYTHONPATH": "/path/to/modules"
},
"working_dir": "/Users/username/workspace",
"enabled": true,
"quarantined": false
}
}
```
**Real examples:**
#### Python Script as Server
```json
{
"custom-python": {
"protocol": "stdio",
"command": "python",
"args": [
"/Users/username/mcp-servers/my_server.py",
"--config", "production"
],
"env": {
"PYTHONUNBUFFERED": "1"
},
"working_dir": "/Users/username/mcp-servers",
"enabled": true,
"quarantined": false
}
}
```
#### Node.js Script as Server
```json
{
"custom-node": {
"protocol": "stdio",
"command": "node",
"args": [
"/Users/username/mcp-servers/server.js"
],
"env": {
"NODE_ENV": "production"
},
"working_dir": "/Users/username/mcp-servers",
"enabled": true,
"quarantined": false
}
}
```
## Complete Configuration File Structure
```json
{
"api_key": "mcpproxy-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"listen": "127.0.0.1:8080",
"docker_isolation": {
"enabled": true,
"memory_limit": "512m",
"cpu_limit": "1.0",
"timeout": "30s",
"network_mode": "bridge",
"default_images": {
"uvx": "python:3.11",
"npx": "node:20",
"python": "python:3.11",
"node": "node:20"
}
},
"mcpServers": {
"server1": {
"protocol": "stdio",
"command": "uvx",
"args": ["package-name"],
"enabled": true,
"quarantined": false
},
"server2": {
"protocol": "http",
"url": "https://api.example.com",
"enabled": true,
"quarantined": false
}
}
}
```
## Common Configuration Mistakes
### 1. Missing Package Name (uvx/npx)
```json
// ❌ WRONG
{"command": "uvx", "args": ["--arg", "value"]}
// ✅ CORRECT
{"command": "uvx", "args": ["package-name", "--arg", "value"]}
```
### 2. Wrong Docker Flags
```json
// ❌ WRONG - Using -it
{"command": "docker", "args": ["run", "-it", "--rm", "image"]}
// ✅ CORRECT - Using -i only
{"command": "docker", "args": ["run", "-i", "--rm", "image"]}
```
### 3. Docker Isolation Not Disabled
```json
// ❌ WRONG - Isolation enabled for Docker
{
"command": "docker",
"args": ["run", "-i", "--rm", "image"],
"isolation": {"enabled": true} // Docker-in-Docker!
}
// ✅ CORRECT - Isolation disabled
{
"command": "docker",
"args": ["run", "-i", "--rm", "image"],
"isolation": {"enabled": false}
}
```
### 4. Wrong Protocol for Server Type
```json
// ❌ WRONG - Using stdio for URL
{"protocol": "stdio", "url": "https://api.example.com"}
// ❌ WRONG - Using http for command
{"protocol": "http", "command": "uvx", "args": ["package"]}
// ✅ CORRECT - Match protocol to type
{"protocol": "http", "url": "https://api.example.com"}
{"protocol": "stdio", "command": "uvx", "args": ["package"]}
```
### 5. Server Still Quarantined
```json
// ❌ WRONG - Quarantine prevents execution
{
"command": "uvx",
"args": ["package"],
"quarantined": true // Tools return security analysis!
}
// ✅ CORRECT - Set to false to use tools
{
"command": "uvx",
"args": ["package"],
"quarantined": false
}
```
### 6. Invalid JSON
```json
// ❌ WRONG - Trailing comma
{
"command": "uvx",
"args": ["package"],
"enabled": true, // Trailing comma breaks JSON
}
// ✅ CORRECT - No trailing comma
{
"command": "uvx",
"args": ["package"],
"enabled": true
}
```
## Environment Variables
### Built-in Environment Variables
MCPProxy automatically provides:
- `HOME` - User's home directory
- `PATH` - System PATH
### Custom Environment Variables
Add via `env` field:
```json
{
"env": {
"API_KEY": "secret",
"DEBUG": "true",
"CUSTOM_VAR": "value"
}
}
```
### Environment Variable Priority
For Docker servers:
1. Variables in Docker args (`-e VAR=value`)
2. Variables in `env` field passed via `-e VAR` (value from env)
3. Host environment (if not overridden)
**Example:**
```json
{
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "API_KEY", // Value from env field
"-e", "DEBUG=true", // Value in args
"image:tag"
],
"env": {
"API_KEY": "secret" // Used by -e API_KEY above
}
}
```
## Quarantine Settings
New servers are automatically quarantined for security. To use them:
```json
{
"quarantined": false // Set to false to enable tool execution
}
```
**What quarantine does:**
- `quarantined: true` - Tools return security analysis instead of executing
- `quarantined: false` - Tools execute normally
**Best practice:** Review new servers before setting `quarantined: false`.
## Docker Isolation Configuration
See `references/docker-isolation-guide.md` for detailed Docker isolation configuration.
**Quick reference:**
```json
{
// Global settings
"docker_isolation": {
"enabled": true, // Master switch
"memory_limit": "512m", // Default memory per container
"cpu_limit": "1.0", // Default CPU per container
"network_mode": "bridge" // Default network mode
},
// Per-server override
"mcpServers": {
"my-server": {
"isolation": {
"enabled": false, // Disable isolation
// OR customize:
"enabled": true,
"image": "python:3.12",
"memory_limit": "1g",
"cpu_limit": "2.0",
"network_mode": "host"
}
}
}
}
```
## Verification After Configuration
After adding or modifying server configuration:
```bash
# 1. Trigger reload
touch ~/.mcpproxy/mcp_config.json
# 2. Wait for connection (30 seconds)
sleep 30
# 3. Check server status
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | \
python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
# 4. Verify connection
# Look for:
# "connected": true
# "status": "ready"
# "tool_count": > 0
# 5. If not connected, check logs
tail -50 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
## Configuration Checklist
When adding a new server, verify:
- [ ] JSON is valid (no trailing commas, quotes matched)
- [ ] Protocol matches server type (stdio/http/sse)
- [ ] For uvx/npx: Package name is first argument
- [ ] For Docker: Using `-i` not `-it`
- [ ] For Docker: Isolation disabled (`"enabled": false`)
- [ ] `enabled` is set to `true`
- [ ] `quarantined` is set to `false` (after review)
- [ ] Environment variables defined if needed
- [ ] Config file reloaded (touch or restart)
- [ ] Server status shows `connected: true` (wait 30s)
If server won't connect, see `references/connection-failures.md` for debugging.
## Summary
Configuration patterns vary by server type. The most common mistakes are:
1. Missing package name for uvx/npx
2. Using `-it` instead of `-i` for Docker
3. Leaving isolation enabled for Docker-based servers
4. Leaving servers quarantined
Follow the pattern for your server type, verify with the checklist, and check logs if it doesn't connect.

View File

@@ -0,0 +1,372 @@
# Connection Failures - Detailed Diagnosis
**When to use this reference:** Server shows `connected: false` or has connection errors in status API.
This reference provides step-by-step workflows for diagnosing why an MCP server won't connect through MCPProxy.
## Systematic Diagnosis Workflow
### Step 1: Check Server-Specific Logs
Server-specific logs contain the most revealing information, especially stderr output from the failing server.
```bash
# List all server logs (most recent first)
ls -lhrt ~/Library/Logs/mcpproxy/server-*.log | tail -10
# Check specific server log (last 50 lines)
tail -50 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
# Find errors in server log
grep -i "error\|failed\|stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
# Focus on stderr (most revealing)
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
```
**What to look for:**
- Error messages in stderr output
- Stack traces indicating crashes
- Missing dependencies or packages
- Permission errors
- Network connection failures
### Step 2: Identify Error Patterns
Most connection failures follow recognizable patterns. Match the error message to the pattern below.
## Common Error Patterns
### Pattern: "unexpected argument found"
**Example stderr:**
```
error: unexpected argument '--local-timezone' found
```
**Root cause:** Package manager (uvx/npx) received server arguments before package name.
**Commands affected:** `uvx`, `npx`, `yarn dlx`, `bunx`
**How it happens:**
```json
// WRONG - Arguments passed to uvx instead of the package
{
"command": "uvx",
"args": ["--local-timezone", "America/New_York"]
}
// Package manager sees: uvx --local-timezone America/New_York
// And thinks: "--local-timezone is not a uvx option!"
```
**Fix:**
```json
// CORRECT - Package name first, then its arguments
{
"command": "uvx",
"args": [
"mcp-server-time", // Package name FIRST
"--local-timezone", // Then package's arguments
"America/New_York"
]
}
```
**Verification:**
```bash
# After fixing config, check server log for successful connection
tail -20 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | grep -i "connected\|ready"
```
### Pattern: "the input device is not a TTY"
**Example stderr:**
```
the input device is not a TTY
```
**Root causes:**
#### Cause 1: Using `-it` with Docker
MCPProxy communicates via stdio pipes, not terminal TTY sessions. Using `-it` with Docker tries to allocate a TTY, which fails in pipe mode.
```json
// WRONG - Docker with -it flags
{
"command": "docker",
"args": ["run", "-it", "--rm", "image:tag"]
}
```
**Fix:**
```json
// CORRECT - Only -i for stdin pipe
{
"command": "docker",
"args": ["run", "-i", "--rm", "image:tag"]
}
```
#### Cause 2: Docker Isolation on Docker-based Server
MCPProxy's Docker isolation feature wraps commands in Docker containers. When the command itself is Docker, you get Docker-in-Docker, which causes TTY issues.
```json
// WRONG - Docker isolation wrapping a Docker command
{
"command": "docker",
"args": ["run", "-i", "--rm", "image:tag"],
"isolation": {
"enabled": true // This wraps Docker in Docker!
}
}
```
**Fix:**
```json
// CORRECT - Disable isolation for Docker commands
{
"command": "docker",
"args": ["run", "-i", "--rm", "image:tag"],
"isolation": {
"enabled": false
}
}
```
**Why this happens:** MCPProxy auto-detects Docker commands and should skip isolation, but explicit configuration is more reliable.
**Verification:**
```bash
# Check if isolation was applied
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
# Verify no Docker containers running for this server
docker ps | grep mcpproxy-SERVER_NAME
```
### Pattern: "context deadline exceeded"
**Example error:**
```
last_error: "context deadline exceeded"
status: "error"
```
**Root cause:** Server failed to initialize within timeout (usually 30 seconds).
**Common reasons:**
1. **Underlying error hidden** - Check stderr for the real error:
```bash
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
```
2. **Missing package name** (uvx/npx) - Look for "unexpected argument":
```bash
grep "unexpected argument" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
3. **Docker TTY issue** - Look for "not a TTY":
```bash
grep "TTY" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
4. **Docker image pull failed**:
```bash
grep -i "pull\|not found" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
5. **Server crashed during startup**:
```bash
grep -i "stack trace\|panic\|exception" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
6. **Missing environment variables**:
```bash
grep -i "missing\|required.*variable" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
7. **Network issues** (HTTP servers):
```bash
grep -i "connection refused\|timeout" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
```
**Investigation workflow:**
```bash
# 1. Check if process even started
grep "Starting connection\|Docker isolation setup" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
# 2. Check stderr for the real error
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
# 3. If Docker isolation enabled, check container logs
docker ps -a | grep mcpproxy-SERVER_NAME
docker logs CONTAINER_ID
# 4. Check Docker command being executed
grep "docker_run_args" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -3
```
**Fix:** Address the underlying error found in stderr.
### Pattern: "database is locked"
**Example error:**
```
ERROR: database is locked
```
**Root cause:** Another mcpproxy instance is running and has locked `~/.mcpproxy/config.db`.
**Fix:**
```bash
# Kill all mcpproxy instances
pkill mcpproxy
# Wait for processes to clean up
sleep 2
# Verify no mcpproxy processes remain
ps aux | grep mcpproxy | grep -v grep
# Restart mcpproxy
open /Applications/mcpproxy.app # macOS
# OR
mcpproxy & # Linux/headless
```
**Prevention:** Always use `pkill mcpproxy` before starting a new instance.
### Pattern: "transport error" / "EOF"
**Example error:**
```
last_error: "transport error: EOF"
```
**Root cause:** Server process exited unexpectedly or closed stdio pipes.
**Investigation:**
```bash
# Check if server crashed
grep -i "exit\|crash\|terminated" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -10
# Look for error messages before EOF
grep -B 20 "EOF" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -30
# Check stderr for crash details
grep "stderr" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -20
```
**Common causes:**
- Missing dependencies in Docker image
- Server code bug causing crash
- Memory/resource limits hit (Docker isolation)
- Permission errors accessing files
**Fix:** Depends on stderr output. Often requires fixing server code or Docker image.
## Step 3: Docker Isolation Checks
If Docker isolation is enabled and server is failing:
```bash
# Check if isolation is active for this server
grep -i "docker isolation enabled\|docker isolation setup" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
# List running containers
docker ps | grep mcpproxy
# Check container status
docker ps -a | grep mcpproxy-SERVER_NAME
# View container logs
docker logs CONTAINER_NAME
# Check Docker context (for Colima users)
docker context show
```
**If Docker isolation is interfering:**
1. See `references/docker-isolation-guide.md` for detailed Docker troubleshooting
2. Consider disabling isolation for this server (add `"isolation": {"enabled": false}`)
## Step 4: Configuration Verification
Verify the server configuration is correct:
```bash
# View server config (pretty-printed)
cat ~/.mcpproxy/mcp_config.json | python3 -m json.tool | grep -A 20 '"name": "SERVER_NAME"'
# Check for common mistakes:
# - uvx/npx: Package name is first argument?
# - Docker: Using -i not -it?
# - Docker servers: isolation disabled?
# - Quarantined: "quarantined": false?
```
For detailed configuration patterns, see `references/configuration-patterns.md`.
## Step 5: Test Server Command Manually
Test if the server command works outside MCPProxy:
```bash
# For uvx servers
uvx mcp-server-name --arg value
# For Docker servers (remove -i for manual testing)
docker run --rm -it image:tag
# For npx servers
npx mcp-server-name --arg value
```
**What to look for:**
- Does the command fail with same error?
- Is the package/image available?
- Are arguments correct?
## Debugging Checklist
Use this checklist systematically:
- [ ] Checked server-specific log for errors
- [ ] Examined stderr output (most revealing)
- [ ] Matched error to common patterns above
- [ ] Verified command syntax (uvx: package name first, Docker: -i not -it)
- [ ] Checked Docker isolation status (disabled for Docker commands?)
- [ ] Verified configuration file is valid JSON
- [ ] Tested command manually outside MCPProxy
- [ ] Triggered config reload (touch config file or restart mcpproxy)
- [ ] Waited 30 seconds for connection to initialize
- [ ] Checked main.log for global errors
If all checks pass and server still won't connect, see `references/debugging-examples.md` for real-world case studies.
## Success Verification
After fixing the issue:
```bash
# 1. Check server status via API
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
# Look for:
# "connected": true
# "status": "ready"
# "tool_count": > 0
# 2. Check server log for successful connection
tail -20 ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | grep -i "connected\|ready\|discovered"
# 3. Verify tools are available
curl -s "http://127.0.0.1:8080/api/v1/tools?apikey=YOUR_KEY" | python3 -m json.tool | grep "SERVER_NAME:"
```
If `connected: true` and `tool_count > 0`, the issue is resolved.

View File

@@ -0,0 +1,258 @@
# Real-World MCPProxy Debugging Examples
This file contains detailed walkthroughs of actual debugging sessions, providing concrete examples of how to diagnose and fix MCPProxy issues.
## Example 1: Buildkite Server - Docker Isolation Conflict
**Date**: October 2025
**Symptom**: Buildkite MCP server failing to connect with "the input device is not a TTY" error
**Server Type**: Docker-based MCP server (`ghcr.io/buildkite/buildkite-mcp-server:latest`)
### Initial Configuration
```json
{
"name": "buildkite",
"protocol": "stdio",
"command": "docker",
"args": [
"run",
"--pull=always",
"-q",
"-i",
"--rm",
"-e",
"BUILDKITE_API_TOKEN",
"ghcr.io/buildkite/buildkite-mcp-server:latest",
"stdio"
],
"env": {
"BUILDKITE_API_TOKEN": "${keyring:buildkite_token}"
},
"enabled": true,
"quarantined": false
}
```
### Diagnostic Process
1. **Ran diagnostic script**:
```bash
~/.claude/skills/mcpproxy-debug/scripts/diagnose_server.sh buildkite
```
2. **Key findings from stderr**:
```
"the input device is not a TTY"
```
3. **Pattern recognition**: This error typically indicates:
- Either `-it` flags being used (but config showed `-i` only)
- OR Docker isolation wrapping a Docker command (Docker-in-Docker)
4. **Checked Docker isolation status**:
- Global `docker_isolation.enabled` was `true`
- Server had no explicit isolation override
- MCPProxy was wrapping the Docker command in another container
### Root Cause
Docker isolation was enabled globally, and MCPProxy was attempting to run:
```
docker run [isolation-wrapper-args] docker run [server-args] ...
```
This creates a Docker-in-Docker situation where the inner Docker command can't allocate a TTY because it's running inside a container.
### Solution
Added explicit isolation disable to the server configuration:
```json
{
"name": "buildkite",
"protocol": "stdio",
"command": "docker",
"args": [
"run",
"--pull=always",
"-q",
"-i",
"--rm",
"-e",
"BUILDKITE_API_TOKEN",
"ghcr.io/buildkite/buildkite-mcp-server:latest",
"stdio"
],
"env": {
"BUILDKITE_API_TOKEN": "${keyring:buildkite_token}"
},
"isolation": {
"enabled": false
},
"enabled": true,
"quarantined": false
}
```
### Verification
After restarting mcpproxy:
1. **Process inspection**:
```bash
ps aux | grep mcpproxy
```
Confirmed the buildkite server was now running directly:
```
docker run --cidfile ... --pull=always -q -i --rm -e BUILDKITE_API_TOKEN ghcr.io/buildkite/buildkite-mcp-server:latest stdio
```
2. **Connection verification**:
```bash
~/.claude/skills/mcpproxy-debug/scripts/diagnose_server.sh buildkite
```
Output showed:
```
Successfully connected and initialized
server_name: "buildkite-mcp-server"
server_version: "0.7.2"
tool_count: 28
```
3. **No more TTY errors** in recent logs
### Key Lessons
1. **Docker isolation auto-skip may fail**: The intended automatic skip for Docker commands didn't work in this case
2. **Always explicitly disable isolation for Docker commands**: Best practice to prevent Docker-in-Docker issues
3. **Stderr is the most revealing**: The "TTY" error in stderr immediately pointed to the issue
4. **The diagnostic script is highly effective**: Automated pattern detection caught the problem immediately
### Applicable to Other Servers
This same issue and solution applies to any Docker-based MCP server:
- `ghcr.io/github/github-mcp-server`
- `ghcr.io/sooperset/mcp-atlassian:latest`
- Any custom Docker image serving MCP
**Template solution**:
```json
{
"name": "any-docker-mcp-server",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "ENV_VAR", "image:tag"],
"isolation": {
"enabled": false
}
}
```
## Example 2: API Key Mismatch After Restart
**Symptom**: API calls return "Invalid or missing API key" after restarting mcpproxy
### Diagnostic Process
1. **Check config file API key**:
```bash
grep '"api_key"' ~/.mcpproxy/mcp_config.json
```
Result: `"api_key": "d380462e333f25a1c61bad1d5d3f673277d5167dcdba18954effc7d6f0401c37"`
2. **API call with config key fails**:
```bash
curl -H "X-API-Key: d380462e333f25a1c61bad1d5d3f673277d5167dcdba18954effc7d6f0401c37" \
http://127.0.0.1:8080/api/v1/servers
```
Result: `{"success": false, "error": "Invalid or missing API key"}`
3. **Check logs for API key source**:
```bash
grep -i "api.*key" ~/Library/Logs/mcpproxy/main.log | tail -10
```
Result: `API key authentication enabled | {"source": "environment variable", "api_key_prefix": "9fc1****ee1c"}`
### Root Cause
The `MCPPROXY_API_KEY` environment variable was set (likely by the tray app), which takes precedence over the config file. The actual API key was `9fc15eb0...` (different from config).
### Solution
Use the environment variable key or unset the environment variable:
**Option 1**: Use the actual key from logs
```bash
# Extract from log prefix
grep "api_key_prefix" ~/Library/Logs/mcpproxy/main.log | tail -1
```
**Option 2**: Unset environment variable to use config file
```bash
unset MCPPROXY_API_KEY
pkill mcpproxy
open /Applications/mcpproxy.app
```
### Key Lesson
Always check both environment variables and config file when debugging API authentication. Environment variables take precedence.
## Pattern: Missing Package Name in uvx/npx
**Error**: `unexpected argument '--some-flag' found`
**Example**:
```json
// WRONG
{"command": "uvx", "args": ["--local-timezone", "America/New_York"]}
// CORRECT
{"command": "uvx", "args": ["mcp-server-time", "--local-timezone", "America/New_York"]}
```
**Diagnostic**: Check stderr in server logs for "unexpected argument" errors, then verify args array has package name first.
## Pattern: Context Deadline Exceeded
**Error**: `MCP initialize failed: transport error: context deadline exceeded`
**Common Causes**:
1. Missing package name (uvx/npx) - check stderr for "unexpected argument"
2. Docker TTY issue - check stderr for "not a TTY"
3. Server crashed on startup - check stderr for stack traces
4. Environment variables missing - check stderr for "missing" or "required"
**Diagnostic Workflow**:
```bash
# 1. Check stderr for actual error
grep "stderr" ~/Library/Logs/mcpproxy/server-{SERVER_NAME}.log | tail -20
# 2. Check if server started at all
grep "Starting connection" ~/Library/Logs/mcpproxy/server-{SERVER_NAME}.log | tail -5
# 3. Check Docker container if isolation enabled
docker ps | grep mcpproxy
docker logs CONTAINER_ID
```

View File

@@ -0,0 +1,458 @@
# Docker Isolation - Complete Guide
**When to use this reference:** Docker isolation is enabled and servers are failing, or you see "not a TTY" errors.
This reference explains how MCPProxy's Docker isolation feature works and how to debug issues with it.
## What is Docker Isolation?
MCPProxy can run stdio MCP servers inside Docker containers for security isolation. This prevents untrusted servers from accessing your filesystem or network.
**Key concept:** MCPProxy wraps the server command in `docker run`, manages the container lifecycle, and handles stdio communication between Claude Code and the containerized server.
## How It Works
### Normal Flow (No Isolation)
```bash
# User configures:
command: "uvx"
args: ["mcp-server-sqlite", "--db-path", "/path/to/db"]
# MCPProxy runs directly:
uvx mcp-server-sqlite --db-path /path/to/db
```
### With Docker Isolation Enabled
```bash
# User configures same as above
# MCPProxy detects runtime (uvx → Python) and wraps in Docker:
docker run -i --rm \
--memory="512m" \
--cpus="1.0" \
--network=bridge \
python:3.11 \
uvx mcp-server-sqlite --db-path /path/to/db
```
**MCPProxy automatically:**
1. Detects the runtime from the command (uvx→Python, npx→Node.js)
2. Selects appropriate Docker image
3. Wraps command in `docker run` with resource limits
4. Tracks container lifecycle
5. Cleans up container on exit
## Configuration
### Global Docker Isolation Settings
Located in `~/.mcpproxy/mcp_config.json`:
```json
{
"docker_isolation": {
"enabled": true, // Master switch
"memory_limit": "512m", // Per-container memory limit
"cpu_limit": "1.0", // CPU cores (1.0 = 1 core)
"timeout": "30s", // Container startup timeout
"network_mode": "bridge", // Docker network mode
"default_images": {
"uvx": "python:3.11", // Image for uvx commands
"npx": "node:20", // Image for npx commands
"python": "python:3.11", // Image for python commands
"node": "node:20" // Image for node commands
}
}
}
```
### Per-Server Isolation Override
You can override isolation settings for specific servers:
```json
{
"name": "my-server",
"command": "uvx",
"args": ["mcp-server-name"],
"isolation": {
"enabled": false, // Disable isolation for this server
// OR customize settings:
"enabled": true,
"image": "python:3.12-slim", // Custom image
"memory_limit": "1g", // More memory
"cpu_limit": "2.0", // More CPU
"network_mode": "none", // No network access
"working_dir": "/workspace" // Working directory in container
}
}
```
## Checking Docker Isolation Status
### Is Isolation Enabled for a Server?
```bash
# Check server log for isolation setup
grep -i "docker isolation enabled\|docker isolation setup" \
~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
# Look for:
# "Docker isolation enabled for server: SERVER_NAME"
# "Docker isolation setup successful"
```
### View the Docker Command Being Used
```bash
# See actual docker run command
grep "docker_run_args" ~/Library/Logs/mcpproxy/main.log | tail -3
# Example output:
# docker_run_args: ["run", "-i", "--rm", "--memory=512m", "--cpus=1.0",
# "--network=bridge", "python:3.11", "uvx", "mcp-server-name"]
```
### List Running Containers
```bash
# List all MCPProxy containers
docker ps | grep mcpproxy
# Check specific server container
docker ps | grep mcpproxy-SERVER_NAME
# Check container health
docker inspect --format='{{.State.Status}}' CONTAINER_ID
```
### View Container Logs
```bash
# If container is running
docker logs CONTAINER_NAME
# If container exited, find it in history
docker ps -a | grep mcpproxy-SERVER_NAME
docker logs CONTAINER_ID
```
## Common Docker Isolation Issues
### Issue 1: Docker-in-Docker (Most Common)
**Symptom:** "the input device is not a TTY" error for Docker-based servers
**Root cause:** MCPProxy's Docker isolation wraps a Docker command in Docker, creating nested containers.
```json
// Server config uses Docker directly
{
"command": "docker",
"args": ["run", "-i", "--rm", "mcp-image:latest"]
}
// MCPProxy wraps it in another Docker (if isolation enabled):
docker run ... python:3.11 docker run -i --rm mcp-image:latest
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This fails: Docker-in-Docker
```
**Solution:** Explicitly disable isolation for Docker-based servers:
```json
{
"name": "docker-server",
"command": "docker",
"args": ["run", "-i", "--rm", "mcp-image:latest"],
"isolation": {
"enabled": false // CRITICAL: Always disable for Docker commands
}
}
```
**Why explicit is better:** MCPProxy should auto-detect Docker commands and skip isolation, but explicit configuration is more reliable and prevents edge cases.
### Issue 2: File Access Problems
**Symptom:** Server can't access local files, permission errors
**Root cause:** Container can't access host filesystem by default.
**Solution 1: Disable isolation** (if file access is required):
```json
{
"isolation": {
"enabled": false
}
}
```
**Solution 2: Mount volumes** (more secure):
```json
{
"isolation": {
"enabled": true,
"volumes": [
"/host/path:/container/path:ro" // Read-only mount
]
}
}
```
**Note:** Volume mounting requires MCPProxy support. Check documentation for current status.
### Issue 3: Network Access Blocked
**Symptom:** Server can't make API calls, network timeouts
**Root cause:** Container network mode blocks external access.
**Check current network mode:**
```bash
grep "network_mode" ~/.mcpproxy/mcp_config.json
```
**Solution:** Change network mode:
```json
{
"isolation": {
"enabled": true,
"network_mode": "bridge" // Default, allows external access
// OR
"network_mode": "host" // Full host network access
// OR
"network_mode": "none" // No network (most secure)
}
}
```
### Issue 4: Container Image Not Found
**Symptom:** "context deadline exceeded", stderr shows "pull" or "not found"
**Investigation:**
```bash
# Check stderr for pull errors
grep -i "pull\|not found" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log
# Try pulling image manually
docker pull python:3.11
```
**Solution:** Ensure Docker image exists locally or can be pulled:
```bash
# Pull image before starting mcpproxy
docker pull python:3.11
# Or specify a custom image that exists
{
"isolation": {
"image": "python:3.12-slim" // Different image
}
}
```
### Issue 5: Resource Limits Too Low
**Symptom:** Container exits unexpectedly, out of memory errors
**Investigation:**
```bash
# Check container exit status
docker ps -a | grep mcpproxy-SERVER_NAME
# Look for OOMKilled status
docker inspect CONTAINER_ID | grep -i "oom"
```
**Solution:** Increase resource limits:
```json
{
"isolation": {
"enabled": true,
"memory_limit": "2g", // Increase from default 512m
"cpu_limit": "2.0" // Increase from default 1.0
}
}
```
## Docker Context Support (Colima)
MCPProxy uses your system's default Docker context. For Colima users:
```bash
# Check current Docker context
docker context show
# List all contexts
docker context ls
# Example output:
# NAME DESCRIPTION DOCKER ENDPOINT
# default Current DOCKER_HOST based configuration unix:///var/run/docker.sock
# colima * colima unix:///Users/.../.colima/docker.sock
# MCPProxy uses whichever has the asterisk (*)
```
**To change context:**
```bash
docker context use colima
docker context use default
```
**After changing context:** Restart mcpproxy for changes to take effect.
## Disabling Docker Isolation
### Globally (All Servers)
Edit `~/.mcpproxy/mcp_config.json`:
```json
{
"docker_isolation": {
"enabled": false // Disables for all servers
}
}
```
### Per-Server (Recommended)
Only disable for servers that need it:
```json
{
"name": "special-server",
"command": "docker", // Or any command that needs no isolation
"args": ["..."],
"isolation": {
"enabled": false // Only this server runs without isolation
}
}
```
**Best practice:** Keep isolation enabled globally, disable per-server only when necessary.
## Debugging Workflow for Docker Isolation Issues
Use this systematic workflow:
### Step 1: Verify Isolation Status
```bash
# Is isolation enabled for this server?
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-SERVER_NAME.log | tail -5
```
### Step 2: Check Container Status
```bash
# Is container running?
docker ps | grep mcpproxy-SERVER_NAME
# Did container exit?
docker ps -a | grep mcpproxy-SERVER_NAME
```
### Step 3: View Container Logs
```bash
# Get container ID
CONTAINER_ID=$(docker ps -a | grep mcpproxy-SERVER_NAME | awk '{print $1}')
# View logs
docker logs $CONTAINER_ID
```
### Step 4: Check Docker Command
```bash
# See exact command being used
grep "docker_run_args" ~/Library/Logs/mcpproxy/main.log | tail -3
```
### Step 5: Test Manually
```bash
# Extract docker run command from logs and test it manually
# Example:
docker run -i --rm --memory=512m --cpus=1.0 python:3.11 uvx mcp-server-name
```
### Step 6: Apply Fix
Based on the issue:
- Docker-in-Docker → Disable isolation
- File access → Disable isolation or mount volumes
- Network issues → Change network_mode
- Resource limits → Increase memory/CPU
- Image not found → Pull image or change image
### Step 7: Reload and Verify
```bash
# Trigger config reload
touch ~/.mcpproxy/mcp_config.json
# Or restart
pkill mcpproxy && open /Applications/mcpproxy.app
# Verify fix
curl -s "http://127.0.0.1:8080/api/v1/servers?apikey=YOUR_KEY" | \
python3 -m json.tool | grep -A 10 '"name": "SERVER_NAME"'
```
## When to Use Docker Isolation
### ✅ Use Docker Isolation When:
- Running untrusted third-party MCP servers
- Need security boundaries between servers
- Want resource limits per server
- Servers don't need host filesystem access
- Servers don't need special network configurations
### ❌ Disable Docker Isolation When:
- Server needs to access local files
- Server command is already Docker (prevents Docker-in-Docker)
- Server needs host network access
- Troubleshooting connection issues
- Performance is critical (isolation adds overhead)
## Quick Reference
### Check if isolation is active
```bash
grep -i "docker isolation" ~/Library/Logs/mcpproxy/server-NAME.log | tail -5
```
### List containers
```bash
docker ps | grep mcpproxy
```
### View container logs
```bash
docker logs CONTAINER_NAME
```
### Disable isolation for server
```json
{"isolation": {"enabled": false}}
```
### Increase resources
```json
{"isolation": {"memory_limit": "2g", "cpu_limit": "2.0"}}
```
### Change network mode
```json
{"isolation": {"network_mode": "host"}}
```
### Use custom image
```json
{"isolation": {"image": "python:3.12-slim"}}
```
## Summary
Docker isolation provides security but adds complexity. Start with isolation disabled while getting servers working, then enable it once everything is stable. Always disable isolation explicitly for Docker-based servers to prevent Docker-in-Docker issues.