Add the server
Codex reads an MCP configuration like every other client. Over HTTP:
{
"mcpServers": {
"opentree": {
"type": "http",
"url": "https://read.botook.ai/mcp",
"headers": { "Authorization": "Bearer otr_live_YOUR_KEY" }
}
}
}Or as a local stdio process, which is the shape the OpenAI CLI prefers by default:
[mcp_servers.opentree]
command = "npx"
args = ["-y", "opentree-mcp"]
env = { OPENTREE_API_KEY = "otr_live_YOUR_KEY" }Both transports expose the identical twelve tools against the identical account — stdio is a thin bridge onto the same HTTP surface. Install it once:
npx -y opentree-mcpPublish from the session
publish_html({
content: "<!doctype html><title>Q3 review</title>…",
expires_in_hours: 168,
password: "optional",
agentation: true
})The reply contains the unlisted URL. Nobody needs an account to open it, and nothing about it is public: no listing, no index, no guessable id.
Where Codex Sites stops
Codex can already produce and preview an artifact. What it does not give you is a link with policy attached — a password, an expiry, a work-email gate, a hostname that is yours. That is the gap this fills, and it is the same gap described on the comparison page.
Access rules as arguments
| Field | Effect |
|---|---|
password | A passcode in front of the page, checked before any content is served. |
allowed_email_domain | Only addresses at that work domain may open it, confirmed by a one-time email. |
expires_in_hours | Deletion time. never keeps the link open. |
burn_after_read | The first successful open is the last one. |
pii_check | block by default: sensitive content refuses to publish. |
Update, do not republish
Codex loops. Replace the content behind the existing token rather than minting a new link every iteration:
update_site({ site_id: "…", content: "<!doctype html>…" })The same thing without the MCP layer
If the sandbox can run curl, it can publish — no server registration involved:
curl -X POST https://read.botook.ai/sites \
-H "Authorization: Bearer otr_live_YOUR_KEY" \
-F file=@index.htmlAnd with no account at all:
curl -X POST https://read.botook.ai/v1/publish -F file=@index.htmlSharing across a team
One page can carry several links. Mint a share token per audience and revoke them independently, so cutting off a contractor does not disturb the client:
curl -X POST https://read.botook.ai/share-tokens \
-H "Authorization: Bearer otr_live_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"site_id":"SITE_ID","label":"design-review","max_views":25}'Reading the page back
A second agent often needs the content, not the markup. /raw/:token answers
plain text:
curl https://read.botook.ai/raw/UNLISTED_TOKENWhen the sandbox has no network
Codex often runs with egress switched off, and then no amount of configuration will let the tool call leave the box. The honest fix is to move the publish one step outward: have the session write the file to the workspace and let the host publish it, either by hand or from the same task runner that already has credentials.
# inside the sandbox: just write the file
# dist/report.html
# on the host, afterwards:
curl -X POST https://read.botook.ai/sites \
-H "Authorization: Bearer otr_live_YOUR_KEY" \
-F file=@dist/report.htmlWhat Codex can hand over
Not everything a coding session produces is HTML, and none of it needs converting first:
| Shape | How to send it | What is stored |
|---|---|---|
| HTML | -F file=@page.html | The bytes, as written |
| Markdown | -H 'content-type: text/markdown' | Rendered to a self-contained document at publish time |
| SVG | -F file=@diagram.svg | The bytes, served as an image |
| A build tree | -F file=@site.zip | Unpacked, entry at index.html |
What the reviewer sees
A rendered page in a browser, with no account. Turn agentation on and they can
leave a note pinned to the paragraph they disagree with; turn reactions on and they get
a one-click emoji row instead. Both are off by default, because an internal build artifact usually
should not invite comments.
Reading the result back
The session that published a page can ask what happened to it. Two calls cover it:
curl -s https://read.botook.ai/sites/SITE_ID/engagement -H "Authorization: Bearer otr_live_YOUR_KEY"
curl -s https://read.botook.ai/sites/SITE_ID/feedback -H "Authorization: Bearer otr_live_YOUR_KEY"Engagement is aggregate: opens, unique viewers, dwell, scroll depth, read-to-end. Feedback is the actual text a human left, with the anchor it was attached to.
Tidying up a loop's output
A loop that publishes on every iteration leaves a trail. List and delete, or let the default seven-day expiry do it for you:
curl -s https://read.botook.ai/sites -H "Authorization: Bearer otr_live_YOUR_KEY" | jq '.sites[].id'
curl -X DELETE https://read.botook.ai/sites/SITE_ID -H "Authorization: Bearer otr_live_YOUR_KEY"Questions
Does this work from ChatGPT rather than the CLI?
If the workspace can reach an HTTP MCP
server, yes — point it at https://read.botook.ai/mcp. Otherwise use the
connector-style OAuth flow or plain HTTP.
Is the link public?
No. The token is 22 random characters and the response carries
noindex. Public sharing is something you opt into by handing the link over.
Can I keep the artifact forever?
Pass expires_in_hours: "never". There is no
ceiling on this deployment — see pricing.