Skip to content

Latest commit

 

History

History
265 lines (206 loc) · 11.3 KB

File metadata and controls

265 lines (206 loc) · 11.3 KB

Onboarding Guide: connect a local MCP server to ChatGPT or Codex

This guide helps you get from zero to a working tunnel-client process connected to your MCP server.

If you found this by searching "How do I connect local MCP server to ChatGPT", "How do I connect local MCP server to Codex", "localhost to ChatGPT", or "Codex local MCP", start with tunnel-client help quickstart. That is the shortest supported path before you drop into init, doctor, or Codex plugin flows.

For the customer-shareable network model and request flow, see architecture.md. For contributor-level connector routing, streaming, and troubleshooting details, see connectors.md.

1) Prerequisites

  • A reachable MCP server endpoint, or a command that starts a stdio MCP server.
  • A tunnel control-plane API key.
  • A provisioned tunnel_id for your environment.

Use these exact setup pages when you need to create or inspect those values:

  • Tunnels management and supported tunnel-client download: https://platform.openai.com/settings/organization/tunnels
  • Organization roles: https://platform.openai.com/settings/organization/people/roles
  • Organization groups: https://platform.openai.com/settings/organization/people/groups
  • Runtime API keys: https://platform.openai.com/settings/organization/api-keys
  • Admin API keys: https://platform.openai.com/settings/organization/admin-keys
  • ChatGPT connector settings: https://chatgpt.com/#settings/Connectors

Which value comes from where:

  • CONTROL_PLANE_TUNNEL_ID: create or inspect it in Tunnels management, or via tunnel-client admin tunnels create|list|get ... with OPENAI_ADMIN_KEY.
  • CONTROL_PLANE_API_KEY: create it in Runtime API keys; this is the key used by tunnel-client doctor and tunnel-client run.
  • OPENAI_ADMIN_KEY: only for tunnel-client admin tunnels list|create|update|delete. Do not use the admin key for the long-lived daemon.

Required permissions:

  • The runtime key principal needs Tunnels Read + Use for the target tunnel.
  • People who create or edit tunnels need Tunnels Read + Manage.
  • People who create OPENAI_ADMIN_KEY need Platform admin-key permission as well as any tunnel permissions they need for the task.

For the recommended roles/groups and screenshots, see permissions.md.

2) First-run paths

If you already have a tunnel-client binary, start there. The CLI now embeds the shortest first-use path:

tunnel-client help quickstart
tunnel-client help samples

The recommended binary-first path is:

tunnel-client run --embedded-mcp-stub --control-plane.tunnel-id tunnel_0123456789abcdef0123456789abcdef --health.listen-addr 127.0.0.1:0 --health.url-file /tmp/tunnel-client-health.url
curl -fsS "$(cat /tmp/tunnel-client-health.url)/readyz"
open "$(cat /tmp/tunnel-client-health.url)/ui"

If Codex is installed locally and you want the plugin surface instead of the raw binary flow, install it directly from the binary. The canonical command is tunnel-client codex plugin ...; tunnel-client plugin codex ... remains a compatibility alias. Keep lifecycle operations on the native runtimes command family:

tunnel-client codex plugin install
tunnel-client help plugin
tunnel-client runtimes list
tunnel-client codex plugin uninstall

Use tunnel-client run ... when you intentionally want a foreground daemon attached to the current terminal. For a long-lived local runtime managed by Codex, prefer tunnel-client runtimes connect ...; do not use nohup or disown as the tunnel-client supervision path. After runtimes connect, run tunnel-client runtimes status <alias> before reporting success. Only report success when status shows the managed runtime running with health reported; use --json when Codex needs explicit process_running, healthy, and ready fields.

If you want a named profile instead of the one-command demo path:

tunnel-client init --sample sample_mcp_stdio_local --profile local-stdio --tunnel-id tunnel_0123456789abcdef0123456789abcdef --mcp-command "python /path/to/server.py"
tunnel-client doctor --profile local-stdio --explain
tunnel-client run --profile local-stdio

If you still need the tunnel id or runtime/admin keys, open the matching setup URL above before running init. If your rollout has self-serve tunnel access, create the tunnel yourself in Tunnels management or with tunnel-client admin tunnels create, then export the returned id as CONTROL_PLANE_TUNNEL_ID and a separate runtime key as CONTROL_PLANE_API_KEY. Create or verify the connector from the ChatGPT settings URL above only while tunnel-client run ... is healthy, and keep the daemon running for connector discovery and every MCP call from ChatGPT.

The Platform Tunnels page download button is sourced from tunnel-service's gated tunnel metadata response. When a new public tunnel-client release becomes the supported download, update tunnel-service's hard-coded public artifact URL alongside the release handoff.

Other fast starts:

  • Remote HTTP MCP server with no OAuth/PRMD metadata: tunnel-client init --sample sample_mcp_remote_no_auth --profile remote-http --tunnel-id tunnel_... --mcp-server-url https://mcp.example.com/mcp
  • Outbound proxy or private PKI environment: export HTTPS_PROXY="http://proxy.internal.example.com:8080" export ENTERPRISE_CA_BUNDLE="/etc/ssl/certs/proxy-root.pem" then tunnel-client init --sample sample_mcp_enterprise_proxy --profile corp-proxy --tunnel-id tunnel_... --mcp-server-url https://mcp.internal.example.com/mcp
  • Embedded demo MCP server for end-to-end validation: tunnel-client dev mcp-stub then tunnel-client init --sample sample_mcp_with_dcr --profile sample_mcp_with_dcr --tunnel-id tunnel_... --mcp-server-url http://127.0.0.1:NNNN/mcp

The embedded UI is served from the health listener. With the default 127.0.0.1:8080, the UI is at http://127.0.0.1:8080/ui. If you set HEALTH_LISTEN_ADDR=:0 or --health.listen-addr=:0, the OS picks an ephemeral port at startup; use HEALTH_URL_FILE or --health.url-file if another tool needs the resolved UI URL.

If Codex is installed locally and you want the plugin surface instead of the raw binary flow, install it directly from the binary:

tunnel-client codex assistant "Summarize what tunnel-client is for."
tunnel-client codex status
tunnel-client codex plugin install
tunnel-client runtimes list
tunnel-client codex plugin uninstall

Starter prompts for Codex:

  • Figure out what tunnel-client is for from the binary help, then get me to /ui with the shortest local path.
  • Use tunnel-client to create or reuse a profile, run doctor --explain, and then start the foreground daemon attached to this terminal.
  • Run tunnel-client codex assistant and summarize what this checkout is for in one sentence.
  • Install the Codex plugin from the tunnel-client binary, connect the provided tunnel id, and tell me whether the runtime is launched, healthy, or ready.
  • For a long-lived local runtime, use tunnel-client runtimes connect to attach the provided tunnel id, then run tunnel-client runtimes status <alias> before reporting whether the runtime is launched, healthy, or ready.

3) Build from source

From the tunnel-client module root:

make admin-ui
go build -o bin/tunnel-client ./cmd/client

make admin-ui refreshes embedded UI assets before the local binary build. You can skip it for a docs-only checkout when the committed assets under pkg/adminui/assets are already present and unchanged.

Release source archives include the release semantic version in pkg/version/VERSION, so a plain go build from a downloaded release .tar.gz keeps the same client name and version metadata used by released binaries.

Or from a repository checkout with Bazel available:

bazel build //api/tunnel-client/cmd/client:client
./bazel-bin/api/tunnel-client/cmd/client/client

After building from source, use ./bin/tunnel-client or the Bazel output path unless you add that location to your PATH.

4) Configure

At minimum, you must set:

  • CONTROL_PLANE_API_KEY: control-plane authentication.
  • CONTROL_PLANE_TUNNEL_ID: the tunnel identifier for this deployment.
  • One main MCP binding:
    • MCP_SERVER_URL for a Streamable HTTP MCP endpoint, or
    • --mcp.command for a stdio MCP server.

Auth split to keep straight:

  • CONTROL_PLANE_API_KEY / OPENAI_API_KEY: runtime key used by the daemon.
  • tunnel-client admin tunnels get <tunnel_id> can use that runtime key for read-only metadata lookup.
  • admin tunnels list/create/update/delete require OPENAI_ADMIN_KEY or --admin-key.
  • The enterprise proxy sample documents both keys in comments so runtime and admin flows stay separate.

Example:

export CONTROL_PLANE_API_KEY="sk-..."        # preferred
export CONTROL_PLANE_TUNNEL_ID="tunnel_0123456789abcdef0123456789abcdef"
export MCP_SERVER_URL="https://mcp.internal.example.com/mcp"

CONTROL_PLANE_TUNNEL_ID must match the runtime validator: tunnel_ followed by 32 lowercase hexadecimal characters.

For the full surface (flags, defaults, advanced knobs), see configuration.md.

OAuth-protected MCP (supported)

  • Authorization headers are forwarded through the OpenAI tunnel service to your MCP server.
  • Custom MCP request headers configured on the app are forwarded through the OpenAI tunnel service, except internal auth and IP-forwarding transport headers.
  • OAuth discovery GETs are forwarded to the tunnel-client; discovery payloads and WWW-Authenticate resource_metadata are rewritten to OpenAI tunnel-service URLs for the same tunnel_id.
  • authorization_servers[0] from PRMD is the only source of truth and metadata fetch target for auth-server metadata enrichment and Harpoon OAuth target registration.
  • Auth-server metadata is accepted even when metadata issuer differs from authorization_servers[0] (external IdP issuer topologies are supported), and mismatch diagnostics are retained.
  • The authorization server itself is not tunneled. If it is only reachable on-prem or behind a firewall and not accessible from the internet or the tunnel-client host, the OAuth flow can fail.

5) Run

./bin/tunnel-client run --log.level=info --log.format=struct-text

This is the foreground/manual operator path. Keep this terminal attached while you want the daemon running. When Codex needs a long-lived local runtime that survives the current shell, use tunnel-client runtimes connect ... instead of improvising nohup or disown.

The process will:

  • Start polling the OpenAI tunnel service for work.
  • Forward JSON-RPC requests to your MCP server.
  • Expose health endpoints on HEALTH_LISTEN_ADDR (default 127.0.0.1:8080). Set it to :8080 only when a trusted remote probe needs access, or 127.0.0.1:0 when you explicitly want an ephemeral loopback port chosen at startup.

6) Verify

In another shell:

curl -fsS "http://127.0.0.1:8080/healthz"
curl -fsS "http://127.0.0.1:8080/readyz"
curl -fsS "http://127.0.0.1:8080/metrics" | head

7) Next reads