OpenTree DashboardSign in

$ oauth authorize

A custom connector for Claude.ai

Paste one URL into your connector settings. Claude registers itself, performs the OAuth handshake, and the twelve tools appear in the conversation — no API key ever gets pasted into a chat window.

Add the connector

  1. Open Settings → Connectors in Claude.ai and choose to add a custom connector.
  2. Paste the server URL: https://read.botook.ai/mcp.
  3. Approve the consent screen. The connector registers itself dynamically — there is no client id for you to create by hand.
  4. 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:

StepEndpoint
Dynamic client registrationPOST /oauth/register
Authorization (PKCE required)GET /oauth/authorize
Token exchangePOST /oauth/token
RevocationPOST /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

What you can do in the conversation

Everything the other clients can do — the tool list is not reduced for hosted clients:

ToolWhat it does
publish_htmlPublish a document and get back an unlisted URL.
update_siteReplace the content behind an existing URL, in place.
get_siteRead one site's settings, counters and share URL.
list_sitesList everything the calling key owns.
delete_siteHard-delete the record and the stored bytes.
set_passwordPut a passphrase in front of the page, or clear it.
set_expiryMove the expiry, or pin the link open forever.
set_email_gateRestrict 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_TOKEN

Or 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.

ArtifactLifetimeKilled by
Access token30 daysPOST /oauth/revoke, or removing the connector
Refresh tokenUntil the grant is revokedThe same call — it is checked against the deny list on every refresh
Authorization codeSingle use, minutesBeing 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

SymptomLikely causeWhat to check
Connector never finishes addingDiscovery document unreachableGET /.well-known/oauth-authorization-server should return JSON, unauthenticated
Tools missing after consentClient cached a failed registrationRemove the connector and add it again — registration is dynamic, so nothing is left behind
Every call returns 401Grant revoked or expiredRe-approve from settings; grants last 30 days
Consent screen loopsPKCE challenge mismatchEnsure 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.

Keep reading