OpenAI Tunnel Client

repository·master·Indexed 41 days ago

https://github.com/openai/tunnel-client

A secure agent that enables OpenAI-hosted products, such as ChatGPT and Codex, to interact with Model Context Protocol (MCP) servers running on private networks, local machines, or isolated clusters without public internet exposure. It provides a bridge for MCP servers to reach OpenAI services and includes a CLI for managing runtimes, an admin UI, and support for Docker, Kubernetes, and VM deployments.

Tokens
86.5K
Snippets
207
Records
371
Agent score
53%

What's inside tunnel-client

  1. What is the Secure MCP Tunnel client?

    master

    The tunnel-client is a customer-run agent that acts as a bridge between private or localhost MCP (Model Context Protocol) servers and OpenAI-hosted products like ChatGPT, Codex, the Responses API, and AgentKit.

    It allows OpenAI-hosted services to reach your MCP server without requiring the server to be exposed to the public internet or requiring new inbound firewall rules. The client provides observability via /healthz, /readyz, /metrics, and /ui endpoints.

  2. Configure Harpoon target templates

    master

    Harpoon target templates allow callers to use bounded identifiers for operator-configured HTTPS operations (GET, POST, or PUT). The client manages the destination, method, query names, and authentication headers, ensuring the caller only provides validated identifiers.

    Key Characteristics:

    • Supported Methods: GET (bodyless), POST, and PUT (require an explicit body policy). DELETE is unsupported.
    • Configuration: Templates must use config_version: 2 and template.version: 1 in YAML.
    • Execution: They are executed via the call_target MCP tool without a caller-supplied method.
    • Discovery: The list_targets tool publishes the invocation schema, parameter descriptions, and examples.
    • Safety: The client never follows redirects and validates all identifiers.
  3. Understand the tunnel-client permission model

    master

    Permissions required for tunnel-client operations depend on the specific task and the identity used:

    • Connecting to an existing tunnel: Requires a runtime key whose principal has Tunnels Read + Use permissions (use --tunnel-id).
    • Creating/Connecting without --tunnel-id or remote workflows: Requires an admin key plus Tunnels Read + Manage permissions.
    • ChatGPT Connector usage: If a Codex user selects the tunnel in ChatGPT connector settings, ensure they are also granted Tunnels Use.

    Security Best Practice: Always store admin or runtime key references using environment variable syntax (e.g., env:OPENAI_ADMIN_KEY or env:CONTROL_PLANE_API_KEY). Never pass literal keys directly in commands.

  4. How the tunnel-client architecture works

    master

    The tunnel-client enables OpenAI-hosted products (like ChatGPT, Codex, or AgentKit) to interact with a private Model Context Protocol (MCP) server located within a customer's network without exposing that server to the public internet.

    Core Mechanism

    1. Outbound Connection: The tunnel-client (an agent run by the customer) initiates an outbound HTTPS long-poll connection to the OpenAI tunnel service.
    2. Work Retrieval: The client polls for queued JSON-RPC commands via GET /v1/tunnels/{tunnel_id}/poll.
    3. Request Forwarding: When work is received, the client forwards the MCP JSON-RPC request to the private MCP server (via stdio, HTTP, or in-memory).
    4. Response Delivery: The client posts the result back to OpenAI via POST /v1/tunnels/{tunnel_id}/response.

    Key Benefits

    • No Inbound Firewall Rules: All tunnel traffic is initiated as outbound HTTPS from the customer network.
    • Private MCP Server: The internal MCP URL is never exposed to the internet; OpenAI products only interact with the OpenAI-hosted tunnel URL.
    • Local Operations: Health, readiness, metrics, logs, and an optional admin UI are managed locally by the customer.
    sequenceDiagram
      autonumber
      participant Product as OpenAI product
      participant Tunnel as OpenAI tunnel service
      participant Client as tunnel-client
      participant MCP as Customer MCP server
    
      Client->>Tunnel: Long-poll for work
      Product->>Tunnel: POST /v1/mcp/{tunnel_id}
      Tunnel-->>Client: Return queued JSON-RPC command
      Client->>MCP: Forward MCP JSON-RPC request
      MCP-->>Client: Return MCP response or notifications
      Client->>Tunnel: POST /v1/tunnels/{tunnel_id}/response
      Tunnel-->>Product: Return final response or SSE stream
  5. Understand the tunnel client authentication model

    master

    The tunnel-client uses two distinct authentication principals depending on the operation:

    Runtime Authentication

    • Used by: The tunnel daemon and standard runtime operations.
    • Credentials: CONTROL_PLANE_API_KEY or OPENAI_API_KEY.
    • Required Permissions: The principal associated with the runtime key needs Tunnels Read + Use permissions for the specific target tunnel.

    Admin Authentication

    • Used by: tunnel-client admin tunnels commands (list, create, update, delete).
    • Credentials: OPENAI_ADMIN_KEY (passed via --admin-key).
    • Required Permissions: Tunnels Manage.

    Tip: When using tunnel-client admin --json tunnels get <id>, you can retrieve organization_ids and workspace_ids. Use these exact values for subsequent admin operations instead of guessing IDs.

  6. Troubleshoot MCP discovery and tool visibility

    master

    On runtimes supporting component health, monitor control-plane for polls and response-delivery for uploads separately.

    Key Metrics:

    • queue: Counts waiting local work.
    • dispatcher: Counts actual active operations.

    Note on Discovery: A ready stdio runtime may not show observed MCP discovery until initialize or tools-list traffic is forwarded. Health reads do not trigger this traffic. Always check timestamps, generation, and catalog completeness before assuming tool names are missing or incorrect.

  7. Understand the Harpoon response contract

    master

    Harpoon returns a standardized response structure for all template calls. This structure mirrors the upstream HTTP response.

    Response Fields

    • status_code: The upstream HTTP status code (e.g., 200, 403, 404).
    • headers: An object containing upstream response headers (if present).
    • body_base64: The upstream response body encoded in Base64. You must decode this to obtain the actual bytes. The content is not guaranteed to be JSON.
    • body_size_bytes: The size of the upstream body in bytes.

    Error Handling

    • Upstream Errors: HTTP error statuses (like 403 or 404) are returned as successful tool responses containing the upstream status code.
    • Tool Errors: Invalid arguments, transport failures, or responses that exceed the max_response_bytes limit are treated as tool errors. An oversized response is not returned as a partial body; it results in an error.
    {
      "status_code": 200,
      "headers": {"Content-Type": ["application/json"]},
      "body_base64": "eyJvayI6dHJ1ZX0=",
      "body_size_bytes": 11
    }
  8. Configure health listener and security settings

    master

    Health Listener Configuration

    • Default: The health listener binds to 127.0.0.1:8080.
    • Network Exposure: Set HEALTH_LISTEN_ADDR=:8080 only if you intend to expose the health port to a trusted network.
    • Ephemeral Ports: Setting HEALTH_LISTEN_ADDR=:0 tells the OS to assign an ephemeral port. If you do this, you must set HEALTH_URL_FILE so probes can discover the resolved base URL.

    Security Best Practices

    • Secrets: Instead of environment variables, prefer mounting secrets as files and using the flag --control-plane.api-key=file:/....
  9. Proxy connectivity checks for HTTP proxies

    master

    When a template is configured to use an HTTP proxy, the client performs health checks using the following logic:

    • Mechanism: The client tests CONNECT to the template's fixed origin host and port.
    • Scope: The check never supplies parameter values, target authentication headers, or invokes the actual HTTP operation.
    • Meaning of Success: A successful CONNECT confirms that the proxy can reach the host/port; it does not guarantee that a specific resource exists or that the target will authorize a request.
    • Impact: A rejected CONNECT marks the proxy health as degraded for all templates using that proxy. Direct routes (non-proxy) do not trigger these checks.
  10. Use secret references in configuration

    master

    To keep sensitive data out of checked-in YAML files or process arguments, use the env: or file: prefixes for secret-bearing fields.

    • env:VARNAME: Resolves the value from an environment variable.
    • file:/path/to/secret: Resolves the value from a file.

    Supported for:

    • control_plane.api_key
    • control_plane.extra_headers
    • mcp.extra_headers
    • mcp.discovery_extra_headers
    • cloudflared.token

    Note: For cloudflared.token, literal values are rejected; you must use env: or file:. For control_plane.api_key, literal values are accepted for compatibility but not recommended.

    control_plane:
      api_key: env:CONTROL_PLANE_API_KEY
      extra_headers:
        X-Internal-Auth: env:CONTROL_PLANE_HEADER_VALUE
    
    mcp:
      extra_headers:
        X-Internal-Auth: env:MCP_RUNTIME_HEADER_VALUE
      discovery_extra_headers:
        X-Discovery-Auth: file:/run/secrets/mcp-discovery-header
    
    cloudflared:
      token: env:CLOUDFLARED_TOKEN
  11. Understand tunnel-client Channels

    master

    The tunnel-client manages multiple logical communication channels. Understanding these is key to how requests are routed and how payloads are identified.

    • main: The primary channel, configured via --mcp.server-url or --mcp.command.
    • harpoon: A built-in channel that is only active when the Harpoon feature has at least one registered target.
    • Additional channels: You can define custom channels by using channel-qualified entries in your --mcp.server-url or --mcp.command configurations.

    All response payloads sent to the /v1/tunnels/{tunnel_id}/response endpoint will include the specific channel value that was used for the request.

  12. Configure MCP bindings and channels

    master

    The tunnel-client requires a main channel binding. If no channel is specified, traffic is normalized to main.

    Supported Transports

    • stdio: Launched as local child processes via MCP_COMMAND or --mcp.command. Note: You can only have one active tunnel-client instance per tunnel_id when using stdio. Multiple instances sharing a tunnel_id are not supported.
    • Streamable HTTP: Reachable via MCP_SERVER_URL or --mcp.server-url. Supports Unix-domain sockets via unix-socket=<path|env:VAR>.
    • in-memory: Used for testing/demos only.

    Logical Channels

    You can configure additional channels using channel-qualified syntax:

    --mcp.server-url="channel=search,url=https://search-mcp.internal/mcp"
    --mcp.command="channel=tools,command=python /srv/tools_mcp.py"

    Note: The channel name harpoon is reserved for the embedded Harpoon MCP server.