GatewaySetup
Gateway onlineOAuth ready
Search⌘K
Gateway setup

Labby → Codex → ChatGPT.

Install a pinned Labby release, connect Codex to Labby over hardened SSH, expose Labby safely over HTTPS, configure Google OAuth, and add the same gateway to ChatGPT.

Pinned Labby v1.20.1Node 18+Recommended exposure Tailscale FunnelOpenAI Tunnel advanced alternative
Step 0 · Architecture

Pick the exposure path before touching OAuth.

Both paths connect ChatGPT to Labby. Only one is the simple all-in-one answer when Labby itself is the browser-facing OAuth authorization server.

Advanced · private MCP transport

OpenAI Secure MCP Tunnel

Outbound-only tunnel-client → OpenAI → private Labby. Great for keeping MCP private, but the browser-facing OAuth authorization server is not automatically tunneled.

Use Funnel for this tutorial's happy path. If you want Google OAuth and one URL that “just works,” Funnel removes the most moving parts. The Secure MCP Tunnel section later shows the private-MCP alternative and its OAuth caveat.
ChatGPT plan note. Full MCP write/modify support is currently for Business, Enterprise, and Edu. Pro can connect custom MCPs in developer mode for read/fetch permissions, but full MCP actions are not currently enabled there.
Step 1 · Pinned install

Run the known-good Labby, not whatever “latest” happens to be.

This guide is deliberately pinned to Labby 1.20.1. The npm package is just a thin launcher for the matching native Labby release, so nobody needs a Rust checkout or a source build.

1.1

Confirm Node.js 18 or newer.

Labby's npm launcher declares Node 18+.

node --version
npm --version

If node is missing or the major version is below 18, install a current Node LTS before continuing.

1.2

Verify the pinned no-install launcher.

This is the lowest-friction entrypoint and the one used throughout this runbook.

npx -y @dinglebear/labby@1.20.1 --version
Expected output: labby 1.20.1. If the version differs, stop here rather than silently continuing with a different binary.
1.3

Optional: install the same pinned package globally.

If this machine will run Labby continuously, a global install makes later commands shorter. It is still pinned to exactly the same release.

npm install --global @dinglebear/labby@1.20.1
labby --version

If you skip this, simply keep using npx -y @dinglebear/labby@1.20.1 … everywhere below.

Release v1.20.1 · stable GitHub release ↗ npm @dinglebear/labby@1.20.1 npm package ↗ Rule keep the version pinned; do not substitute latest.
Step 2 · Codex + Labby over SSH

Let Codex consume Labby from a remote machine without exposing MCP publicly.

Modern Codex is an MCP client. Configure Codex to launch ssh as its stdio transport; SSH starts the pinned labby mcp process on the remote Labby host and carries MCP over stdin/stdout.

Direction matters: Codex → SSH → labby mcp. Current Codex does not expose a codex mcp-server command, so this guide does not pretend Labby can launch Codex as an MCP server.
2.1

Prepare SSH and verify the remote Labby host in one pass.

Run this on the machine where Codex runs. Use a dedicated key so Codex can start the stdio transport non-interactively.

REMOTE_USER="YOUR_LABBY_USER"
REMOTE_HOST="YOUR_LABBY_HOST_OR_TAILSCALE_IP"
KEY="$HOME/.ssh/codex-labby"
KNOWN_HOSTS="$HOME/.ssh/known_hosts.codex-labby"

mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
test -f "$KEY" || ssh-keygen -t ed25519 -N "" -f "$KEY" -C "codex-labby"
chmod 600 "$KEY" && chmod 644 "$KEY.pub"

# Install the key. If ssh-copy-id is unavailable, use your normal authorized_keys workflow.
ssh-copy-id -i "$KEY.pub" "$REMOTE_USER@$REMOTE_HOST"

# Pin and inspect the host key. Compare this fingerprint with a trusted fingerprint for the Labby host.
ssh-keyscan -t ed25519 -H "$REMOTE_HOST" > "$KNOWN_HOSTS"
chmod 600 "$KNOWN_HOSTS"
ssh-keygen -lf "$KNOWN_HOSTS"

# One fail-closed preflight: identity + Node + exact Labby package.
ssh -T -i "$KEY"   -o IdentitiesOnly=yes   -o UserKnownHostsFile="$KNOWN_HOSTS"   -o StrictHostKeyChecking=yes   -o BatchMode=yes   -o ConnectTimeout=10   "$REMOTE_USER@$REMOTE_HOST"   'hostname; whoami; node --version; npx -y @dinglebear/labby@1.20.1 --version'
Host-key check: ssh-keyscan discovers a key but does not authenticate it. Compare the displayed fingerprint with one obtained through a trusted channel before proceeding.
2.2

Add the SSH-backed Labby server to Codex.

This uses Codex's real codex mcp add NAME -- COMMAND… interface. The hardened SSH flags prevent prompts, connection sharing surprises, and silent host-key acceptance.

codex mcp remove labby 2>/dev/null || true
codex mcp add labby --   ssh -T -i "$KEY"   -o IdentitiesOnly=yes   -o UserKnownHostsFile="$KNOWN_HOSTS"   -o StrictHostKeyChecking=yes   -o BatchMode=yes   -o ConnectTimeout=10   -o ServerAliveInterval=30   -o ServerAliveCountMax=3   -o ControlMaster=no   -S none   "$REMOTE_USER@$REMOTE_HOST"   npx -y @dinglebear/labby@1.20.1 mcp

The equivalent configuration is stored by Codex under ~/.codex/config.toml. No Labby or OAuth secrets need to be embedded in the project.

2.3

Prove configuration and a real MCP round trip together.

codex mcp get labby --json
codex mcp list --json
codex exec 'Use the Labby MCP server to list the available gateway tools. Read only; do not mutate anything.'
Pass condition: Codex reports the labby MCP server and the read-only smoke test reaches Labby successfully. If the config exists but the smoke test fails, rerun the Step 2.1 SSH preflight before debugging Codex.
Step 3 · Recommended HTTPS exposure

Expose Labby with Tailscale Funnel.

Funnel terminates public HTTPS for you and forwards it to Labby on loopback. No router port-forwarding, public IP, or hand-managed TLS certificate is required.

Required Tailscale 1.38.3+ · MagicDNS · HTTPS certificates · Funnel permission. Public HTTPS :443 Private 127.0.0.1:8765
3.1

Install Tailscale if this server does not already have it.

On mainstream Linux, Tailscale's official installer is:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

On macOS, Tailscale recommends the Standalone app. Install its CLI integration from Tailscale → Settings → CLI integration. Then verify the CLI is available.

tailscale version
tailscale status
3.2

Enable Funnel for Labby's local port.

You can configure Funnel before Labby is running. The first run may open a Tailscale approval page to enable HTTPS/Funnel for your tailnet.

tailscale funnel --bg 8765

If your platform requires elevated privileges, rerun that exact command with sudo. The --bg mode persists across Tailscale restarts and machine reboots.

3.3

Copy the public HTTPS URL.

tailscale funnel status

Look for a line shaped like https://your-machine.your-tailnet.ts.net. Save the root URL with no trailing /mcp. In the rest of this guide it is called PUBLIC_URL.

Example only: PUBLIC_URL=https://labby-gateway.example-tailnet.ts.net
ChatGPT's MCP endpoint will later be PUBLIC_URL/mcp.
3.4

Know how to inspect or remove the exposure.

tailscale funnel status --json
tailscale funnel reset
Funnel is public internet exposure. Keep Labby authentication enabled. Anyone on the internet can reach the public URL even though the underlying service remains on loopback.
Step 4 · Google OAuth

Give Labby a browser login that ChatGPT can complete.

Labby acts as the OAuth authorization server that ChatGPT talks to, while Google is the human identity provider behind it. The public URL from Funnel becomes Labby's issuer and the base for Google's callback.

What you need before starting: your Funnel root URL from Step 3, one Google account you want to use as Labby's bootstrap administrator, and access to Google Cloud Console / Google Auth Platform.
4.1

Set your public URL and print the exact Google callback.

Replace the example with the exact URL shown by tailscale funnel status. Do not add /mcp here.

PUBLIC_URL="https://YOUR-MACHINE.YOUR-TAILNET.ts.net"
printf 'Google redirect URI: %s/auth/google/callback\n' "$PUBLIC_URL"

Keep this shell open through the rest of the setup, or rerun this block later.

4.2

Create or select a Google Cloud project.

  1. Open Google Cloud Console ↗.
  2. Select an existing project dedicated to Labby, or create a new one such as Labby Gateway.
  3. Open Google Auth Platform. In the current console this contains Branding, Audience, Data Access, and Clients.
4.3

Configure the consent screen.

  1. Under Branding, enter an app name such as Labby, your support email, and developer contact email.
  2. Under Audience, choose Internal only if every allowed user belongs to the same eligible Google Workspace organization. Otherwise choose External.
  3. If the app is External and still in Testing, add the Google account you will use for Labby as a test user.
  4. Under Data Access, request only the basic identity scopes Labby needs: openid, email, and profile.
Labby's default Google scopes are already openid,email,profile, so you do not need broad Google API permissions for ordinary login.
4.4

Create the OAuth client.

  1. Open Google Auth Platform → Clients.
  2. Choose Create client.
  3. Application type: Web application.
  4. Name it something obvious, for example Labby Gateway.
  5. Under Authorized redirect URIs, add exactly the URL printed in Step 4.1: https://…/auth/google/callback.
  6. You can leave Authorized JavaScript origins empty for this server-side flow.
  7. Create the client and immediately save both the Client ID and Client secret.
Exact means exact. Google compares redirect URIs by scheme, hostname, path, case, and trailing slash. Register /auth/google/callback exactly as Labby will send it.
Store the client secret now. Google no longer guarantees that newly created client secrets remain viewable indefinitely after creation. Treat it like a password.
4.5

Generate Labby's token-encryption key.

This is separate from the Google client secret. Labby requires a 32-byte key to encrypt reusable OAuth credentials at rest.

openssl rand -hex 32

Copy the 64-hex-character output somewhere temporary and private. You will paste it into LABBY_TOKEN_ENCRYPTION_KEY in the next step.

4.6

Create Labby's protected environment file.

Labby defaults to ~/.labby. If an old environment file exists, back it up before editing.

mkdir -p "$HOME/.labby"
chmod 700 "$HOME/.labby"
if [ -f "$HOME/.labby/.env" ]; then
  cp -p "$HOME/.labby/.env" "$HOME/.labby/.env.before-google-oauth"
fi
chmod 700 "$HOME/.labby"

Now open the file in an editor:

nano "$HOME/.labby/.env"

Paste this template and replace every value beginning with YOUR_:

LABBY_MCP_HTTP_HOST=127.0.0.1
LABBY_MCP_HTTP_PORT=8765
LABBY_AUTH_MODE=oauth
LABBY_AUTH_PROVIDER=google
LABBY_PUBLIC_URL=https://YOUR-MACHINE.YOUR-TAILNET.ts.net
LABBY_GOOGLE_CLIENT_ID=YOUR_GOOGLE_CLIENT_ID
LABBY_GOOGLE_CLIENT_SECRET=YOUR_GOOGLE_CLIENT_SECRET
LABBY_AUTH_ADMIN_EMAIL=YOUR_GOOGLE_ACCOUNT_EMAIL
LABBY_TOKEN_ENCRYPTION_KEY=YOUR_64_HEX_CHARACTER_KEY

Then lock the file down:

chmod 600 "$HOME/.labby/.env"
Keep the redirect allowlist simple. For the beginner path, do not set LABBY_AUTH_ALLOWED_REDIRECT_URIS. Labby's defaults already include the current ChatGPT and Claude callback patterns. Setting that variable replaces the defaults rather than extending them.
4.7

Run Labby's auth doctor before starting the server.

npx -y @dinglebear/labby@1.20.1 doctor auth

Fix any reported missing variable, malformed public URL, key-length problem, or unsafe file permission before proceeding.

Step 5 · Launch + combined verification

Start Labby once, then run one checkpoint for the whole public path.

Instead of six tiny checks, use one local process and one consolidated verifier that covers loopback health, Funnel health, OAuth discovery, the protected MCP boundary, and Codex's saved MCP configuration.

5.1

Start the pinned Labby server.

npx -y @dinglebear/labby@1.20.1 serve --host 127.0.0.1 --port 8765

Keep this visible during first setup. Labby reads the OAuth settings from ~/.labby/.env at startup.

5.2

Run the combined readiness checkpoint.

PUBLIC_URL="https://YOUR-MACHINE.YOUR-TAILNET.ts.net"
set -e

printf '1/6 local Labby... '
curl -fsS http://127.0.0.1:8765/ready >/dev/null && echo OK

printf '2/6 public Labby... '
curl -fsS "$PUBLIC_URL/ready" >/dev/null && echo OK

printf '3/6 authorization metadata... '
curl -fsS "$PUBLIC_URL/.well-known/oauth-authorization-server" >/dev/null && echo OK

printf '4/6 protected-resource metadata... '
curl -fsS "$PUBLIC_URL/.well-known/oauth-protected-resource" >/dev/null && echo OK

printf '5/6 MCP auth boundary... '
status="$(curl --max-time 10 -sS -o /dev/null -w '%{http_code}' "$PUBLIC_URL/mcp")"
case "$status" in 401|403) echo "OK ($status protected)" ;; *) echo "CHECK ($status)"; exit 1 ;; esac

printf '6/6 Codex MCP config... '
codex mcp get labby --json >/dev/null && echo OK

tailscale funnel status
Pass condition: the first six lines report OK and Funnel points at the Labby port you configured. If a numbered check fails, that number tells you exactly which boundary owns the problem.
5.3

Finish with one real Codex-to-Labby smoke test.

codex exec 'Use the Labby MCP server to list the available gateway tools. Read only; do not mutate anything.'

This verifies the SSH-backed Codex path independently from the public ChatGPT path. Both should work before you turn the foreground Labby process into a long-running service.

Persistence comes after the smoke tests. Once Codex and ChatGPT both work against the pinned release, move that same Labby command under launchd, systemd, or your normal supervisor without changing versions mid-flight.
Step 6 · ChatGPT web

Enable Developer Mode and add Labby as a custom app.

OpenAI's current UI calls custom MCP connectors Apps. The labels can vary slightly by workspace role, but the flow is Developer Mode → Create app → enter the MCP endpoint → scan tools → finish OAuth.

Use ChatGPT on the web for setup. Developer-mode MCP configuration is managed in the web app. If your plan/workspace does not expose Developer Mode or Create, an administrator or a supported plan may be required.
6.1

Turn on Developer Mode.

Individual / Pro

Open Settings → Apps → Advanced Settings, then enable Developer mode if the toggle is available for your account.

Business / Enterprise / Edu

Your workspace may require an admin to allow Developer Mode first. Business admins/owners manage Apps in Workspace settings. Enterprise/Edu can gate it under workspace permissions for Connected Data.

Capability note: OpenAI currently documents full MCP write/modify support for Business, Enterprise, and Edu. Pro developer-mode custom MCP use is currently limited to read/fetch-style access. The UI can change as the feature rolls forward.
6.2

Open the Create App screen.

For an individual-enabled account, go to Settings → Apps → Create. In managed workspaces, admins/owners may instead begin from Workspace settings → Apps → Create.

Use the Funnel path here: choose the normal remote/server URL connection, not Tunnel. The MCP server URL is your public Funnel root plus /mcp.
6.3

Enter the Labby app details.

FieldValue
NameLabby
DescriptionYour MCP gateway / homelab control plane
ConnectionRemote MCP / URL
MCP server URLhttps://YOUR-MACHINE.YOUR-TAILNET.ts.net/mcp
AuthenticationOAuth
https://YOUR-MACHINE.YOUR-TAILNET.ts.net/mcp

Do not paste only the Funnel root. ChatGPT needs Labby's actual MCP transport endpoint ending in /mcp.

6.4

Scan tools and complete OAuth.

  1. Choose Scan tools or the equivalent validation action.
  2. ChatGPT should discover Labby's OAuth metadata and open the authorization flow.
  3. Sign in with the Google account that exactly matches LABBY_AUTH_ADMIN_EMAIL, or another identity already admitted by your Labby policy.
  4. Review the permissions Labby is asking ChatGPT to receive.
  5. Complete the authorization and return to ChatGPT.
  6. Review the discovered tools, then choose Create.
Do not “fix” an OAuth failure by turning authentication off. If Scan Tools reaches Labby but login fails, use the troubleshooting section to inspect the callback, public URL, admin email, and metadata.
6.5

Verify the app is enabled.

Open Settings → Apps. Your Labby entry should appear among enabled/developer apps, usually with a Dev/developer indicator. Managed workspaces may also show a Drafts area to administrators.

6.6

Use Labby in a chat.

Start a new web chat, add/select Labby from the Apps control for the message, or @mention the app where supported. Ask for a harmless read-only operation first, such as listing or summarizing available gateway tools.

First smoke test: prove ChatGPT can see Labby's gateway tools and complete a safe read-only call. You already proved the separate Codex → SSH → Labby path in Step 5, so failures here belong to the public HTTPS/OAuth/App path.
6.7

Refresh after changing Labby's tool surface.

If you add/remove an upstream or change exposed tools later, return to the app configuration and refresh/rescan the MCP definition so ChatGPT does not operate from stale tool metadata.

Alternative A · OpenAI Secure MCP Tunnel

Yes, ChatGPT can use OpenAI's MCP tunnel. It is not API-only.

Secure MCP Tunnel is an outbound-only transport from your machine to OpenAI. It can connect a private or localhost MCP server to ChatGPT developer-mode apps without publishing the MCP transport itself to the internet.

The useful part: ChatGPT can select a Secure MCP Tunnel directly when creating a developer-mode app. You do not need a public /mcp URL for the tunneled MCP transport.
The important OAuth catch: the tunnel forwards MCP/OAuth discovery, but it does not automatically tunnel the browser-facing authorization server. With Labby's Google OAuth flow, the authorization and Google callback URLs derived from LABBY_PUBLIC_URL still need to be reachable by the user's browser. Therefore Tunnel is a complete replacement for Funnel only when you are not relying on a browser-facing private Labby OAuth server. For the exact Google OAuth setup in this guide, Funnel remains the simplest single-path solution.

Use Tunnel alone when…

Your private MCP endpoint does not require a browser-reachable private authorization server, or your auth server is already reachable at a separate public HTTPS origin.

Use Funnel when…

You want one public HTTPS origin to serve Labby MCP, Labby OAuth metadata/authorization, and the Google callback with the least moving parts.

A.1

Create the tunnel in OpenAI Platform.

  1. Open the OpenAI Platform Tunnels page.
  2. Create a new tunnel, name it something recognizable such as labby-home, and save the returned tunnel_… ID.
  3. Associate the tunnel with the Platform organization and the target ChatGPT workspace/account that will use it.
A.2

Make sure the operator has the correct tunnel permissions.

OpenAI separates tunnel administration from tunnel use:

JobPermission
Create/edit/delete tunnelsTunnels Read + Manage
Run tunnel-client or select the tunnel in ChatGPTTunnels Read + Use
A.3

Create a runtime API key.

Create a normal project/runtime API key that the local tunnel process will use. Do not use an organization admin key for the long-running client.

Export it only in the shell/session that runs the tunnel client:

export CONTROL_PLANE_API_KEY="YOUR_OPENAI_RUNTIME_API_KEY"
export CONTROL_PLANE_TUNNEL_ID="tunnel_YOUR_TUNNEL_ID"
Do not paste the API key into the HTML artifact, a YAML file, Git, screenshots, or support logs. The generated profile stores an environment-variable reference, not the literal key.
A.4

Install the official tunnel-client.

On macOS, OpenAI documents a Homebrew install:

brew install openai/tools/tunnel-client
tunnel-client --version

On Linux or Windows, use the current official release for your platform from OpenAI's repository and follow its binary installation instructions.

A.5

Inspect the built-in DCR/OAuth profile before using it.

tunnel-client help quickstart
tunnel-client profiles samples list
tunnel-client profiles samples show sample_mcp_with_dcr

sample_mcp_with_dcr is OpenAI's general-purpose HTTP MCP profile for servers such as Labby that publish OAuth / protected-resource metadata.

A.6

Create a Labby tunnel profile.

Labby remains private on loopback. The tunnel client talks directly to its local MCP URL.

tunnel-client init   --sample sample_mcp_with_dcr   --profile labby   --tunnel-id "$CONTROL_PLANE_TUNNEL_ID"   --mcp-server-url http://127.0.0.1:8765/mcp

The generated profile should reference env:CONTROL_PLANE_API_KEY rather than embedding the key.

A.7

Run the tunnel preflight.

tunnel-client doctor --profile labby --explain

Resolve tunnel permission, runtime key, local MCP reachability, and OAuth-discovery errors before starting the long-running process.

A.8

Start the tunnel.

tunnel-client run --profile labby

By default the generated first-use profile exposes its local health/admin listener on loopback around port 8080. In another terminal, inspect it:

curl -fsS http://127.0.0.1:8080/healthz
curl -fsS http://127.0.0.1:8080/readyz

If enabled by the profile, the local operator UI is available at http://127.0.0.1:8080/ui.

A.9

Select the tunnel in ChatGPT.

  1. In ChatGPT web, enable Developer Mode as described in Step 6.
  2. Open Settings → Apps → Create.
  3. For Connection, choose Tunnel.
  4. Select the tunnel you created, or paste its tunnel_… ID if the UI requests it.
  5. Configure authentication. If you choose Labby's Google OAuth, remember that Labby's browser authorization URL must still be publicly reachable.
  6. Scan tools, complete auth if applicable, review the tool list, and create the app.
A.10

Know which route you actually built.

Tunnel-only + no browser-private OAuth: Labby's MCP transport can stay private.
Tunnel + separate public auth origin: MCP stays private while OAuth is reachable independently.
Funnel happy path: simplest for Labby + Google OAuth because one public HTTPS origin handles everything.
Client installer · harvested from install-mcp

Tell us what MCP client you use. Get its actual config.

The ecosystem never agreed on one file shape, so this section follows the same pattern as install-mcp: describe Labby once, then render the native install method for the selected client.

Labby stays pinned. Local installs always use npx -y @dinglebear/labby@1.20.1 mcp. Remote installs point the client at your HTTPS /mcp endpoint. No generated example uses latest.
13 common clientslocal + remote awareCLI / deep links when availablegenerated from mcp-install-instructions 1.2.1
J.1

Codex is the primary path; other clients stay available.

This guide defaults to Codex and its native ~/.codex/config.toml / codex mcp model. The picker still renders the correct dialect for other clients when you need one, instead of pretending every editor consumes the same mcp.json.

J.2

Generic starter JSON is still available.

For clients that understand the conventional mcpServers stdio shape, this remains a useful portable starting point.

{
  "mcpServers": {
    "labby": {
      "command": "npx",
      "args": ["-y", "@dinglebear/labby@1.20.1", "mcp"]
    }
  }
}
J.3

Server knobs belong in ~/.labby/.env.

LABBY_MCP_HTTP_HOST=127.0.0.1
LABBY_MCP_HTTP_PORT=8765
LABBY_AUTH_MODE=oauth
LABBY_AUTH_PROVIDER=google
LABBY_PUBLIC_URL=https://YOUR-MACHINE.YOUR-TAILNET.ts.net

The client installer changes how the client reaches Labby. It does not move secrets or server configuration into editor config files.

Never paste Google client secrets, Labby token-encryption keys, OpenAI API keys, or bearer tokens into a project MCP config. Keep secrets in Labby's owner-only ~/.labby/.env, your shell environment, or a secret manager.
Troubleshooting · isolate one hop at a time

Do not debug Google when the socket is dead.

Start at Labby and move outward. Each check below tells you which boundary is actually broken.

?.1

“Address already in use” on port 8765.

Find the process that already owns the port.

lsof -nP -iTCP:8765 -sTCP:LISTEN
ss -ltnp | grep ':8765'

Either stop the stale Labby process or deliberately choose another port. If you change the port, update LABBY_MCP_HTTP_PORT, Funnel/Tunnel, and LABBY_SERVER_URL together.

?.2

Local Labby works, but the Funnel URL does not.

curl -fsS http://127.0.0.1:8765/ready
tailscale status
tailscale funnel status

If local /ready works, do not touch Labby config yet. Check whether Funnel points to port 8765, whether the node is online, and whether Funnel was approved for the tailnet.

?.3

ChatGPT says it cannot reach or scan the MCP server.

PUBLIC_URL="https://YOUR-MACHINE.YOUR-TAILNET.ts.net"
curl -fsS "$PUBLIC_URL/ready"
curl -fsS "$PUBLIC_URL/.well-known/oauth-protected-resource" | python3 -m json.tool
curl --max-time 10 -sS -D - -o /dev/null "$PUBLIC_URL/mcp"

Use $PUBLIC_URL/mcp as the ChatGPT MCP endpoint. Use only $PUBLIC_URL for LABBY_PUBLIC_URL.

?.4

Google shows redirect_uri_mismatch.

PUBLIC_URL="https://YOUR-MACHINE.YOUR-TAILNET.ts.net"
printf '%s/auth/google/callback\n' "$PUBLIC_URL"

Compare that output character-for-character with the OAuth client's Authorized redirect URIs in Google Auth Platform. Check HTTPS, hostname, capitalization, callback path, and trailing slash.

?.5

Google login succeeds, but Labby rejects the user.

Make sure LABBY_AUTH_ADMIN_EMAIL is the same verified Google email you signed in with, then rerun:

npx -y @dinglebear/labby@1.20.1 doctor auth

If using an External Google app still in Testing, also confirm the account is listed as a Google OAuth test user.

?.6

Codex shows Labby configured, but calls fail.

Separate the SSH transport from Codex itself with the same fail-closed preflight used in Step 2:

ssh -T -i "$KEY"   -o IdentitiesOnly=yes   -o UserKnownHostsFile="$KNOWN_HOSTS"   -o StrictHostKeyChecking=yes   -o BatchMode=yes   "$REMOTE_USER@$REMOTE_HOST"   'hostname; whoami; node --version; npx -y @dinglebear/labby@1.20.1 --version'

If SSH passes, inspect what Codex actually saved and then rerun one read-only round trip:

codex mcp get labby --json
codex exec 'Use the Labby MCP server to list the available gateway tools. Read only; do not mutate anything.'

If the SSH preflight passes but the Codex smoke test does not, the failure is in the saved MCP command/config or Codex runtime, not Labby's public OAuth path.

?.7

OpenAI Tunnel is connected, but OAuth still fails.

This is the tunnel's biggest conceptual trap. Check whether the authorization URL advertised by Labby is reachable from an ordinary browser.

curl -fsS http://127.0.0.1:8765/.well-known/oauth-authorization-server | python3 -m json.tool

If the advertised issuer/authorization endpoint is localhost, a private hostname, or otherwise unreachable from the browser, the MCP tunnel cannot repair that. Publish the authorization server separately over HTTPS, or use the Funnel path for Labby + Google OAuth.

?.8

The tunnel does not appear in ChatGPT.

tunnel-client doctor --profile labby --explain

Then verify the tunnel is associated with the same Platform organization and target ChatGPT workspace/account, and that your identity has Tunnels Read + Use.

?.9

Need to remove public exposure immediately.

tailscale funnel reset

This removes Funnel configuration. It does not delete Labby configuration or Google OAuth credentials. Stop the Labby process separately if you also want the local gateway offline.

Known-good @dinglebear/labby@1.20.1 Release ↗Diagnostic order 127.0.0.1 → public HTTPS → OAuth metadata → auth challenge → ChatGPT. The first failed hop owns the investigation.