Skip to main content
Glama
jinyoung

Streamable Pod Shell MCP Server

by jinyoung
README.md
# 🧭 Streamable Pod Shell MCP Server

A FastMCP-based server that creates isolated Kubernetes Pod sessions with real-time streaming shell command execution.

## πŸ“‹ Overview

This MCP server automatically creates a dedicated Kubernetes Pod for each session, providing:

- πŸ”’ **Isolated execution environment** per session
- πŸ“‘ **Real-time streaming** of stdout/stderr
- ⏱️ **Automatic TTL-based cleanup** to prevent Pod leaks
- 🐚 **Shell command execution** inside Pods
- πŸ“ **File management** (create/delete/list)
- 🟒 **Node.js code execution** support

## πŸ—οΈ Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              β”‚         β”‚              β”‚         β”‚   Kubernetes     β”‚
β”‚  MCP Client  │────────▢│  MCP Server  │────────▢│   Cluster        β”‚
β”‚ (Claude/AI)  β”‚         β”‚  (FastMCP)   β”‚         β”‚                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                                                   β”‚  β”‚ session-   β”‚  β”‚
                                                   β”‚  β”‚ abc123     β”‚  β”‚
                                                   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                                                   β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                                                   β”‚  β”‚ session-   β”‚  β”‚
                                                   β”‚  β”‚ def456     β”‚  β”‚
                                                   β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                                                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## πŸš€ Quick Start

### Prerequisites

- Python 3.10+
- Kubernetes cluster (kind, minikube, or production cluster)
- `kubectl` configured
- `uv` (for Python dependency management)

### Installation

1. **Clone the repository**
   ```bash
   git clone <repository-url>
   cd pod-mcp
   ```

2. **Install dependencies with uv**
   ```bash
   uv pip install -e .
   ```

3. **Set up Kubernetes resources**
   ```bash
   # Apply RBAC and namespace
   kubectl apply -f k8s/namespace.yaml
   kubectl apply -f k8s/serviceaccount.yaml
   kubectl apply -f k8s/role.yaml
   kubectl apply -f k8s/rolebinding.yaml
   ```

4. **Run the server locally**
   ```bash
   python run_server.py
   ```

## πŸ› οΈ MCP Tools

### Session Management

#### `create_session`
Create a new isolated Pod session.

**Parameters:**
- `ttl` (int, optional): Time-to-live in seconds (default: 600)
- `image` (str, optional): Container image (default: `busybox:latest`)
- `session_id` (str, optional): Custom session ID (auto-generated if not provided)

**Example:**
```python
{
  "ttl": 600,
  "image": "node:18-alpine",
  "session_id": "my-session"
}
```

#### `delete_session`
Delete a Pod session and clean up resources.

**Parameters:**
- `session_id` (str): Session ID to delete

#### `list_sessions`
List all active sessions with their details.

#### `extend_session`
Extend the TTL of an existing session.

**Parameters:**
- `session_id` (str): Session ID
- `extra_seconds` (int): Additional seconds to add (default: 300)

#### `get_session_status`
Get the status of a Pod session.

**Parameters:**
- `session_id` (str): Session ID

### File Operations

#### `list_files`
List files in a directory (streaming output).

**Parameters:**
- `session_id` (str): Session ID
- `path` (str, optional): Directory path (default: `/tmp`)

#### `create_file`
Create a file with content.

**Parameters:**
- `session_id` (str): Session ID
- `file_path` (str): Full path for the new file
- `content` (str): File content

#### `delete_file`
Delete a file from the Pod.

**Parameters:**
- `session_id` (str): Session ID
- `file_path` (str): Full path to the file

### Code Execution

#### `run_node`
Execute Node.js code (streaming output).

**Note:** Requires Node.js in the container image (e.g., `node:18-alpine`)

**Parameters:**
- `session_id` (str): Session ID
- `code` (str): JavaScript/Node.js code to execute

**Example:**
```javascript
console.log('Hello from Node.js!');
console.log(process.version);
```

#### `run_shell`
Execute arbitrary shell commands (streaming output).

**Parameters:**
- `session_id` (str): Session ID
- `command` (str): Shell command to execute

**Example:**
```bash
"echo 'Hello World' && date && pwd"
```

## 🐳 Docker Deployment

### Build Image

```bash
docker build -t streamable-pod-mcp:latest .
```

### Push to Registry

```bash
docker tag streamable-pod-mcp:latest your-registry/streamable-pod-mcp:latest
docker push your-registry/streamable-pod-mcp:latest
```

### Deploy to Kubernetes

```bash
# Update image in k8s/deployment.yaml
kubectl apply -f k8s/deployment.yaml
```

## ⎈ Helm Installation

### Install

```bash
helm install pod-mcp ./helm/streamable-pod-mcp \
  --namespace pod-mcp \
  --create-namespace
```

### Custom Values

```bash
helm install pod-mcp ./helm/streamable-pod-mcp \
  --namespace pod-mcp \
  --set image.repository=your-registry/streamable-pod-mcp \
  --set image.tag=v0.1.0 \
  --set mcpServer.podNamespace=default \
  --set mcpServer.defaultTTL=1200
```

### Upgrade

```bash
helm upgrade pod-mcp ./helm/streamable-pod-mcp \
  --namespace pod-mcp
```

### Uninstall

```bash
helm uninstall pod-mcp --namespace pod-mcp
```

## πŸ§ͺ Testing with kind

### 1. Create kind Cluster

```bash
kind create cluster --name mcp-test
```

### 2. Load Docker Image

```bash
# Build image
docker build -t streamable-pod-mcp:latest .

# Load into kind
kind load docker-image streamable-pod-mcp:latest --name mcp-test
```

### 3. Deploy

```bash
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/serviceaccount.yaml
kubectl apply -f k8s/role.yaml
kubectl apply -f k8s/rolebinding.yaml
kubectl apply -f k8s/deployment.yaml
```

### 4. Port Forward

```bash
kubectl port-forward -n pod-mcp svc/pod-mcp-server 8000:8000
```

### 5. Test

```bash
# The server should now be accessible at localhost:8000
curl http://localhost:8000/health
```

## πŸ” Security Considerations

### RBAC Permissions

The server requires the following permissions:
- `pods`: `get`, `list`, `watch`, `create`, `delete`
- `pods/status`: `get`
- `pods/exec`: `create`
- `pods/log`: `get`

### Resource Limits

Each session Pod has default limits:
- CPU: 200m (limit), 100m (request)
- Memory: 256Mi (limit), 128Mi (request)

### Command Filtering

⚠️ **Warning:** The `run_shell` tool allows arbitrary command execution. Consider:
- Running in isolated namespaces
- Implementing command whitelisting
- Using NetworkPolicies to restrict Pod network access
- Monitoring and logging all commands

## πŸ“Š Monitoring

### View Server Logs

```bash
kubectl logs -n pod-mcp deployment/pod-mcp-server -f
```

### List Session Pods

```bash
kubectl get pods -l managed-by=streamable-pod-mcp
```

### Check TTL Watcher

The TTL watcher runs as a background thread and automatically deletes expired Pods. Check server logs for entries like:

```
TTL watcher started (check interval: 10s)
TTL expired for session abc123, deleting pod...
```

## 🎯 Usage with Claude / Cursor

### Claude Desktop Configuration

Add to your Claude Desktop MCP settings:

```json
{
  "mcpServers": {
    "pod-shell": {
      "url": "http://localhost:8000",
      "transport": "http"
    }
  }
}
```

### Example Conversation

```
User: Create a new session with Node.js

Claude: I'll create a session with Node.js support.
[Calls create_session with image="node:18-alpine"]

Session created: session-abc123

User: Run some JavaScript code to check the Node version

Claude: [Calls run_node with code="console.log(process.version)"]

Output: v18.19.0

User: List files in /tmp

Claude: [Calls list_files with path="/tmp"]

total 0
drwxrwxrwt    2 root     root            40 Nov  2 12:00 .
drwxr-xr-x   17 root     root          4096 Nov  2 12:00 ..
```

## πŸ”§ Configuration

### Environment Variables

- `POD_NAMESPACE`: Kubernetes namespace for session pods (default: `default`)
- `IN_CLUSTER`: Whether running inside cluster (default: `false`)

### Server Configuration

Edit `run_server.py` to customize:

```python
initialize_server(
    namespace="my-namespace",  # Custom namespace
    in_cluster=False,          # Set True when deployed in-cluster
    start_watcher=True,        # Enable TTL watcher
)
```

## πŸ› Troubleshooting

### Pods Not Creating

1. Check RBAC permissions:
   ```bash
   kubectl auth can-i create pods --namespace=default --as=system:serviceaccount:pod-mcp:pod-mcp-server
   ```

2. Check server logs:
   ```bash
   kubectl logs -n pod-mcp deployment/pod-mcp-server
   ```

### Connection Refused

1. Verify service is running:
   ```bash
   kubectl get svc -n pod-mcp
   ```

2. Check port forwarding:
   ```bash
   kubectl port-forward -n pod-mcp svc/pod-mcp-server 8000:8000
   ```

### Pods Not Deleting

1. Check TTL watcher is running (check server logs)
2. Manually clean up:
   ```bash
   kubectl delete pods -l managed-by=streamable-pod-mcp
   ```

## πŸ“š Development

### Project Structure

```
pod-mcp/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ pod_manager.py      # Pod lifecycle management
β”‚   β”œβ”€β”€ executor.py          # Kubernetes exec stream handler
β”‚   └── mcp_server.py        # FastMCP server and tools
β”œβ”€β”€ k8s/                     # Kubernetes manifests
β”œβ”€β”€ helm/                    # Helm chart
β”œβ”€β”€ run_server.py            # Server entry point
β”œβ”€β”€ pyproject.toml           # Python dependencies (uv)
β”œβ”€β”€ Dockerfile               # Container image
└── README.md
```

### Running Tests

```bash
# Install dev dependencies
uv pip install -e ".[dev]"

# Run tests (coming soon)
pytest
```

### Code Formatting

```bash
black src/
```

## 🎯 Roadmap

- [ ] Persistent Volume support for session data
- [ ] Multi-namespace management
- [ ] WebSocket direct streaming mode
- [ ] Enhanced security with command filtering
- [ ] Metrics and Prometheus integration
- [ ] Session snapshots and restoration
- [ ] Support for more base images (Python, Go, etc.)

## πŸ“„ License

MIT License - see LICENSE file for details

## 🀝 Contributing

Contributions welcome! Please open an issue or submit a pull request.

## πŸ“§ Contact

For questions or issues, please open a GitHub issue.