workspace-agent-relay-mcp
A local relay and dashboard that enables ChatGPT Workspace Agents to stream real-time updates—plan, progress, tool calls, and results—back to the user's machine via MCP and SSE.
README
workspace-agent-relay-mcp
A local relay + dashboard that lets a ChatGPT Workspace Agent report its plan, progress, tool calls, questions, and final result back to your machine — in real time, while it works.

The left side is the relay dashboard: the user's message, the agent's plan checklist (all steps done), the tool calls it ran locally (write_file, run_command with durations), and the final result. The right side is the ChatGPT Workspace Agent itself — which the local operator otherwise cannot see. The relay mirrors the agent's work into a live, readable view.
Why this exists: the Workspace Agent trigger API is fire-and-forget. You POST a task and get a
202 Acceptedwith no body and no way to retrieve the agent's answer. So an agent running in ChatGPT has no built-in channel to show you what it's doing or hand you a result. This relay gives it that channel: a tiny MCP server the agent calls back into, plus a live dashboard you watch.
How it fits together
Three pieces, two of them on your machine:
you (browser) your machine ChatGPT cloud
┌────────────┐ ┌──────────────────────────┐ ┌──────────────────┐
│ dashboard │◀── │ workspace-agent-relay │ ◀──MCP─ │ Workspace Agent │
│ :8799 │ │ (MCP + web + SSE) │ calls │ (triggered runs)│
└────────────┘ └─────────────┬────────────┘ └──────────────────┘
│ HTTP /internal/tool-trace
▲
│ fire-and-forget trace per tool call
┌────────┴─────────────┐
│ notion-local-ops-mcp │ ← the agent's "hands"
│ (file/git/shell tools)│ (separate repo, optional)
└──────────────────────┘
- workspace-agent-relay-mcp (this repo): the MCP server the agent writes into, the dashboard you read from, and the SSE bus that pushes updates live.
- notion-local-ops-mcp (separate repo, optional): the agent's working tools — files, git, shell. When paired with this relay, every tool call the agent makes there is auto-mirrored here as a trace, so you watch it work without the agent manually reporting anything.
- ChatGPT Workspace Agent: the cloud brain. Triggered by this relay, calls back into both MCPs.
What the agent sees (MCP tools)
Six tools, all narrow on purpose — no shell, no arbitrary file access, no secrets:
| Tool | When the agent calls it |
|---|---|
record_plan |
At the start of a run, with its step plan (stable ids + titles). |
record_progress |
After a few steps, batch-updating step statuses + an optional one-line note. |
record_result |
Once at the end, with status (done/blocked/failed), title, and full Markdown. |
ask_user |
Only when genuinely blocked on a human decision. |
get_run_context |
To recover the current run's summary if it loses context. |
server_info |
Introspection — relay URL, auth mode, registered tools. |
What you see (dashboard)
Open http://127.0.0.1:8799/ in a browser. For each run, in reading order:
-
Your message (what you sent the agent).
-
Plan checklist — the steps the agent committed to, with live
in_progress / done / skippedstates.
-
Tool calls — a collapsible list of tool-call traces auto-mirrored from the agent's working MCP (apply_patch, git_commit, run_command, …), each with args, result summary, and duration. Failed calls are flagged red.
-
Notes — the agent's own one-line progress narrations (only when it has something worth saying).
-
Question — if the agent asked you something via
ask_user. -
Result — the final Markdown deliverable.
Everything streams in over SSE while the agent works — you don't refresh.
Quick start
1. Install
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env
2. Configure .env
# Protects /api/* and /mcp. Generate with: openssl rand -hex 32
WORKSPACE_AGENT_RELAY_AUTH_TOKEN=replace-me
# From your ChatGPT Workspace Agent settings:
WORKSPACE_AGENT_RELAY_TRIGGER_URL=https://api.chatgpt.com/v1/workspace_agents/agtch_your_id/trigger
WORKSPACE_AGENT_RELAY_AGENT_TOKEN=your-workspace-agent-access-token
Two tokens, two jobs:
| Variable | Protects | Lives in |
|---|---|---|
WORKSPACE_AGENT_RELAY_AUTH_TOKEN |
/api/* and /mcp |
.env and the dashboard "Relay API Token" field |
WORKSPACE_AGENT_RELAY_AGENT_TOKEN |
Outbound trigger calls to ChatGPT | .env only — never the browser |
3. Run
workspace-agent-relay-mcp # serves MCP + dashboard on 127.0.0.1:8799
Or behind a tunnel:
./scripts/dev-tunnel.sh # uses cloudflared if cloudflared.local.yml exists, else a quick tunnel
4. Configure the ChatGPT Workspace Agent
This is the part that turns the relay from a blank dashboard into a live one. Three things to set in ChatGPT: the trigger, the MCP connector, and the Instructions.
a. Create the Workspace Agent and copy its trigger credentials
- In ChatGPT, create (or open) a Workspace Agent.
- Copy its Trigger URL (
https://api.chatgpt.com/v1/workspace_agents/agtch_…/trigger) intoWORKSPACE_AGENT_RELAY_TRIGGER_URL. - Copy its access token into
WORKSPACE_AGENT_RELAY_AGENT_TOKEN(server-side only — never paste this into the browser dashboard).
The relay uses these to POST new tasks to the agent when you hit Send in the dashboard.
b. Add the relay as an MCP connector
In the Workspace Agent's MCP connectors section, add:
| Field | Value |
|---|---|
| URL | http://127.0.0.1:8799/mcp locally, or your tunnel URL https://<your-host>/mcp |
| Auth type | Bearer |
| Token | the value of WORKSPACE_AGENT_RELAY_AUTH_TOKEN |
After connecting, confirm the tool list includes record_plan, record_progress, record_result, ask_user, get_run_context, server_info. If a tool is missing, reconnect the MCP so ChatGPT re-fetches tools/list.
c. Paste the collaboration Instructions
Open docs/agent-instructions.md, copy the fenced block under "指令正文", and paste it into the Agent's Instructions field (append to, or replace, any existing relay section). This tells the agent the workflow it must follow on every run:
record_plan → bind_relay_run → batch record_progress → record_result
Without this, the agent will silently do work in ChatGPT that the dashboard can't see.
d. (Optional, for live tool traces) Connect notion-local-ops-mcp too
If you want the Tool calls panel to stream the agent's real file/git/shell actions, also connect notion-local-ops-mcp as a second MCP connector and paste its bind_relay_run step — see Pairing with notion-local-ops-mcp below.
5. Smoke test
- Start the relay, open
http://127.0.0.1:8799/, paste the auth token. - Send a short task from the dashboard (e.g. "create /tmp/hello.txt with one line, then read it back").
- Watch the plan checklist appear, tool traces stream in, then the final result.
Pairing with notion-local-ops-mcp
This relay only exposes reporting tools (plan/progress/result) — it deliberately has no shell, file, or git access. To let the agent actually do work and have those actions show up live in the dashboard, pair it with its sibling project:
notion-local-ops-mcp — a local MCP that gives the agent file reads/writes, patch editing, git, shell, and delegated tasks. It lives in a separate repo and works standalone; the relay integration is opt-in.
What the pairing enables
On its own, this relay shows the agent's plan + progress + result. With notion-local-ops-mcp paired, it additionally shows every tool call the agent makes (write_file, apply_patch, git_commit, run_command, …) streaming in live — without the agent manually reporting any of them.
How they talk
ChatGPT Agent ──MCP──▶ notion-local-ops-mcp ──POST /internal/tool-trace──▶ this relay ──SSE──▶ dashboard
- The agent calls
record_planon this relay (the plan shows up). - The agent calls
bind_relay_runon notion-local-ops-mcp, passing therequest_id+callback_tokenfrom the trigger. It does not pass a relay URL — that's configured on the notion-local-ops side viaNOTION_LOCAL_OPS_RELAY_URL(defaulthttp://127.0.0.1:8799). - From then on, every
@tracedtool the agent runs on notion-local-ops fires a fire-and-forget trace POST to this relay. - This relay stores each trace as a progress event and pushes it over SSE to the dashboard.
The internal endpoint authenticates with the per-run callback_token in the body (not the dashboard bearer), and a closed/terminal run rejects traces with 409. The relay being unreachable never blocks the agent's tool execution — traces are best-effort.
Setup on the notion-local-ops side
See notion-local-ops-mcp → Relay Bridge for its env knobs (NOTION_LOCAL_OPS_RELAY_URL, NOTION_LOCAL_OPS_RELAY_BRIDGE_ENABLED, NOTION_LOCAL_OPS_RELAY_BRIDGE_TIMEOUT). Default values point at this relay's default port, so a same-machine install works with no extra config.
Project layout
src/workspace_agent_relay_mcp/
server.py # FastMCP server + the six agent-facing tools + global instructions
app.py # Starlette app: routes, middleware, SSE event bus
api/routes/ # /api/agents, /api/conversations, /api/runs (SSE), /internal/tool-trace
store/relay_store.py # SQLite layer: runs, events, plans, artifacts, redaction
trigger.py # Builds the input_text sent to the ChatGPT trigger API
config.py # Env-driven config
oauth.py # Optional OAuth mode for ChatGPT web developer mode
frontend/ # React + Vite dashboard (TypeScript, TanStack Query, SSE)
scripts/dev-tunnel.sh # supervisor + cloudflared rolling-reload launcher
docs/ # design specs + agent instructions
tests/ # pytest suite
Trigger semantics (the short version)
conversation_key— the stable continuation key for a thread. Reuse it to keep one conversation.request_id— per-run trace key, echoed in every callback.idempotency_key— per-message retry key. New logical message → new key.conversation_url— saved as human-readable metadata only; not the continuation key.
The dashboard shows both the current conversation_key and the latest conversation_url so you don't accidentally fork a thread by reusing the wrong one.
Security notes
.env,cloudflared.local.yml, and*.sqlite*are git-ignored — keep them out of commits.- The per-run
callback_tokenis stored only as a hash; it's redacted from logs and API responses. - Use a high-entropy
WORKSPACE_AGENT_RELAY_AUTH_TOKEN. - Rotate any Workspace Agent access token that ever leaks into chat or logs.
- Debug MCP logging redacts common token/secret/authorization/key fields before writing summaries.
Status
Early, single-user, local-first. Not a product. Built to learn and prototype the Workspace Agent callback gap.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.