Connect an assistant
Everything in the API reference is available to an AI assistant over the Model Context Protocol — one tool per operation, running the very same handlers, under the very same rules. No key is pasted anywhere: the connection is its own credential, granted per client, narrowed to the scopes you tick, and revocable on its own.
The URL
Give any MCP client this one address; it discovers everything else itself:
https://drop.top/api/mcp
Connecting from claude.ai
- Open Settings → Connectors.
- Choose Add custom connector and paste the URL above.
- Claude opens the authorization flow in a browser window. If you are signed in here, the consent screen appears immediately; if not, you sign in first — the assistant never sees your password, only the outcome.
- No account yet? Make one there and then. The sign-in page's Create an account link carries the connection request with it, so signing up drops you back exactly where you were rather than on the console with a half-finished handshake behind you. If this is a brand-new account, the consent screen asks one question first — what to call your workspace — because the connection has to act on something.
- Review the consent screen (next section), tick what you actually want to grant, and approve.
- Back in Claude, the connector lists its tools. Ask it to “list my channels” to see it work.
Disconnecting is yours at both ends: remove the connector in Claude, or — the end that actually revokes the grant — open the studio's API page, find it under Connected assistants, and disconnect. The next connection has to ask you again.
What you approve
The consent screen is not a yes/no dialog. It lists every capability the connection could have as a
checkbox row — the same scopes an API key carries, described in plain words
(“Send alerts that can ring phones”, not alerts:send jargon alone). Untick anything and
the grant is minted without it: an assistant connected for drafting can be given
alerts:write and denied alerts:send, and every send it ever
attempts will be refused with a 403 naming the missing scope — until you reconnect and approve it.
If your account can act for more than one workspace, the same screen carries a workspace picker: the grant binds to the workspace you choose there, and only roles that may hold it are offered. An assistant connected to one workspace cannot wander into another — connecting it to a second one is a second consent.
Two things are not on the screen because they are not on offer: an assistant can never create or read API keys
(a token that could mint sk_live_ keys would escape its own revocable grant), and the
subscriber API is not exposed (that is the handset's surface, not yours).
How discovery works
The whole handshake is standard OAuth 2.1, which is why no client id or key is ever typed in: the client calls
/api/mcp unauthenticated and gets a 401 whose
WWW-Authenticate header points at the protected-resource metadata
(/.well-known/oauth-protected-resource); that names the authorization server,
whose own metadata advertises dynamic client registration (RFC 7591); the client registers itself, sends
you to authorize, you land on the consent screen above, and approval yields the token the
client uses from then on. Every step is the client's to figure out — your part is the one screen.
The tools
20 tools, one per operation. Each requires the scope beside it — a grant without that
scope gets a refusal that names it. send_alert and cancel_alert
are the REST PATCH action's two halves, split into their own tools so the irreversible verbs are unmistakably
separate calls.
| Tool | Scope | What it does |
|---|---|---|
get_account |
account:read |
Your creator profile |
list_webhooks |
webhooks:read |
List endpoints |
create_webhook |
webhooks:write |
Register an endpoint |
delete_webhook |
webhooks:write |
Remove an endpoint |
list_pushers |
channels:read |
List your channels |
create_pusher |
channels:write |
Create a channel |
get_pusher |
channels:read |
Fetch one channel |
update_pusher |
channels:write |
Update a channel |
delete_pusher |
channels:write |
Delete a channel |
list_invites |
channels:read |
The ways in to a private channel |
create_invite |
channels:write |
Mint an invite link |
revoke_invite |
channels:write |
Revoke an invite link |
list_alerts |
alerts:read |
List a channel's alerts |
create_alert |
alerts:write |
Compose an alert — draft, schedule or send |
get_alert |
alerts:read |
Fetch one alert |
update_alert |
alerts:write |
Edit, send or cancel an alert |
send_alert |
alerts:send |
Send a draft now, or send a scheduled alert early. Irreversible — an alert cannot be unsent. |
cancel_alert |
alerts:send |
Withdraw an alert: stops it reaching phones that have not polled; phones that have it keep it. |
delete_alert |
alerts:write |
Delete a draft or scheduled alert |
get_stats |
analytics:read |
Account-level statistics |
Public or invite-only, never guessed
A channel is one of two things, and they are not variations of each other. A public channel is listed in
search and anyone who finds drop.top/<handle> can subscribe. A private
one is sealed, not merely unlisted: it is absent from search, and knowing the handle admits nobody —
only a phone holding an invite code can subscribe or read a single alert the channel has ever sent, and to
everyone else that handle answers exactly as a handle that was never created.
Over the REST API, is_public defaults to true. Over MCP it
has no default at all: create_pusher refuses to run without it. That difference
is deliberate. A script omitting a field is its author accepting a documented default; a model omitting one is a
guess nobody made — and the wrong guess means a school's or an ops team's channel was listed in public search
and joinable by strangers for as long as it existed. Turning it private afterwards removes none of them.
So expect an assistant to ask you which one, in plain words, and to say back what it is about to create. Create it private and a first invite link is minted with it and returned in the same reply — a private channel with no way in would be one nobody, including you, could ever join. That link, code and all, is what goes on the poster or into the QR code.
Drafts first, sends deliberately
The MCP server tells every connected model the same thing, and it is worth repeating to the person steering one:
an alert cannot be unsent. Cancelling stops it reaching phones that have not polled yet and does nothing
at all to the phones that already have it. So compose with send: false unless you have
clearly asked to publish, and expect a well-behaved assistant to confirm with you before calling
send_alert. If you want that enforced rather than trusted, deny
alerts:send at the consent screen — the drafts pile up, and the sending stays a human's
button.
And what an alert does on a phone is a request, not a command: a pusher asks; the phone decides. The alert anatomy page has the whole story.