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:
parent
8f637508f4
commit
6cb57c9dc6
20
.env.example
20
.env.example
@ -1,12 +1,12 @@
|
||||
# ABOUTME: Environment variables for Claude Docker
|
||||
# ABOUTME: Copy this to ~/.claude-docker/.env and fill in your values
|
||||
# Copy this file to .env and fill in your credentials
|
||||
# The .env file will be baked into the Docker image during build
|
||||
|
||||
# Optional: Anthropic API key (only needed if not using subscription auth)
|
||||
# ANTHROPIC_API_KEY=sk-ant-your_anthropic_api_key_here
|
||||
# Required for Claude Code
|
||||
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||||
|
||||
# Optional: Twilio credentials for SMS notifications via MCP
|
||||
# TWILIO_ACCOUNT_SID=your_twilio_sid_here
|
||||
# TWILIO_API_KEY=your_twilio_api_key_here
|
||||
# TWILIO_API_SECRET=your_twilio_api_secret_here
|
||||
# TWILIO_FROM_NUMBER=+1234567890 # Your Twilio phone number
|
||||
# TWILIO_TO_NUMBER=+1234567890 # Your personal phone to receive SMS
|
||||
# Optional: Twilio credentials for SMS notifications
|
||||
TWILIO_ACCOUNT_SID=your_twilio_account_sid
|
||||
TWILIO_API_KEY=your_twilio_api_key
|
||||
TWILIO_API_SECRET=your_twilio_api_secret
|
||||
TWILIO_FROM_NUMBER=+1234567890
|
||||
TWILIO_TO_NUMBER=+0987654321
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -26,3 +26,5 @@ data/
|
||||
*.tmp
|
||||
*.temp
|
||||
~*
|
||||
# Environment file with credentials
|
||||
.env
|
||||
|
11
Dockerfile
11
Dockerfile
@ -12,8 +12,11 @@ RUN apt-get update && apt-get install -y \
|
||||
sudo \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create a non-root user
|
||||
RUN useradd -m -s /bin/bash claude-user && \
|
||||
# Create a non-root user with matching host UID/GID
|
||||
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
|
||||
|
||||
# Create app directory
|
||||
@ -38,6 +41,10 @@ COPY config/mcp-config.json /app/config/
|
||||
COPY scripts/startup.sh /app/
|
||||
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
|
||||
RUN chown -R claude-user:claude-user /app /home/claude-user
|
||||
|
||||
|
73
README.md
73
README.md
@ -12,19 +12,26 @@ A Docker container setup for running Claude Code with full autonomous permission
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Clone and install:**
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone https://github.com/VishalJ99/claude-docker.git
|
||||
cd claude-docker
|
||||
./scripts/install.sh
|
||||
```
|
||||
|
||||
2. **Configure your API keys:**
|
||||
```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
|
||||
|
||||
# For Twilio MCP integration:
|
||||
# For Twilio MCP integration (optional):
|
||||
TWILIO_ACCOUNT_SID=your_twilio_sid
|
||||
TWILIO_API_KEY=your_twilio_api_key
|
||||
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
|
||||
```
|
||||
|
||||
> **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.
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
### 🤖 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
|
||||
- No need to re-authenticate every time you start claude-docker
|
||||
- Credentials stored securely in `~/.claude-docker/claude-home`
|
||||
- Automatic UID/GID mapping ensures perfect file permissions between host and container
|
||||
|
||||
### 🐳 Clean Environment
|
||||
- Each session runs in fresh Docker container
|
||||
@ -91,8 +146,12 @@ claude-docker/
|
||||
|
||||
## Configuration
|
||||
|
||||
The setup creates `~/.claude-docker/` with:
|
||||
- `.env` - API keys and configuration
|
||||
During build, the `.env` file from the claude-docker directory is baked into the image:
|
||||
- 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
|
||||
- `config/` - MCP server configuration
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"twilio": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
@ -10,7 +11,14 @@
|
||||
"twilio_api_v2010",
|
||||
"--tags",
|
||||
"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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,18 +24,42 @@ if [ ! -d "$CURRENT_DIR/.claude" ]; then
|
||||
echo "✓ Claude configuration created"
|
||||
fi
|
||||
|
||||
# Check if .env file exists in user's home claude-docker directory
|
||||
ENV_FILE="$HOME/.claude-docker/.env"
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
# Check if .env exists in claude-docker directory for building
|
||||
ENV_FILE="$PROJECT_ROOT/.env"
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
echo "✓ Found .env file with credentials"
|
||||
else
|
||||
echo "⚠️ No .env file found at $ENV_FILE"
|
||||
echo "Please create it with your API keys. See .env.example for reference."
|
||||
exit 1
|
||||
echo " Twilio MCP features will be unavailable."
|
||||
echo " To enable: create .env in claude-docker directory with your credentials"
|
||||
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
|
||||
echo "Building Claude Docker image..."
|
||||
docker build -t claude-docker:latest "$PROJECT_ROOT"
|
||||
echo "Building Claude Docker image with your user permissions..."
|
||||
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
|
||||
|
||||
# Ensure the claude-home directory exists
|
||||
@ -45,8 +69,6 @@ mkdir -p "$HOME/.claude-docker/claude-home"
|
||||
echo "Starting Claude Code in Docker..."
|
||||
docker run -it --rm \
|
||||
-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" \
|
||||
--workdir /workspace \
|
||||
--name claude-docker-session \
|
||||
|
51
scripts/setup-env.sh
Normal file
51
scripts/setup-env.sh
Normal 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"
|
@ -3,10 +3,22 @@
|
||||
# ABOUTME: Configures environment and starts Claude Code with Twilio MCP integration
|
||||
|
||||
# Load environment variables from .env if it exists
|
||||
# Use the .env file baked into the image at build time
|
||||
if [ -f /app/.env ]; then
|
||||
echo "Loading credentials from baked-in .env file"
|
||||
set -a
|
||||
source /app/.env 2>/dev/null || true
|
||||
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
|
||||
|
||||
# Configure Claude Code to use the MCP server
|
||||
@ -20,11 +32,17 @@ else
|
||||
echo "Your login will be saved for future sessions"
|
||||
fi
|
||||
|
||||
# Start Claude Code with permissions bypass
|
||||
echo "Starting Claude Code..."
|
||||
if [ -n "$TWILIO_ACCOUNT_SID" ] && [ -n "$TWILIO_API_KEY" ]; then
|
||||
echo "Twilio MCP integration enabled"
|
||||
# Verify Twilio MCP configuration
|
||||
if [ -n "$TWILIO_ACCOUNT_SID" ] && [ -n "$TWILIO_API_KEY" ] && [ -n "$TWILIO_API_SECRET" ]; then
|
||||
echo "Twilio MCP pre-configured with:"
|
||||
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
|
||||
echo "No Twilio credentials found, MCP features will be unavailable"
|
||||
fi
|
||||
|
||||
# Start Claude Code with permissions bypass
|
||||
echo "Starting Claude Code..."
|
||||
exec claude --dangerously-skip-permissions "$@"
|
60
test-twilio.js
Executable file
60
test-twilio.js
Executable 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();
|
Loading…
Reference in New Issue
Block a user