Fix Twilio MCP integration with baked-in credentials

- Add proper type and env sections to mcp-config.json
- Remove dynamic MCP add command, use pre-configured MCP
- Bake .env credentials into Docker image at build time
- Remove runtime .env volume mount - true one-time setup
- Auto-rebuild image when .env file changes
- Export Twilio env vars for MCP server subprocess
- Remove conflicting .mcp.json file
This commit is contained in:
Vishal Jain 2025-06-13 09:53:43 +01:00
parent 8f637508f4
commit 6cb57c9dc6
9 changed files with 262 additions and 35 deletions

View File

@ -1,12 +1,12 @@
# ABOUTME: Environment variables for Claude Docker # Copy this file to .env and fill in your credentials
# ABOUTME: Copy this to ~/.claude-docker/.env and fill in your values # The .env file will be baked into the Docker image during build
# Optional: Anthropic API key (only needed if not using subscription auth) # Required for Claude Code
# ANTHROPIC_API_KEY=sk-ant-your_anthropic_api_key_here ANTHROPIC_API_KEY=your_anthropic_api_key_here
# Optional: Twilio credentials for SMS notifications via MCP # Optional: Twilio credentials for SMS notifications
# TWILIO_ACCOUNT_SID=your_twilio_sid_here TWILIO_ACCOUNT_SID=your_twilio_account_sid
# TWILIO_API_KEY=your_twilio_api_key_here TWILIO_API_KEY=your_twilio_api_key
# TWILIO_API_SECRET=your_twilio_api_secret_here TWILIO_API_SECRET=your_twilio_api_secret
# TWILIO_FROM_NUMBER=+1234567890 # Your Twilio phone number TWILIO_FROM_NUMBER=+1234567890
# TWILIO_TO_NUMBER=+1234567890 # Your personal phone to receive SMS TWILIO_TO_NUMBER=+0987654321

4
.gitignore vendored
View File

@ -25,4 +25,6 @@ data/
# Temporary files # Temporary files
*.tmp *.tmp
*.temp *.temp
~* ~*
# Environment file with credentials
.env

View File

@ -12,8 +12,11 @@ RUN apt-get update && apt-get install -y \
sudo \ sudo \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Create a non-root user # Create a non-root user with matching host UID/GID
RUN useradd -m -s /bin/bash claude-user && \ ARG USER_UID=1000
ARG USER_GID=1000
RUN groupadd -g $USER_GID claude-user && \
useradd -m -s /bin/bash -u $USER_UID -g $USER_GID claude-user && \
echo "claude-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers echo "claude-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Create app directory # Create app directory
@ -38,6 +41,10 @@ COPY config/mcp-config.json /app/config/
COPY scripts/startup.sh /app/ COPY scripts/startup.sh /app/
RUN chmod +x /app/startup.sh RUN chmod +x /app/startup.sh
# Copy .env file during build to bake credentials into the image
# This enables one-time setup - no need for .env in project directories
COPY .env /app/.env
# Set proper ownership # Set proper ownership
RUN chown -R claude-user:claude-user /app /home/claude-user RUN chown -R claude-user:claude-user /app /home/claude-user

View File

@ -12,19 +12,26 @@ A Docker container setup for running Claude Code with full autonomous permission
## Quick Start ## Quick Start
1. **Clone and install:** 1. **Clone the repository:**
```bash ```bash
git clone https://github.com/VishalJ99/claude-docker.git git clone https://github.com/VishalJ99/claude-docker.git
cd claude-docker cd claude-docker
./scripts/install.sh
``` ```
2. **Configure your API keys:** 2. **Configure your API keys:**
```bash ```bash
# Edit ~/.claude-docker/.env with your keys # Copy the example file
cp .env.example .env
# Edit .env with your credentials
nano .env
```
Add your credentials to the `.env` file:
```bash
ANTHROPIC_API_KEY=your_anthropic_key ANTHROPIC_API_KEY=your_anthropic_key
# For Twilio MCP integration: # For Twilio MCP integration (optional):
TWILIO_ACCOUNT_SID=your_twilio_sid TWILIO_ACCOUNT_SID=your_twilio_sid
TWILIO_API_KEY=your_twilio_api_key TWILIO_API_KEY=your_twilio_api_key
TWILIO_API_SECRET=your_twilio_api_secret TWILIO_API_SECRET=your_twilio_api_secret
@ -32,13 +39,60 @@ A Docker container setup for running Claude Code with full autonomous permission
TWILIO_TO_NUMBER=your_phone_number TWILIO_TO_NUMBER=your_phone_number
``` ```
> **Important**: The `.env` file will be baked into the Docker image during build. This means:
> - Your credentials are embedded in the image
> - You can use the image from any directory without needing the .env file
> - Keep your image secure since it contains your credentials
> **Note**: Twilio MCP requires API Key/Secret instead of Auth Token. Create API keys in your Twilio Console under Account → API keys & tokens. > **Note**: Twilio MCP requires API Key/Secret instead of Auth Token. Create API keys in your Twilio Console under Account → API keys & tokens.
3. **Use from any project directory:** 3. **Build and install:**
```bash
./scripts/install.sh
```
This will:
- Build the Docker image with your credentials baked in
- Install the `claude-docker` command to your PATH
4. **Use from any project directory:**
```bash ```bash
claude-docker claude-docker
``` ```
## Usage Patterns
### One-Time Setup Per Project
For the best experience, run `claude-docker` once per project and leave it running:
1. **Start Claude Docker:**
```bash
cd your-project
claude-docker
```
2. **Detach from the session (keep it running):**
- **Mac/Linux**: `Ctrl + P`, then `Ctrl + Q`
- Hold Control key, press P, then Q while still holding Control
- Container keeps running in background
3. **Reattach when needed:**
```bash
docker ps # Find your container ID
docker attach claude-docker-session # Reattach to the session
```
4. **Stop when done with project:**
```bash
docker stop claude-docker-session
```
This workflow gives you:
- ✅ Persistent authentication (login once per machine)
- ✅ Persistent project context (one session per project)
- ✅ Perfect file permissions between host and container
- ✅ No repeated setup or authentication
## Features ## Features
### 🤖 Full Autonomy ### 🤖 Full Autonomy
@ -60,6 +114,7 @@ A Docker container setup for running Claude Code with full autonomous permission
- Login once, use forever - authentication tokens persist across sessions - Login once, use forever - authentication tokens persist across sessions
- No need to re-authenticate every time you start claude-docker - No need to re-authenticate every time you start claude-docker
- Credentials stored securely in `~/.claude-docker/claude-home` - Credentials stored securely in `~/.claude-docker/claude-home`
- Automatic UID/GID mapping ensures perfect file permissions between host and container
### 🐳 Clean Environment ### 🐳 Clean Environment
- Each session runs in fresh Docker container - Each session runs in fresh Docker container
@ -91,8 +146,12 @@ claude-docker/
## Configuration ## Configuration
The setup creates `~/.claude-docker/` with: During build, the `.env` file from the claude-docker directory is baked into the image:
- `.env` - API keys and configuration - Credentials are embedded at `/app/.env` inside the container
- No need to manage .env files in each project
- The image contains everything needed to run
The setup creates `~/.claude-docker/` in your home directory with:
- `claude-home/` - Persistent Claude authentication and settings - `claude-home/` - Persistent Claude authentication and settings
- `config/` - MCP server configuration - `config/` - MCP server configuration

View File

@ -1,6 +1,7 @@
{ {
"mcpServers": { "mcpServers": {
"twilio": { "twilio": {
"type": "stdio",
"command": "npx", "command": "npx",
"args": [ "args": [
"-y", "-y",
@ -10,7 +11,14 @@
"twilio_api_v2010", "twilio_api_v2010",
"--tags", "--tags",
"Api20100401Message" "Api20100401Message"
] ],
"env": {
"TWILIO_ACCOUNT_SID": "${TWILIO_ACCOUNT_SID}",
"TWILIO_API_KEY": "${TWILIO_API_KEY}",
"TWILIO_API_SECRET": "${TWILIO_API_SECRET}",
"TWILIO_FROM_NUMBER": "${TWILIO_FROM_NUMBER}",
"TWILIO_TO_NUMBER": "${TWILIO_TO_NUMBER}"
}
} }
} }
} }

View File

@ -24,18 +24,42 @@ if [ ! -d "$CURRENT_DIR/.claude" ]; then
echo "✓ Claude configuration created" echo "✓ Claude configuration created"
fi fi
# Check if .env file exists in user's home claude-docker directory # Check if .env exists in claude-docker directory for building
ENV_FILE="$HOME/.claude-docker/.env" ENV_FILE="$PROJECT_ROOT/.env"
if [ ! -f "$ENV_FILE" ]; then if [ -f "$ENV_FILE" ]; then
echo "✓ Found .env file with credentials"
else
echo "⚠️ No .env file found at $ENV_FILE" echo "⚠️ No .env file found at $ENV_FILE"
echo "Please create it with your API keys. See .env.example for reference." echo " Twilio MCP features will be unavailable."
exit 1 echo " To enable: create .env in claude-docker directory with your credentials"
fi fi
# Build the Docker image if it doesn't exist # Check if we need to rebuild the image
NEED_REBUILD=false
if ! docker images | grep -q "claude-docker"; then if ! docker images | grep -q "claude-docker"; then
echo "Building Claude Docker image..." echo "Building Claude Docker image with your user permissions..."
docker build -t claude-docker:latest "$PROJECT_ROOT" NEED_REBUILD=true
elif ! docker image inspect claude-docker:latest | grep -q "USER_UID.*$(id -u)" 2>/dev/null; then
echo "Rebuilding Claude Docker image to match your user permissions..."
NEED_REBUILD=true
elif [ -f "$ENV_FILE" ]; then
# Check if .env is newer than the Docker image
IMAGE_CREATED=$(docker inspect -f '{{.Created}}' claude-docker:latest 2>/dev/null)
if [ -n "$IMAGE_CREATED" ]; then
IMAGE_TIMESTAMP=$(date -d "$IMAGE_CREATED" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "${IMAGE_CREATED%%.*}" +%s 2>/dev/null)
ENV_TIMESTAMP=$(stat -c %Y "$ENV_FILE" 2>/dev/null || stat -f %m "$ENV_FILE" 2>/dev/null)
if [ -n "$IMAGE_TIMESTAMP" ] && [ -n "$ENV_TIMESTAMP" ] && [ "$ENV_TIMESTAMP" -gt "$IMAGE_TIMESTAMP" ]; then
echo "⚠️ .env file has been updated since last build"
echo " Rebuilding to include new credentials..."
NEED_REBUILD=true
fi
fi
fi
if [ "$NEED_REBUILD" = true ]; then
docker build --build-arg USER_UID=$(id -u) --build-arg USER_GID=$(id -g) -t claude-docker:latest "$PROJECT_ROOT"
fi fi
# Ensure the claude-home directory exists # Ensure the claude-home directory exists
@ -45,8 +69,6 @@ mkdir -p "$HOME/.claude-docker/claude-home"
echo "Starting Claude Code in Docker..." echo "Starting Claude Code in Docker..."
docker run -it --rm \ docker run -it --rm \
-v "$CURRENT_DIR:/workspace" \ -v "$CURRENT_DIR:/workspace" \
-v "$ENV_FILE:/app/.env:ro" \
-v "$HOME/.claude-docker/config:/app/.claude:rw" \
-v "$HOME/.claude-docker/claude-home:/home/claude-user/.claude:rw" \ -v "$HOME/.claude-docker/claude-home:/home/claude-user/.claude:rw" \
--workdir /workspace \ --workdir /workspace \
--name claude-docker-session \ --name claude-docker-session \

51
scripts/setup-env.sh Normal file
View File

@ -0,0 +1,51 @@
#!/bin/bash
# ABOUTME: Setup script to create ~/.claude-docker/.env with Twilio credentials
# ABOUTME: Run this once on your host machine to configure Twilio for all projects
CLAUDE_DOCKER_DIR="$HOME/.claude-docker"
ENV_FILE="$CLAUDE_DOCKER_DIR/.env"
# Create directory if it doesn't exist
mkdir -p "$CLAUDE_DOCKER_DIR"
# Check if .env already exists
if [ -f "$ENV_FILE" ]; then
echo "⚠️ $ENV_FILE already exists!"
read -p "Do you want to update it? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi
fi
echo "Setting up Twilio credentials for Claude Docker..."
echo "You'll need your Twilio account information."
echo
# Collect Twilio credentials
read -p "Enter your Twilio Account SID: " TWILIO_ACCOUNT_SID
read -p "Enter your Twilio API Key: " TWILIO_API_KEY
read -sp "Enter your Twilio API Secret: " TWILIO_API_SECRET
echo
read -p "Enter your Twilio phone number (with country code, e.g., +1234567890): " TWILIO_FROM_NUMBER
read -p "Enter the phone number to receive SMS (with country code): " TWILIO_TO_NUMBER
# Create .env file
cat > "$ENV_FILE" << EOF
# Twilio credentials for Claude Docker
TWILIO_ACCOUNT_SID=$TWILIO_ACCOUNT_SID
TWILIO_API_KEY=$TWILIO_API_KEY
TWILIO_API_SECRET=$TWILIO_API_SECRET
TWILIO_FROM_NUMBER=$TWILIO_FROM_NUMBER
TWILIO_TO_NUMBER=$TWILIO_TO_NUMBER
EOF
# Set restrictive permissions
chmod 600 "$ENV_FILE"
echo
echo "✅ Twilio credentials saved to $ENV_FILE"
echo "These credentials will be available to all Claude Docker sessions."
echo
echo "To test, run claude-docker from any project directory and use:"
echo " node /workspace/test-twilio.js"

View File

@ -3,10 +3,22 @@
# ABOUTME: Configures environment and starts Claude Code with Twilio MCP integration # ABOUTME: Configures environment and starts Claude Code with Twilio MCP integration
# Load environment variables from .env if it exists # Load environment variables from .env if it exists
# Use the .env file baked into the image at build time
if [ -f /app/.env ]; then if [ -f /app/.env ]; then
echo "Loading credentials from baked-in .env file"
set -a set -a
source /app/.env 2>/dev/null || true source /app/.env 2>/dev/null || true
set +a set +a
# Export Twilio variables for MCP server
export TWILIO_ACCOUNT_SID
export TWILIO_API_KEY
export TWILIO_API_SECRET
export TWILIO_FROM_NUMBER
export TWILIO_TO_NUMBER
else
echo "WARNING: No .env file found in image. Twilio features will be unavailable."
echo "To enable Twilio: create .env in claude-docker directory before building the image."
fi fi
# Configure Claude Code to use the MCP server # Configure Claude Code to use the MCP server
@ -20,11 +32,17 @@ else
echo "Your login will be saved for future sessions" echo "Your login will be saved for future sessions"
fi fi
# Start Claude Code with permissions bypass # Verify Twilio MCP configuration
echo "Starting Claude Code..." if [ -n "$TWILIO_ACCOUNT_SID" ] && [ -n "$TWILIO_API_KEY" ] && [ -n "$TWILIO_API_SECRET" ]; then
if [ -n "$TWILIO_ACCOUNT_SID" ] && [ -n "$TWILIO_API_KEY" ]; then echo "Twilio MCP pre-configured with:"
echo "Twilio MCP integration enabled" echo " - Account SID: ${TWILIO_ACCOUNT_SID:0:10}..."
echo " - From Number: $TWILIO_FROM_NUMBER"
echo " - To Number: $TWILIO_TO_NUMBER"
echo " - MCP Config: $CLAUDE_MCP_CONFIG"
else else
echo "No Twilio credentials found, MCP features will be unavailable" echo "No Twilio credentials found, MCP features will be unavailable"
fi fi
# Start Claude Code with permissions bypass
echo "Starting Claude Code..."
exec claude --dangerously-skip-permissions "$@" exec claude --dangerously-skip-permissions "$@"

60
test-twilio.js Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env node
// Test script to verify Twilio SMS functionality
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_API_SECRET;
const apiKey = process.env.TWILIO_API_KEY;
const fromNumber = process.env.TWILIO_FROM_NUMBER;
const toNumber = process.env.TWILIO_TO_NUMBER;
console.log('Twilio Test Configuration:');
console.log(`Account SID: ${accountSid?.substring(0, 10)}...`);
console.log(`API Key: ${apiKey?.substring(0, 10)}...`);
console.log(`From: ${fromNumber}`);
console.log(`To: ${toNumber}`);
// Using Twilio REST API directly
const https = require('https');
const auth = Buffer.from(`${apiKey}:${authToken}`).toString('base64');
const data = new URLSearchParams({
To: toNumber,
From: fromNumber,
Body: 'MCP is working! This is a test message from Claude Docker.'
});
const options = {
hostname: 'api.twilio.com',
port: 443,
path: `/2010-04-01/Accounts/${accountSid}/Messages.json`,
method: 'POST',
headers: {
'Authorization': `Basic ${auth}`,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.toString().length
}
};
const req = https.request(options, (res) => {
let body = '';
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
if (res.statusCode === 201) {
console.log('\n✅ SMS sent successfully!');
const response = JSON.parse(body);
console.log(`Message SID: ${response.sid}`);
console.log(`Status: ${response.status}`);
} else {
console.error('\n❌ Failed to send SMS');
console.error(`Status: ${res.statusCode}`);
console.error(`Response: ${body}`);
}
});
});
req.on('error', (e) => {
console.error(`Problem with request: ${e.message}`);
});
req.write(data.toString());
req.end();