Add the connector
- Open Settings → Connectors in Claude.ai and choose to add a custom connector.
- Paste the server URL:
https://read.botook.ai/mcp. - Approve the consent screen. The connector registers itself dynamically — there is no client id for you to create by hand.
- The tools show up in the conversation. Ask for a page, then ask for the link.
What happens during that handshake
An unauthenticated call answers 401 with a WWW-Authenticate header
pointing at the authorization server. The client follows it, reads the discovery document,
registers, and runs an authorization-code flow with PKCE:
curl -s https://read.botook.ai/.well-known/oauth-protected-resource | jq .
curl -s https://read.botook.ai/.well-known/oauth-authorization-server | jq .Registration, authorization, token exchange and revocation:
| Step | Endpoint |
|---|---|
| Dynamic client registration | POST /oauth/register |
| Authorization (PKCE required) | GET /oauth/authorize |
| Token exchange | POST /oauth/token |
| Revocation | POST /oauth/revoke |
Tokens are signed HS256 and live 30 days. Revoking a grant kills the refresh
token with it, so a revoked connector cannot quietly trade its way back in.
Why a connector rather than a key
- Nothing secret is pasted into a chat surface you do not control.
- Consent is explicit and visible, and you can withdraw it from one screen.
- The connector registers itself, so a rotated deployment does not need you to reissue anything.
What you can do in the conversation
Everything the other clients can do — the tool list is not reduced for hosted clients:
| Tool | What it does |
|---|---|
publish_html | Publish a document and get back an unlisted URL. |
update_site | Replace the content behind an existing URL, in place. |
get_site | Read one site's settings, counters and share URL. |
list_sites | List everything the calling key owns. |
delete_site | Hard-delete the record and the stored bytes. |
set_password | Put a passphrase in front of the page, or clear it. |
set_expiry | Move the expiry, or pin the link open forever. |
set_email_gate | Restrict opens to one work-email domain. |
Plus list_feedback, resolve_feedback, set_agentation
and link_wallet. The full table is in the docs.
Publish an artifact you already have
Ask Claude to publish the artifact from the conversation. It generates the document and calls:
publish_html({
content: "<!doctype html><title>Q3 review</title>…",
expires_in_hours: 168,
password: "optional",
agentation: true
})The result is an unlisted link that opens in any browser with no account — more on artifacts.
Revoking access
curl -X POST https://read.botook.ai/oauth/revoke -d token=THE_TOKENOr remove the connector in settings, which does the same thing from the other end. Sites published through it are unaffected — they belong to your account, not to the client.
If your client is not Claude.ai
Any client that speaks MCP over Streamable HTTP can use the same flow; nothing here is specific to one vendor. Clients you configure yourself are usually simpler with a bearer key:
claude mcp add --transport http opentree https://read.botook.ai/mcp \
--header "Authorization: Bearer otr_live_YOUR_KEY"How long a grant lasts
An access token is valid for 30 days and is signed HS256; the refresh token that
comes with it is what keeps a long-lived connector working without asking you again. Revocation
covers both — a revoked grant cannot trade a refresh token for a fresh access token, which is the
difference between real revocation and merely shortening a token's life.
| Artifact | Lifetime | Killed by |
|---|---|---|
| Access token | 30 days | POST /oauth/revoke, or removing the connector |
| Refresh token | Until the grant is revoked | The same call — it is checked against the deny list on every refresh |
| Authorization code | Single use, minutes | Being redeemed, or expiring |
Publishing something other than a page
Conversations produce more than HTML. A long answer is markdown; a diagram is usually SVG; an exported project is a zip. All three publish through the same tool — the format is inferred from what you hand over, and markdown is rendered into a self-contained document at publish time rather than each time somebody reads it.
What the reader gets
An unlisted link that opens with no account. If the conversation asked for the toolbar, the
reader can leave a note anchored to a paragraph, and you can read those back in the same chat with
list_feedback. Pasted into a chat app, the link unfurls with a screenshot of the page;
a gated page unfurls as a placeholder instead, so the preview never becomes a way around the gate.
Asking the conversation what happened
Because the connector holds your account, the follow-up question works in plain language:
which of those pages did anyone actually open? Underneath it is get_site and the
engagement endpoint — opens, unique viewers, dwell, scroll depth, read-to-end, all aggregate.
When the handshake goes wrong
| Symptom | Likely cause | What to check |
|---|---|---|
| Connector never finishes adding | Discovery document unreachable | GET /.well-known/oauth-authorization-server should return JSON, unauthenticated |
| Tools missing after consent | Client cached a failed registration | Remove the connector and add it again — registration is dynamic, so nothing is left behind |
Every call returns 401 | Grant revoked or expired | Re-approve from settings; grants last 30 days |
| Consent screen loops | PKCE challenge mismatch | Ensure the client is not stripping query parameters on the redirect |
Questions
Does the connector see my other sites?
It acts as your account, so yes — that is what makes
list_sites useful. Scope it down by using a separate account, or use an API key
minted for one purpose.
Is there an install step?
No. npx -y opentree-mcp exists for local stdio clients;
a connector needs only the URL.
What if the handshake fails?
Check that https://read.botook.ai/mcp is reachable and that
the discovery documents return JSON. Both are public and unauthenticated by design.