Client Intake for OpenAI Codex: Collect Assets Without Leaving the Terminal
Codex can scaffold a booking flow in one session, then wait days for a client's gallery, price list, and booking-tool login to show up by email.
The problem
A photography studio hires you to rebuild their booking site. Codex gets the structure, the calendar widget, and the pricing page laid out fast — then stalls, because the site needs a real portfolio gallery, an actual price list, and admin access to whatever booking tool they already use, and none of that exists in the chat. You ask the client for it. They reply two days later with six phone photos pasted into an email and a screenshot of a spreadsheet. The booking-tool password never comes, because nobody wants to type a password into an email thread, and they'd be right not to.
The gap is not Codex's coding speed. It's the distance between "the site needs 30 to 40 portfolio photos, a price list, and a login" and having those three things in a form Codex can actually use.
The solution
BriefGate MCP gives Codex a tool for this instead of a chat message. Codex calls define_intake() with a typed list of what the project needs. BriefGate emails the client a branded portal link, validates uploads as they come in, and chases the client automatically on a schedule you pick. When everything is in, Codex calls get_intake_results() and gets back files, structured data, and one-time credential reveals — no re-asking, no guessing which photo in the thread is the final one.
Adding BriefGate to Codex
Codex reads MCP server configuration from ~/.codex/config.toml (or a project-scoped .codex/config.toml in a trusted project). The Codex CLI, the IDE extension, and the ChatGPT desktop app all share this same file, so a server you add once from the terminal is available in all three.
Option A — local package, added from the CLI
codex mcp add briefgate --env BRIEFGATE_API_KEY=bg_live_xxxxx -- npx -y @briefgate/mcpThis writes a [mcp_servers.briefgate] table to config.toml:
[mcp_servers.briefgate]
command = "npx"
args = ["-y", "@briefgate/mcp"]
[mcp_servers.briefgate.env]
BRIEFGATE_API_KEY = "bg_live_xxxxx"Get the key from https://app.briefgate.dev/app. Prefer not to paste one into a config file at all? Add the server without --env, then call BriefGate's own login tool from inside a Codex session — it prints a short code and opens the dashboard in your browser, and once approved the key is stored in ~/.briefgate/credentials.json for you. This is a separate step from anything Codex itself runs; it's a tool the BriefGate MCP server exposes, documented in mcp.md.
Option B — hosted endpoint, OAuth
BriefGate also runs a hosted Streamable HTTP endpoint with OAuth 2.1, and Codex's own MCP client supports both transports and OAuth natively — a url field instead of command/args, and a dedicated login step:
codex mcp add briefgate --url https://mcp.briefgate.dev/mcp
codex mcp login briefgateThe first command registers the endpoint; the second starts Codex's own OAuth flow for it, opening a browser to sign in and approve access. This mirrors how any Streamable HTTP MCP server with OAuth connects to Codex — it isn't a BriefGate-specific integration, just the general mechanism, documented on OpenAI's Codex MCP page.
One caveat worth knowing before you plan around it: local config.toml is shared by the CLI, the IDE extension, and the ChatGPT desktop app, but not by ChatGPT's web interface — the web client reaches MCP-backed tools through installed plugins instead of reading your local config, so a server you add from the terminal will not automatically appear there.
Either option exposes the same 13 tools: define_intake, add_items, update_item, update_intake, list_intakes, get_intake_status, get_intake_results, request_revision, send_chase, manage_recipients, manage_webhook, list_folders, and create_folder.
Telling Codex when to use it
Codex reads project instructions from AGENTS.md. Add a section so it reaches for BriefGate instead of asking in chat:
## Client asset collection
When the project needs a file, image, credential, or piece of copy from the client:
1. Call define_intake with items[] describing exactly what's needed
2. The client is emailed a portal link automatically — never ask for it in chat
3. Keep building whatever doesn't depend on the missing items
4. Check get_intake_status, or register a webhook with manage_webhook, instead of
waiting idle for a reply
5. Call get_intake_results once complete
6. Use type: "secret" for any password or API key — never plain textA worked example
Agent: "The booking calendar and pricing layout are done. To finish the site I need the portfolio gallery, the current price list, and access to the booking tool you're already using. Setting up a BriefGate intake."
Codex calls define_intake:
{
"project_name": "Lucia Ferraro Photography — Booking Site",
"client": {
"email": "[email protected]",
"name": "Lucia",
"language": "en"
},
"items": [
{
"key": "gallery",
"type": "file_list",
"label": "Portfolio gallery",
"help": "30 to 40 of your best shots, full resolution as exported from your camera or editing software — not resized for Instagram. We'll use the sharpest ones as the homepage hero.",
"required": true,
"constraints": { "formats": ["jpg", "png"], "min_count": 30, "max_count": 40 }
},
{
"key": "price_list",
"type": "file",
"label": "Current price list",
"required": true,
"constraints": { "formats": ["pdf"] }
},
{
"key": "booking_tool_login",
"type": "secret",
"label": "Booking tool admin login",
"help": "Username and password for the scheduling system you use today, so we can pull your existing packages and availability rules.",
"required": true
}
],
"chase_schedule": "default"
}Agent: "Intake sent to Lucia. I'll keep building the gallery grid and pricing template against placeholder content until these come in."
Note the honest limit in the gallery item: file_list accepts a format list and a min/max count, but — unlike the single-upload image type — it has no minimum-width constraint the portal can enforce. The 40-image ask is capped and validated; "full resolution" is guidance in the help text the client reads, not something BriefGate rejects a low-resolution file over. See item-types.md for what each type actually checks.
Resuming across sessions
Codex sessions, like most terminal agent sessions, don't persist a running memory of what's still outstanding once you close the terminal. Note the intake_id somewhere it'll survive — a comment near the code that depends on it, or a line in AGENTS.md for the project — and check get_intake_status(intake_id: "in_...") at the start of the next session before assuming nothing arrived, or calling define_intake again and sending Lucia a second link.
Handling the results
Once the gallery, price list, and login are all in, Codex calls get_intake_results:
{
"status": "completed",
"results": {
"gallery": [
{ "filename": "portrait-01.jpg", "url": "https://files.briefgate.dev/.../portrait-01.jpg?token=...", "width": 4032, "height": 3024 }
],
"price_list": { "filename": "ferraro-photo-pricing-2026.pdf", "url": "https://files.briefgate.dev/.../price_list.pdf?token=..." },
"booking_tool_login": { "value": "[email protected]:MyPassword123", "one_time": true, "first_reveal": true }
}
}The secret's value field is present only on this first call — store it in a secrets manager immediately, per secrets.md, because a later get_intake_results call won't return it again. If a photo in the gallery turns out to be a low-res export after all, request_revision(intake_id, "gallery", note) flags it without a new email thread — see mcp.md for the full call.
Next steps
| Topic | Document |
|---|---|
| Full reference for all 13 MCP tools | mcp.md |
| All item types and their constraints | item-types.md |
| Connecting other agents | Get client assets into your coding agent |