A FastMCP-based server that creates isolated Kubernetes Pod sessions with real-time streaming shell command execution.
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
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββ
β β β β β Kubernetes β
β MCP Client ββββββββββΆβ MCP Server ββββββββββΆβ Cluster β
β (Claude/AI) β β (FastMCP) β β β
ββββββββββββββββ ββββββββββββββββ β ββββββββββββββ β
β β session- β β
β β abc123 β β
β ββββββββββββββ β
β ββββββββββββββ β
β β session- β β
β β def456 β β
β ββββββββββββββ β
ββββββββββββββββββββ
- Python 3.10+
- Kubernetes cluster (kind, minikube, or production cluster)
kubectlconfigureduv(for Python dependency management)
-
Clone the repository
git clone https://github.com/uengine-oss/process-gpt-computer-use cd pod-mcp -
Install dependencies with uv
uv pip install -e . -
Set up Kubernetes resources
# 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 -
Run the server locally
python run_server.py
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:
{
"ttl": 600,
"image": "node:20",
"session_id": "my-session"
}Delete a Pod session and clean up resources.
Parameters:
session_id(str): Session ID to delete
List all active sessions with their details.
Extend the TTL of an existing session.
Parameters:
session_id(str): Session IDextra_seconds(int): Additional seconds to add (default: 300)
Get the status of a Pod session.
Parameters:
session_id(str): Session ID
List files in a directory (streaming output).
Parameters:
session_id(str): Session IDpath(str, optional): Directory path (default:/tmp)
Create a file with content.
Parameters:
session_id(str): Session IDfile_path(str): Full path for the new filecontent(str): File content
Delete a file from the Pod.
Parameters:
session_id(str): Session IDfile_path(str): Full path to the file
Upload a Pod file to Supabase Storage (requires Supabase credentials).
Parameters:
session_id(str): Session IDsource_path(str): Absolute path to the file inside the Poddestination_path(str, optional): Relative path (within prefix) for the object. Defaults to the filename.bucket(str, optional): Supabase bucket (default:filesorSUPABASE_BUCKETenv value)overwrite(bool, optional): Overwrite existing object (default:false)content_type(str, optional): Explicit content type; auto-detected when omitted
Note: Uploaded paths are automatically nested under the /pod_mcp prefix unless overridden via SUPABASE_PATH_PREFIX.
Execute Node.js code (streaming output).
Note: Requires Node.js in the container image (recommended: node:20)
Parameters:
session_id(str): Session IDcode(str): JavaScript/Node.js code to executeensure_dependencies(bool, optional): Whentrue, runs a safenpm install(with cache isolation) if dependencies are missing before executing your code
If the session contains a package.json but no node_modules, the server automatically bootstraps dependencies even when ensure_dependencies is false, so first-time executions succeed without manual installs.
Example:
console.log('Hello from Node.js!');
console.log(process.version);Execute arbitrary shell commands (streaming output).
Parameters:
session_id(str): Session IDcommand(str): Shell command to execute
Example:
"echo 'Hello World' && date && pwd"npm install / npm ci commands are automatically wrapped with cache isolation, retry logic, and safe defaults to avoid common "idealTree" lock conflicts inside the shared container environment.
docker build -t streamable-pod-mcp:latest .docker tag streamable-pod-mcp:latest your-registry/streamable-pod-mcp:latest
docker push your-registry/streamable-pod-mcp:latest# Update image in k8s/deployment.yaml
kubectl apply -f k8s/deployment.yamlhelm install pod-mcp ./helm/streamable-pod-mcp \
--namespace pod-mcp \
--create-namespacehelm 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=1200helm upgrade pod-mcp ./helm/streamable-pod-mcp \
--namespace pod-mcphelm uninstall pod-mcp --namespace pod-mcpkind create cluster --name mcp-test# Build image
docker build -t streamable-pod-mcp:latest .
kubectl delete deploy pod-mcp-server -n pod-mcp
# Load into kind
kind load docker-image streamable-pod-mcp:latest --name mcp-testkubectl 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.yamlkubectl port-forward -n pod-mcp svc/pod-mcp-server 8000:8000# The server should now be accessible at localhost:8000
curl http://localhost:8000/health
kubectl exec -it -n pod-mcp session-9c4f386a -- shThe server requires the following permissions:
pods:get,list,watch,create,deletepods/status:getpods/exec:createpods/log:get
Each session Pod has default limits:
- CPU: 200m (limit), 100m (request)
- Memory: 256Mi (limit), 128Mi (request)
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
kubectl logs -n pod-mcp deployment/pod-mcp-server -fkubectl get pods -l managed-by=streamable-pod-mcpThe 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...
Add to your Claude Desktop MCP settings:
{
"mcpServers": {
"pod-shell": {
"url": "http://localhost:8888",
"transport": "http"
}
}
}User: Create a new session with Node.js
Claude: I'll create a session with Node.js support.
[Calls create_session with image="node:20"]
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: v20.11.1
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 ..
POD_NAMESPACE: Kubernetes namespace for session pods (default:default)IN_CLUSTER: Whether running inside cluster (default:false)SUPABASE_URL: Supabase project URL (required forupload_filetool)SUPABASE_KEY: Supabase service role key (required forupload_file; older log messages may still refer toSUPABASE_SERVICE_ROLE_KEY)SUPABASE_BUCKET: Supabase storage bucket name (default:files)SUPABASE_PATH_PREFIX: Storage path prefix applied to uploads (default:/pod_mcp)
Edit run_server.py to customize:
initialize_server(
namespace="my-namespace", # Custom namespace
in_cluster=False, # Set True when deployed in-cluster
start_watcher=True, # Enable TTL watcher
)-
Check RBAC permissions:
kubectl auth can-i create pods --namespace=default --as=system:serviceaccount:pod-mcp:pod-mcp-server
-
Check server logs:
kubectl logs -n pod-mcp deployment/pod-mcp-server
-
Verify service is running:
kubectl get svc -n pod-mcp
-
Check port forwarding:
kubectl port-forward -n pod-mcp svc/pod-mcp-server 8000:8000
- Check TTL watcher is running (check server logs)
- Manually clean up:
kubectl delete pods -l managed-by=streamable-pod-mcp
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
# Install dev dependencies
uv pip install -e ".[dev]"
# Run tests (coming soon)
pytestblack src/- 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.)
MIT License - see LICENSE file for details
Contributions welcome! Please open an issue or submit a pull request.
For questions or issues, please open a GitHub issue.