TophatMCP
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., "@TophatMCPask ChatGPT: 'What is the capital of France?'"
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.
TophatMCP
TophatMCP is an authenticated OpenAI-compatible inference gateway backed by an async job broker that bridges a coding agent to a ChatGPT MCP worker.
A coding agent talks to TophatMCP exactly as if it were any OpenAI-compatible
provider (POST /v1/chat/completions). TophatMCP enqueues the request as an
InferenceJob and blocks the HTTP request until a ChatGPT worker — connected
over a tunnel-presented /mcp surface — claims the job and submits a result.
flowchart TD
A[Coding Agent e.g. Qwen Code] -- "POST /v1/chat/completions" --> B(TophatMCP Gateway)
B -- "enqueue InferenceJob (blocks HTTP)" --> J[(Job Broker / SQLite)]
J -- "pending claim resolves atomically" --> C{ChatGPT MCP Worker}
C -- "submit_inference_result" --> J
J -- "resolve waiter / HTTP 200" --> ATophatMCP never executes the coding agent's tools. It merely preserves the protocol that lets the coding agent execute them locally and feed results back in.
Why a job broker (not a synchronous proxy)
The OpenAI Secure MCP tunnel presents TophatMCP's own /mcp surface to
ChatGPT — ChatGPT dials in. A synchronous /v1 call therefore cannot traverse
the tunnel by calling an outbound function. The rendezvous is inverted into an
async job queue:
The coding agent hits
POST /v1/chat/completions. TophatMCP enqueues anInferenceJoband blocks the HTTP request.The ChatGPT worker (via tunnel → TopHat
/mcp) callsclaim_inference_job. The MCP call stays pending (bounded long-poll, capped at 30 s) until a job arrives, then resolves with the job payload (messages + tool defs).ChatGPT reasons, then calls
submit_inference_result(job_id, …). TophatMCP converts the result into exact OpenAItool_calls/ content and resolves the waiting HTTP request.The loop continues: the agent executes tools locally, sends the next
/v1request → new job → ChatGPT claims → submits.
Both the /v1 handler and the MCP worker tools talk to one shared Job Broker
(durable SQLite job state). There is no sync fetch to a WebUI — ChatGPT is reached
only through the tunnel-presented MCP server.
Related MCP server: mcp-llm-bridge
Firefox extension worker ownership
The supported browser adapter is a user-operated Firefox extension connected to the actuator through Native Messaging. A toolbar click on a ChatGPT tab is an explicit worker-selection action: it selects that tab/profile as the only browser worker allowed to receive inference prompts. This avoids silently choosing a personal ChatGPT conversation.
More than one Firefox profile may have the extension connected at the same
time. The actuator keeps a durable selected-installation record and routes
worker.ensure, turn.execute, and turn.stop only to that selected
profile. Selecting a worker in another profile explicitly transfers ownership
and revokes the prior profile; disconnecting the selected profile leaves the
worker offline rather than falling through to a different personal browser.
This makes it practical to keep separate work, test, and personal profiles
connected without accidentally sending a coding request to the wrong tab.
After the initial selection, TopHat persists both the selected extension installation and the selected conversation URL. It can reopen that thread when the same Firefox profile reconnects. The current implementation is a Linux/Firefox private-beta adapter: do not treat it as a public service or a replacement for an official API. Each user must operate their own logged-in account, connector authorization, and browser session.
Waking a ChatGPT worker
A connector exposes tools to a ChatGPT turn; it does not wake a dormant turn.
For unattended use, run a local controller (PTY or browser driver) that long-polls
GET /internal/jobs/wait?model=<alias>, sends one exact-job worker prompt when it
receives a queued notice, and keeps that browser turn owned until the broker reaches
completed, failed, or cancelled. Prompt acceptance is not job completion. The
endpoint is loopback-only and can additionally require TOPHAT_INTERNAL_TOKEN.
Each wake prompt tells ChatGPT to claim one exact expected_job_id, so a stale or
duplicate wake cannot consume another queued request. A lease is authenticated by
worker_id + job_id + lease_token, so claim and submit may use separate transient
MCP connections. Browser liveness renews a short lease, always capped by the hard
job deadline; a retry is refused when too little deadline budget remains.
The actuator is a separate process: npm run dev:actuator (development) or
npm run start:actuator after building. In extension mode it owns queue wakes
and retries; the Firefox extension owns narrowly scoped ChatGPT-tab interaction.
playwright remains a development fallback. tophat on starts the actuator only
in job-broker mode and reports inference as ready only after a browser worker
is connected and selected.
MCP worker tools
claim_inference_job— out:idle{retry_after_ms}|assigned{job_id, lease_token, lease_expires_at, request}.submit_inference_result— in:job_id,lease_token, result payload.fail_inference_job— in:job_id,lease_token, error.heartbeat_inference_worker— keep-alive.
submit/fail require job_id + lease_token, preventing stale, duplicate,
cross-worker, or corrupt submissions (conditional UPDATE … WHERE lease_token_hash=? AND lease_expires_at>? AND deadline_at>? — 0 rows affected = race lost).
Run it
cp .env.example .env
npm install
npm run build # tscMock backend (in-process scripted worker, no tunnel, no ChatGPT):
TOPHAT_BACKEND=mock npm run dev:httpReal backend (ChatGPT over tunnel):
# set TOPHAT_BACKEND=job-broker (default) and aliases in .env, then:
/home/bamn/bin/tophat on # boots gateway (tmux: tophat) + tunnel (tmux: tophat-tunnel)Launch with tophat (one command)
/home/bamn/bin/tophat is a launch controller that boots the stack in tmux and
gates the tunnel daemon on gateway readiness:
tophat on # gateway (tmux: tophat) + OpenAI tunnel (tmux: tophat-tunnel), backend from .env
tophat mock # gateway in mock mode (no tunnel)
tophat status # gateway /health, tunnel readiness, ChatGPT discovery check
tophat off # tears both daemons down
tophat logs # attach to the gateway tmux sessiontophat on prints the inference proxy endpoint + a curl snippet:
=== inference proxy (point your agent here) ===
base_url: http://127.0.0.1:6767/v1
curl http://127.0.0.1:6767/v1/chat/completions \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${TOPHAT_BEARER_TOKEN}" \
-d '{"model":"chatgpt-5.6-sol-high","messages":[{"role":"user","content":"hi"}],"stream":false}'The tunnel daemon needs CONTROL_PLANE_API_KEY (an OpenAI organization runtime
key from platform.openai.com/settings/organization/api-keys) in the environment.
The tunnel profile (~/.config/tunnel-client/tophat_mcp.yaml) references it via
api_key: "env:CONTROL_PLANE_API_KEY" and never contains the credential itself.
The key must own (or have access to) the configured tunnel_id, in the same
OpenAI organization/project context. For multi-org accounts, also set
CONTROL_PLANE_ORGANIZATION_ID. Export it before tophat on:
export CONTROL_PLANE_API_KEY=<org runtime key>
# export CONTROL_PLANE_ORGANIZATION_ID=<org-id> # only for multi-org accounts
tophat onThe launcher passes the key to the tunnel session through tmux's environment mechanism, never as a command-line argument.
Create/verify the ChatGPT connector at chatgpt.com/#settings/Connectors while the
tunnel daemon is running.
OpenAI-compatible surface
{
"base_url": "http://127.0.0.1:6767/v1",
"api_key": "<TOPHAT_BEARER_TOKEN, unset in mock mode>",
"model": "chatgpt-5.6-sol-high"
}Endpoints:
POST /v1/chat/completions # stream: true → SSE; false → single JSON
GET /v1/models
GET /health
GET /ready?model=… # browser-actuator admission readinessTool-call round trip (the gateway does not execute tools):
sequenceDiagram
participant Agent as Coding Agent
participant Tophat as TophatMCP (Job Broker)
participant Worker as ChatGPT MCP Worker
Agent->>Tophat: POST /v1 (tools declared)
Tophat->>Tophat: enqueue InferenceJob, block HTTP
Worker->>Tophat: claim_inference_job (pending)
Tophat-->>Worker: assigned{request + tool defs}
Worker->>Tophat: submit_inference_result(tool_calls)
Tophat-->>Agent: HTTP 200 OpenAI tool_calls
Note over Agent: Executes tools locally
Agent->>Tophat: POST /v1 (role: tool result)
Tophat->>Worker: claim resolves with next job
Worker->>Tophat: submit_inference_result(final text)
Tophat-->>Agent: HTTP 200 final contentAuthentication
Two modes (TOPHAT_AUTH_MODE):
oauth(default): MCP SDK OAuth with an Owner-password approval page. The OpenAI surface is gated byTOPHAT_BEARER_TOKEN(constant-time compare).tunnel: OAuth disabled on/mcp; access is delegated to a reverse tunnel and guarded byTOPHAT_TUNNEL_TOKEN.HOSTmust be a loopback address.
Tests
npm run test:broker runs src/test-job-broker.ts — self-contained broker
integration tests covering atomic exact-job claims, deadline-capped leases, hard
deadline enforcement, typed waiter rejection, cancellation, tool-call validation,
and startup recovery. npm run test:actuator and the extension-driver suites cover
browser-turn ownership, bounded wake retries, and extension bridge routing.
Production requirements
Keep the HTTP server bound to localhost unless remote access is intentional.
Require authentication for remote access.
Treat the tunnel token / control-plane key as credentials; never log them.
Maintain a separate MCP session per worker connection; idle sessions are reaped.
A browser wake remains owned until the authoritative broker job is terminal; do not infer completion from DOM activity or prompt acceptance.
Hard job deadlines win over leases. Retries require a configured minimum remaining budget and terminally fail once their wake budget is exhausted.
Submit/fail are guarded by lease + deadline; queued/leased rows are recovered to
failed(server_restarted)on startup (no durable continuation advertised until the HTTP client has a resume protocol).
This 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-qualityCmaintenanceAn experimental guarded MCP bridge for Codex/open-ultrawork workflows, enabling submission of bounded research jobs to ChatGPT Pro asynchronously with source verification and safety guards.MIT
- AlicenseAqualityAmaintenanceCentralized encrypted gateway that routes requests to 11+ LLM providers (API keys and CLI subscriptions) through a single OpenAI-compatible endpoint, with MCP tools for vault operations, code search, and shared state.30151MIT
- Alicense-qualityBmaintenanceLocal MCP bridge enabling ChatGPT web to access approved local files and execute tasks via local Codex.1MIT
- Alicense-qualityCmaintenanceA security-hardened MCP gateway that enables AI agents to call LLM APIs (Gemini, OpenAI, Claude, etc.) using ephemeral proxy tokens, eliminating exposure of real API keys.406Apache 2.0
Related MCP Connectors
A paid remote MCP for OpenAI Codex agent coordination MCP, built to return verdicts, receipts, usage
A paid remote MCP for OpenAI Codex memory MCP, built to return verdicts, receipts, usage logs, and a
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
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/B-A-M-N/TopHatMCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server