dockerMCP-ChatGPT
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dockerMCP-ChatGPTList the files in my /workspace directory"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Docker MCP ChatGPT Coding Runner
A self-hosted remote MCP server for ChatGPT that can run shell commands, edit code, install Ubuntu packages, use Git, and authenticate with GitHub CLI. OAuth is bundled with Keycloak. Cloudflare can expose the stack through either a zero-account Quick Tunnel for testing or a stable named tunnel for regular use.
What this reuses
This project avoids rebuilding mature infrastructure:
Model Context Protocol Python SDK provides FastMCP, Streamable HTTP, OAuth protected-resource metadata, bearer-token middleware, and tool annotations.
Keycloak provides OAuth/OIDC, login UI, authorization-code flow, PKCE, refresh tokens, and client management.
Caddy provides reverse proxying so MCP and OAuth use one public hostname.
Cloudflare Tunnel provides optional outbound-only public routes, including Quick Tunnels and named tunnels.
Ubuntu 24.04 supplies Bash, Python, Node.js, Git, GitHub CLI (
gh), compilers, ripgrep, fd, jq, and curl.
There was no existing application code in this repository to preserve; the original repository contained only an empty README file.
Related MCP server: MCPServe
Architecture
ChatGPT
|
| HTTPS / OAuth
v
Cloudflare Quick Tunnel or named tunnel
|
v
Caddy gateway :8080
|-- /mcp and RFC 9728 metadata --> non-root FastMCP service
| |
| | Unix domain socket only
| v
| Ubuntu command runner
| - root user
| - writable container filesystem
| - outbound internet access
| - Git and GitHub CLI
| - persistent /workspace and /root
|
`-- /realms/mcp/* --> Keycloak --> PostgreSQLThe public MCP service and privileged command runner are separate containers. The MCP service remains non-root, read-only, capability-dropped, and connected to the runner only through a Unix socket. Commands requested through MCP execute as root in the runner container.
Exposed MCP tools
sandbox_info: report root, network, filesystem, GitHub CLI, and execution-limit state.run_command: execute a root Bash command starting under/workspace.read_file: read a bounded text file from/workspace.write_file: create or explicitly overwrite a text file under/workspace.list_files: list a bounded directory tree without following symlinks.
The dedicated file tools remain confined to /workspace. The run_command tool is intentionally not confined to workspace file access because root package installation requires access to the container filesystem.
Included development tools
The image includes:
bash, build-essential, curl, fd, git, gh, jq,
nodejs, npm, python3, pip, venv, ripgrepCheck them from the runner:
docker compose exec runner git --version
docker compose exec runner gh --versionRequirements
Ubuntu host with Docker Engine and Docker Compose v2.
A ChatGPT plan or workspace that supports custom remote MCP apps.
For a stable deployment: a public HTTPS hostname managed by Cloudflare or another reverse proxy.
For temporary testing: no Cloudflare account or domain is required when using the Quick Tunnel helper.
Initial setup
Copy the environment template:
cp .env.example .envGenerate independent secrets and put them in .env:
openssl rand -hex 32At minimum, replace POSTGRES_PASSWORD, KEYCLOAK_ADMIN_PASSWORD, OAUTH_CLIENT_SECRET, and MCP_USER_PASSWORD.
For a stable deployment, set PUBLIC_BASE_URL to the final HTTPS origin:
PUBLIC_BASE_URL=https://mcp.example.comDo not include /mcp in PUBLIC_BASE_URL.
For Quick Tunnel testing, the initial value can remain a placeholder. scripts/quick-tunnel.sh replaces it with the generated trycloudflare.com origin and recreates the URL-sensitive services.
Obtain the ChatGPT callback URL
While creating the custom app in ChatGPT, select OAuth and copy the exact callback URL shown by ChatGPT. It looks similar to:
https://chatgpt.com/connector/oauth/<callback_id>Set the full value as CHATGPT_CALLBACK_URL. Do not replace the callback ID, add a trailing slash, or use a wildcard.
The OAuth values entered in ChatGPT must match .env:
MCP endpoint: https://PUBLIC_HOST/mcp
Client ID: value of OAUTH_CLIENT_ID
Client secret: value of OAUTH_CLIENT_SECRET
Scopes: openid profile email offline_access mcp:toolsThe authorization-server issuer is:
https://PUBLIC_HOST/realms/mcpThe initial realm user is configured by MCP_USER and MCP_USER_PASSWORD. Keycloak forces a password change on first login.
Run locally without Cloudflare Tunnel
docker compose up -d --buildEndpoints:
MCP: http://localhost:8080/mcp
Health: http://localhost:8080/health
Keycloak admin: http://localhost:8081/admin/OAuth issuer URLs must match the token issuer exactly. For a ChatGPT connection, use the public HTTPS origin rather than the local URL.
Quick Tunnel: no Cloudflare account or token
Cloudflare Quick Tunnels create a random https://*.trycloudflare.com URL without a Cloudflare account, domain, DNS record, or tunnel token. They are intended only for development and testing.
After setting the required secrets and ChatGPT callback URL in .env, run:
bash scripts/quick-tunnel.shThe helper performs the complete bootstrap sequence:
builds and starts the local application;
recreates the
cloudflared-quickcontainer to obtain a fresh hostname;reads the generated URL from the Cloudflare logs;
writes that origin to
PUBLIC_BASE_URLin.env;recreates Keycloak, MCP, and Caddy so OAuth metadata and hostname validation use the new URL;
prints the MCP endpoint and OAuth issuer.
Example output:
Public origin: https://random-words.trycloudflare.com
MCP endpoint: https://random-words.trycloudflare.com/mcp
OAuth issuer: https://random-words.trycloudflare.com/realms/mcpEnter the printed MCP endpoint in ChatGPT.
The Quick Tunnel can also be started directly, but direct startup does not automatically update PUBLIC_BASE_URL:
docker compose --profile quick-tunnel up -d --build
docker compose logs -f cloudflared-quickImportant limitations:
A new Quick Tunnel process receives a new random hostname.
Rerunning the helper requires updating the MCP endpoint in ChatGPT.
Quick Tunnels are testing infrastructure, not a production service.
Cloudflare documents a 200-concurrent-request limit and no Server-Sent Events support for Quick Tunnels.
This MCP server uses stateless JSON responses rather than SSE, but the temporary hostname and testing-only service limits still apply.
Do not enable the
tunnelandquick-tunnelprofiles at the same time.
Use a named tunnel when the ChatGPT connector must survive container restarts without reconfiguration.
Stable named Cloudflare Tunnel
Create a named tunnel in Cloudflare Zero Trust and configure one public hostname:
Hostname: mcp.example.com
Service: http://gateway:8080Copy the tunnel token into CLOUDFLARE_TUNNEL_TOKEN, then start the profile:
docker compose --profile tunnel up -d --buildWithout --profile tunnel, the named cloudflared container does not start.
The tunnel does not replace OAuth. It only publishes the gateway through an outbound connection. Do not put another interactive login in front of this hostname unless ChatGPT can satisfy it; Keycloak already protects MCP.
Install software at runtime
The runner executes as root, has outbound network access, and has a writable root filesystem. Install packages from the host:
docker compose exec runner bash -lc \
'apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y shellcheck'The same command can be invoked through the MCP run_command tool:
apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y shellcheckRuntime-installed packages remain while the current runner container exists, but disappear when Docker recreates that container. For permanent dependencies, add them to the Dockerfile and rebuild:
docker compose up -d --build --force-recreate runner mcpGitHub login
The official GitHub CLI is installed as gh. Authenticate interactively from the host terminal rather than sending credentials through ChatGPT:
docker compose exec runner gh auth login \
--hostname github.com \
--git-protocol https \
--webThen configure Git to use GitHub CLI as its credential helper:
docker compose exec runner gh auth setup-git
docker compose exec runner gh auth statusFor a headless token login, keep the token out of shell history:
printf '%s' "$GH_TOKEN" | docker compose exec -T runner gh auth login --with-token
docker compose exec runner gh auth setup-gitGitHub CLI configuration, Git configuration, and SSH material under /root persist in the runner_home Docker volume. The workspace persists separately in the workspace volume.
After authentication, commands can use both tools:
docker compose exec runner git clone https://github.com/OWNER/REPOSITORY.git /workspace/REPOSITORY
docker compose exec runner gh repo view OWNER/REPOSITORYWorkspace management
Import a project:
docker compose cp ./my-project/. runner:/workspace/Export it:
docker compose cp runner:/workspace/. ./workspace-exportOpen a root shell:
docker compose exec runner bashOAuth and realm changes
Keycloak imports the realm only when the mcp realm does not already exist. Changing CHATGPT_CALLBACK_URL, the OAuth client secret, or the bootstrap user in .env does not rewrite an existing realm.
Changing PUBLIC_BASE_URL does take effect after recreating Keycloak and MCP. The Quick Tunnel helper performs that recreation automatically.
For other existing-realm changes, update the client in the local Keycloak admin console. During disposable development, stop the stack and remove only the PostgreSQL data volume to re-import the realm. Do not remove workspace or runner_home unless you intend to delete source files or GitHub credentials.
Remaining isolation
The public MCP service is non-root and read-only.
MCP and runner are separate processes in separate containers.
The runner has no Docker socket.
The runner is not attached to the internal Keycloak/PostgreSQL network.
The runner reaches MCP only through a shared Unix socket.
CPU, memory, PID, file-descriptor, output-size, file-size, and timeout limits remain enabled.
Workspace path traversal and symlink escape checks remain enabled for dedicated file tools.
OAuth access-token signature, issuer, audience, expiry, and scope validation remain enabled.
Keycloak admin endpoints are blocked from the public gateway and bound separately to loopback.
Both Cloudflare tunnel containers remain disabled unless their profile or helper is selected.
Critical security warning
This configuration gives an OAuth-authenticated LLM a remote root shell with outbound internet access. That is not a strong sandbox. A malicious instruction, prompt injection, compromised dependency, or stolen OAuth session can:
modify the runner operating system;
read persisted GitHub credentials;
push code or change repositories using your GitHub identity;
download and execute arbitrary software;
exfiltrate workspace data;
attack services reachable through the host or internet.
Use a dedicated low-privilege GitHub account or narrowly scoped token. Do not mount the Docker socket, host filesystem, personal SSH agent, cloud credentials, or production secrets. Keep ChatGPT action confirmations enabled.
Docker containers share the host kernel. For hostile or multi-user workloads, use gVisor, Kata Containers, or a Firecracker-style microVM and isolate the runner on a separate machine or disposable VM.
Useful commands
# Start a fresh Quick Tunnel and update OAuth URLs
bash scripts/quick-tunnel.sh
# Show status
docker compose ps
# Follow application logs
docker compose logs -f mcp runner keycloak gateway
# Follow Quick Tunnel logs
docker compose --profile quick-tunnel logs -f cloudflared-quick
# Check privilege and tools
docker compose exec runner bash -lc 'id && git --version && gh --version'
# Stop services without deleting data
docker compose down
# Stop all services including profile containers
docker compose --profile tunnel --profile quick-tunnel down
# Rebuild after source or Dockerfile changes
docker compose up -d --buildThis server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceAn MCP server to create secure code sandbox environment for executing code within Docker containers.Last updated326MIT
- Alicense-qualityDmaintenanceA simple MCP Server with shell execution capabilities that can be run locally with Ngrok tunneling or hosted in an Ubuntu 24 Docker container.Last updated4MIT
- Alicense-qualityCmaintenanceDockaShell is an MCP server that provides AI agents with isolated Docker containers for persistent shell access, file operations, and full audit trails, enabling autonomous development and self-evolving workflows.Last updated1329Apache 2.0
- AlicenseBqualityBmaintenanceSelf-hosted MCP server that enables ChatGPT to access and operate on local project files after OAuth authorization, without uploading the workspace elsewhere.Last updated38126MIT
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
A MCP server built for developers enabling Git based project management with project and personal…
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/TAY0123/dockerMCP-ChatGPT'
If you have feedback or need assistance with the MCP directory API, please join our Discord server