OpenTree DashboardSign in

$ cat .cursor/mcp.json

Publish from Cursor without leaving the editor

Cursor writes the page, calls the tool, and hands you a private link in the same thread. One config entry, one restart, no deploy step.

Install

Cursor reads MCP servers from .cursor/mcp.json in the project, or from the global file in your home directory. Add the entry:

{
  "mcpServers": {
    "opentree": {
      "type": "http",
      "url": "https://read.botook.ai/mcp",
      "headers": { "Authorization": "Bearer otr_live_YOUR_KEY" }
    }
  }
}

Then restart the editor — Cursor loads servers at startup, and you will be prompted to trust the new one the first time it is called. Confirm it registered under Settings → MCP; the server should list twelve tools.

A local process instead of an HTTP header works too:

npx -y opentree-mcp

Get the key first

Keys live in the dashboard, carry the otr_live_ prefix and are displayed once. Revoke one without disturbing the others:

curl -X POST https://read.botook.ai/api-keys \
  -H "Authorization: Bearer otr_live_EXISTING_KEY" \
  -H "content-type: application/json" -d '{"name":"cursor"}'

Publish what the editor just wrote

publish_html({
  content: "<!doctype html><title>Q3 review</title>…",
  expires_in_hours: 168,
  password: "optional",
  agentation: true
})

Ask in the chat: publish this as a private link that expires in two days. The agent calls the tool, the reply carries the URL, and the file never leaves the editor as an attachment.

Cursor rarely gets a page right the first time. update_site replaces the content behind the existing entry, so the address stays stable across every revision:

update_site({ site_id: "…", content: "<!doctype html>…" })

Publish the whole build

cd dist && zip -r ../site.zip . && cd ..
curl -X POST https://read.botook.ai/sites \
  -H "Authorization: Bearer otr_live_YOUR_KEY" -F file=@site.zip

The archive is unpacked on the way in and served as a tree, with index.html as the entry point.

Put a rule in the project

So the model reaches for the right tool without being told twice, drop this in .cursor/rules/publish.mdc:

---
description: How to share a page with someone outside the repo
alwaysApply: false
---
When asked to share a page, call publish_html on the opentree server.
Never paste HTML into the chat as a fallback. Set expires_in_hours to 168
unless told otherwise, and report only the returned URL.

Access before it leaves your machine

set_password({ site_id: "…", password: "hunter2" })
set_email_gate({ site_id: "…", allowed_email_domain: "client.com" })

An email gate sends a one-time link to a work address and refuses everything else — the right default when the audience is a client rather than a teammate.

Feedback comes back into the editor

Enable the on-page toolbar and the comments a reviewer leaves are readable as structured input on the next turn:

list_feedback({ site_id: "…", unresolved_only: true })

Without the server

curl -X POST https://read.botook.ai/sites \
  -H "Authorization: Bearer otr_live_YOUR_KEY" \
  -F file=@index.html

More than HTML comes out of an editor

A repository is full of things worth sending to someone who will never clone it — a README, an architecture diagram, a coverage report. Each has a direct route in:

In the repoSent asServed as
NOTES.mdcontent-type: text/markdownA rendered, self-contained document
docs/arch.svg-F file=@docs/arch.svgThe image, byte-for-byte
coverage/-F file=@coverage.zipThe tree, entry at index.html
dist/index.html-F file=@dist/index.htmlThe page, as built
curl -X POST https://read.botook.ai/sites \
  -H "Authorization: Bearer otr_live_YOUR_KEY" \
  -H "content-type: text/markdown" \
  --data-binary @README.md

What the recipient actually gets

One URL, one click, no account, no download, no file:// that renders differently on their machine than on yours. With agentation enabled there is a toolbar for notes; with reactions there is an emoji row. Paste the link into a chat app and the unfurl shows a screenshot of the page — except on a gated page, where it is a placeholder card on purpose.

Reading engagement back into the editor

The same key that published can ask how the page was read, without leaving the terminal pane:

curl -s https://read.botook.ai/sites/SITE_ID/engagement \
  -H "Authorization: Bearer otr_live_YOUR_KEY" | jq '{opens,unique_viewers,read_to_end}'

Aggregate counters only — opens, unique_viewers, dwell, ten scroll buckets, read_to_end. Nothing per-visitor, nothing replayable.

Housekeeping

Editor sessions generate drafts fast. List what this key owns and delete the ones that are done; the rest expire by themselves after seven days:

curl -s https://read.botook.ai/sites -H "Authorization: Bearer otr_live_YOUR_KEY" | jq '.sites[] | {id,title}'
curl -X DELETE https://read.botook.ai/sites/SITE_ID -H "Authorization: Bearer otr_live_YOUR_KEY"

Questions

The tools do not show up.

Cursor reads the config at startup. Restart, then check Settings → MCP. A 401 in the tool output means the key is wrong or revoked.

Can I use one key across machines?

You can, but a key per client is easier to revoke. Mint one for the laptop and one for CI.

Does this replace my host?

No. It hosts documents and single-file tools. Applications with a server side still deploy normally — see the use cases.

Keep reading