orkasa

The connector

Connect an assistant

Everything is self-service: nobody at Orkasa registers the broker's app.

Add the connector

In Claude: Settings → Connectors → Add custom connector, and paste https://orkasa.app/api/mcp. In other clients, the same URL wherever they ask for an MCP server address.

Authorize in Orkasa

Orkasa opens, you sign in and approve the permissions. Each one can be unchecked.

Done

The assistant now sees the tools that match the granted permissions, and no others.

What happens underneath

1. The client POSTs /api/mcp           → 401 + WWW-Authenticate:
                                          resource_metadata="…"
2. GET /.well-known/oauth-protected-resource     (RFC 9728)
                                       → authorization_servers: [orkasa.app]
3. GET /.well-known/oauth-authorization-server   (RFC 8414)
                                       → authorize / token / register
4. POST /api/oauth/register            (RFC 7591)  → client_id [+ secret]
5. GET  /oauth/authorize               → the broker signs in and approves
6. POST /api/oauth/token               → access + refresh
7. POST /api/mcp  (Bearer ork_at_…)    → tools/list, tools/call

Steps 2 to 4 are what makes this self-service. Without them, an OAuth client would have to be created by hand for every broker — which is to say, there would be no connector.

Open registration is safe

POST /api/oauth/register is not authenticated. It cannot be: the client announcing itself has no identity with us yet.

A registration grants nothing. It creates an app identity with no brokerage, no token and no data behind it. Every byte it may one day read is decided afterwards, by a signed-in human, on /oauth/authorize, inside their own tenant. The abuse surface is rows in a table, not access — and the limits are cut to that measure:

  • 10 registrations per hour per IP;
  • redirect_uris over https, except loopback (RFC 8252 §7.3: a desktop app receives its code on http://127.0.0.1:<port>);
  • permissions capped to the connector's ceiling, whatever the client asks for;
  • dynamically registered implies mandatory PKCE, as for any public client.

The name is chosen by a stranger

The authorization screen says "X wants to connect", and that name was chosen by whoever registered. So the screen shows the real destination, not the URL the app declares:

Name chosen by the application itself, unverified. Your data is going to evil.example. Authorize only if you recognize that destination and you started this connection.

Showing the declared client_uri as proof of provenance turned the warning against us: registering with client_uri: "https://orkasa.app" was enough to make the screen display "· orkasa.app" under an invented name. The redirect_uri, by contrast, is checked character by character against the client's whitelist — it is the one thing on that screen a phisher cannot dress up.

And it can be withdrawn

The Conexiones card in Orkasa's settings lists the assistants with live access, who authorized it and when, with a Disconnect button. Without it, an authorization given by mistake was good for sixty days — the life of the refresh token.

Desktop clients

A desktop assistant has nowhere to hide a secret. It registers with token_endpoint_auth_method: "none", gets a client_id alone, and what protects the exchange is PKCE — S256 and nothing else.

A code_challenge with no method is rejected: OAuth's historical default is plain, which compares the verifier to the challenge in the clear, and that challenge already travelled inside the authorization URL (so through browser history and logs). The metadata only advertises S256, and the server no longer does anything else.

The token endpoint decides the rule from the registration, never from what the request carries: a confidential client cannot pass itself off as public by omitting its secret.