# How to Add an MCP Server to ChatGPT That Runs Code in a VM

> In ChatGPT on the web, open Plugins, click +, choose Add custom MCP server, enter https://sandboxer.com/mcp with OAuth, create and install the plugin, then type @ in a chat. OpenAI lists full MCP, including the write tools that run code, for Business and Enterprise/Edu. Developer mode is no longer required.

Source: https://sandboxer.com/guides/add-an-mcp-server-to-chatgpt. Updated 2026-10-27.

**Short answer:** In ChatGPT on the web, open Plugins, click +, choose Add custom MCP server, enter https://sandboxer.com/mcp with OAuth, create and install the plugin, then type @ in a chat. OpenAI lists full MCP, including the write tools that run code, for Business and Enterprise/Edu. Developer mode is no longer required.

## At a glance

| | Detail |
| - | - |
| Server URL | `https://sandboxer.com/mcp` |
| Transport | Streamable HTTP (ChatGPT supports SSE and streaming HTTP) |
| Sign-in in ChatGPT | OAuth: you sign in to Sandboxer and click **Allow**. ChatGPT doesn't take an API key |
| Sign-in from code | `Authorization: Bearer <Sandboxer API key>` |
| Tools | 18, of which 5 are read-only and 13 ask for confirmation in ChatGPT |
| Where it runs | Ubuntu microVMs on Sandboxer, not on OpenAI's servers or your computer |
| ChatGPT surface | Web only, not mobile |

## Which ChatGPT plans can do this

OpenAI's help article [Connecting custom MCP servers in ChatGPT](https://help.openai.com/en/articles/12584461-connecting-custom-mcp-servers-in-chatgpt) says, as of October 27, 2026:

| Plan | What OpenAI's article says |
| - | - |
| Business | Custom MCP server connections and full MCP support, on ChatGPT web. Only admins and owners can create and publish plugins for custom MCP servers. |
| Enterprise/Edu | Same, and admins can use role-based access control to let selected members create connections. Only admins and owners publish. |
| Pro | "Full MCP is only available to Business and Enterprise/Edu users, currently. Pro users can connect custom MCP servers with read/fetch permissions." |
| Plus, Free | Not listed in the article. |

What that means for running code: `create_sandbox` and `exec` are write tools (they don't carry `readOnlyHint`), so a ChatGPT account limited to read/fetch can't use them. Running code from ChatGPT needs the full MCP support that OpenAI lists for Business and Enterprise/Edu. Workspace policies apply on top: the creation permission is called **Create plugins with MCPs**.

Developer mode: the same article says that in the current Plugins experience you add a custom MCP server directly and "don't need to turn on Developer Mode first". You accept a risk warning for each server instead. Some workspaces still use the legacy path, **Settings > Apps > Create**, which scans the tools and saves a draft app marked **Dev**.

`search` and `fetch` tools: no longer required. OpenAI's [Add custom MCP server](https://developers.openai.com/api/docs/guides/custom-mcp-server) guide says custom MCP servers "do not require `search`/`fetch` tools". Two features still look for them: company knowledge only includes apps with search/fetch, and deep research uses custom apps for read/fetch actions only. Sandboxer has no `search` or `fetch` tool, so use it in a normal chat.

## Add Sandboxer to ChatGPT

1. Open [chatgpt.com/plugins](https://chatgpt.com/plugins) on the web.
2. Click **+**, then **Add custom MCP server**.
3. Name it `Sandboxer`. Under **Connection**, enter `https://sandboxer.com/mcp`.
4. Choose **OAuth** for authentication. Sandboxer supports both client registration methods ChatGPT uses (Client ID Metadata Documents and dynamic client registration), so leave the client fields empty.
5. Read the risk warning, check **I understand and want to continue**, then click **Create as a plugin** and review the 18 discovered tools.
6. Install the plugin from your personal plugins (or the workspace where you created it), sign in to Sandboxer and click **Allow**.
7. In a new conversation, type `@` and pick Sandboxer.

On a Business workspace, an admin or owner does this once and publishes it from the [Admin console](https://admin.openai.com/) (**Plugins > Manage Legacy Apps > Drafts > Publish**). Members then find it under **Settings > Plugins**. After publishing, ChatGPT keeps a frozen copy of the tool list until an admin refreshes it.

## What ChatGPT asks before each tool runs

ChatGPT treats any tool without `readOnlyHint` as a write action and asks you to confirm it by default. You can tell it to remember approve or deny for one tool for the rest of a conversation. A new or refreshed conversation asks again. These are the annotations the server returns from `tools/list` today:

| Treated as write actions (confirm by default) | Read-only (`readOnlyHint: true`) |
| - | - |
| `create_sandbox`, `exec`, `kill_command`, `write_file`, `host_port`, `stop_sandbox`, `resume_sandbox`, `extend_sandbox`, `rename_sandbox`, `label_sandbox`, `delete_sandbox`, `save_snapshot`, `delete_snapshot` | `list_sandboxes`, `get_sandbox`, `command_status`, `read_file`, `list_snapshots` |

`exec`, `kill_command`, `write_file`, `delete_sandbox` and `delete_snapshot` also carry `destructiveHint: true`. Remembering approval for `exec` is reasonable inside a sandbox you just created, since it only runs in that VM. Keep `delete_sandbox` on confirm.

## A prompt to try

```text
@Sandboxer Create a small sandbox named demo. Run python3 -c 'print(2**100)'.
Then start `sleep 30; echo done` as a detached command, poll command_status
until it exits, and delete the sandbox.
```

We didn't drive ChatGPT for this page (no OpenAI account in the test). We sent the same tool calls ChatGPT would send to `https://sandboxer.com/mcp` on production, with an API key, on October 27, 2026. These are the real results, trimmed:

```text
create_sandbox  {"name":"aeo-chatgpt-1","type":"small","ttlSeconds":900}
→ Sandbox sb_x0n1a2hf96 is ready. URL: https://quiet-brisk-jay.box.sandboxer.com. Stops automatically at 2026-10-27T02:26:10Z.

exec            {"command":"python3 -c 'print(2**100)'"}
→ Exit code 0.
  stdout:
  1267650600228229401496703205376

exec            {"command":"sleep 30; echo done","detached":true}
→ Started process 529 in sb_x0n1a2hf96. Call command_status with processId 529 for its output and exit code.

command_status  {"processId":529}
→ Process 529 is running.

command_status  {"processId":529}          # 35 seconds later
→ Process 529 exited with code 0.
  stdout:
  done

delete_sandbox  {"sandboxId":"sb_x0n1a2hf96"}
→ Deleting sb_x0n1a2hf96: its disk, memory and automatic snapshots are removed.
```

Things worth knowing from that run:

- **Every result has two parts:** the text above, and the same data as `structuredContent` (for example `exitCode`, `stdout`, and `stdoutNextOffset` to pass back so a poll returns only new output).
- **`sandboxId` is optional after the create.** Each tool defaults to the sandbox this MCP session created or used last. `delete_sandbox` is the exception: it always needs the id.
- **`exec` waits up to 120 seconds by default** (600 at most). Anything longer, such as a build or a server, should use `detached: true` and `command_status`. `command_status` is read-only, so ChatGPT doesn't treat a poll as a write action.
- **Sandboxes made over MCP stop after an hour** unless `ttlSeconds` says otherwise. A stopped sandbox bills no running time.
- **Failures come back as tool errors with the API's message** (`isError: true`), for example an unknown sandbox or a plan or start limit, so the model can tell you what went wrong.

## Check the server before you add it

If ChatGPT can't scan the tools, check the two things it needs first. Both work without an account:

```bash
curl -si -X POST https://sandboxer.com/mcp \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}' \
  | grep -i -e '^HTTP' -e '^www-authenticate'
```

```text
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://sandboxer.com/.well-known/oauth-protected-resource/mcp", scope="sandboxer"
```

```bash
curl -s https://sandboxer.com/.well-known/oauth-protected-resource/mcp
```

```json
{"authorization_servers":["https://sandboxer.com"],"bearer_methods_supported":["header"],"resource":"https://sandboxer.com/mcp","scopes_supported":["sandboxer"]}
```

`https://sandboxer.com/.well-known/oauth-authorization-server` then lists the authorize, token, register and revoke endpoints, `S256` PKCE, `client_id_metadata_document_supported: true` and the `refresh_token` grant. With a valid key, `initialize` returns server `sandboxer` on protocol `2025-06-18`, and `tools/list` returns the 18 tools.

## The same server from the OpenAI Responses API

OpenAI's [MCP servers guide](https://developers.openai.com/api/docs/guides/tools-connectors-mcp) uses a `mcp` tool with `server_url` in the Responses API. The Responses API supports Streamable HTTP and HTTP/SSE servers, and puts the server's token in the `authorization` field. For Sandboxer, that token is a Sandboxer API key:

```bash
curl https://api.openai.com/v1/responses \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-6-astra",
    "tools": [{
      "type": "mcp",
      "server_label": "sandboxer",
      "server_description": "Cloud sandboxes that run shell commands.",
      "server_url": "https://sandboxer.com/mcp",
      "authorization": "'"$SANDBOXER_API_KEY"'",
      "allowed_tools": ["create_sandbox", "exec", "command_status", "delete_sandbox"],
      "require_approval": "always"
    }],
    "input": "Create a small sandbox, run python3 -c \"print(2**100)\", then delete it."
  }'
```

We didn't run this request: the test had no OpenAI key. The shape follows OpenAI's guide, and the Sandboxer side of it (a Bearer API key on `/mcp`, `initialize`, `tools/list` and the four tools) is what we ran above. Notes from OpenAI's guide:

- **Approvals default to on.** Each call comes back as an `mcp_approval_request` that you answer with an `mcp_approval_response` on the next request. `"never"`, or a `never` list of tool names, skips them.
- **The `authorization` value isn't stored**, so send it on every request.
- **`allowed_tools` keeps the tool list short**, which lowers cost and latency. The four above cover create, run, poll and clean up.

Use a [scoped API key](/docs/api-keys) here. A key with the `ci` preset can create sandboxes and delete only the ones it created.

## FAQ

### Do I need ChatGPT developer mode to add an MCP server?

Not in the current Plugins experience. OpenAI's help article says you add the server from Plugins and accept a risk warning, without turning on developer mode first. Workspace policies still decide whether you can.

### Can ChatGPT Plus or Pro run code through an MCP server?

OpenAI's article lists full MCP, including write actions, for Business and Enterprise/Edu only, and says Pro can connect custom MCP servers with read/fetch permissions. Plus isn't listed. Running code uses write tools, so it needs Business or Enterprise/Edu.

### Can I paste a Sandboxer API key into ChatGPT instead of signing in?

No. ChatGPT offers OAuth, no authentication, or a mix of the two for custom MCP servers, and Sandboxer always requires sign-in, so choose OAuth. API keys are for code, such as the Responses API or the Agents SDK.

### How do I take away ChatGPT's access?

Open [API keys](/app/api-keys) in Sandboxer. The connection is listed as "ChatGPT (MCP)". Click **Disconnect** and its token stops working at once. The token only ever worked on `/mcp`, not on the REST API.

### Can ChatGPT connect to `sbx mcp` on my laptop?

Not directly. ChatGPT connects to remote servers only, and OpenAI points to its Secure MCP Tunnel for local ones. The hosted `https://sandboxer.com/mcp` has the same tools, minus `handoff_session`, so there's no need for a tunnel.

## Related

- [MCP server docs](/docs/mcp)
- [OpenAI Agents SDK](/docs/openai-agents)
- [API keys and scopes](/docs/api-keys)
- [What is an AI agent sandbox?](/guides/what-is-an-ai-agent-sandbox)
