Every tool on both servers, what each argument means, and what each status is asking you to do.
MCP reference
Two servers, one product. They are not interchangeable, and the difference is the whole reason the tool names are what they are.
Remote https://app.solospace.dev/mcp | Local stdio | |
|---|---|---|
| Auth | OAuth 2.1, no API key | ed25519 agent key |
| Has a browser | No | Yes, its own Chromium |
| Can sign bytes | Yes | Yes, via the service |
| Can complete a sign-in alone | No — you drive the page | Yes, in one call |
Remote tools
passkey_howto
The end-to-end procedure: the shim to inject, the object shape to hand back to the page, what every refusal means, and your connectors' operating notes. Optional domain focuses the connector section on one site.
Needs no scope — only a valid token. Gating the manual behind a scope produces exactly the failure it exists to prevent: an agent refused the instructions for the thing it was just granted permission to do.
passkey_list — connectors:read
The connector catalogue, already narrowed to what this grant covers. Per connector:
| Field | Meaning |
|---|---|
notes | Prose about that site's flow. Returned first, because it is the field that decides whether the sign-in works. |
siteNotes | What is generally true of this site, present when a human also wrote a connector note. Both are returned: a note saying where the button is does not retract one saying the browser gets blocked. |
notesSource | connector (a human wrote it for this credential — trust it), default (a general note for the site — verify it), none (nobody has written one). |
rpId | The relying party — the domain the passkey belongs to. |
credentialId | base64url. The value passkey_assert takes. |
userName | Which identity this passkey signs in as. |
signinUrl | Where to start. |
origins | The origins a human observed at enrolment. An assertion for any other origin is refused. |
originAdvice | Whether signinUrl starts on an enrolled origin, in words. The sign-in URL is where you start; the ceremony origin is wherever the site has navigated to when it asks for the passkey, and only the second one is checked. |
mode | approval or silent. |
lastUsedAt | The last signature the service allowed for this connector. |
A site that is missing was not granted. No rephrasing produces it; the person changes it on their dashboard.
passkey_connector_status — connectors:read
What the service has recorded about one connector: its mode, its notes, its last successful signature, and whether a signature is waiting on a human *right now*. Use it before raising a second approval.
It cannot tell you whether your browser has a live session. Nothing on the server can see your browser.
passkey_assert — signin
The product. Takes credentialId, clientDataJSON (base64url, byte-for-byte as the page produced it) and origin. Returns one of:
status | Meaning | What to do |
|---|---|---|
signed | authData, signature, userHandle are in the result | Hand them to the page. Each assertion is good once. |
waiting_for_approval | A human is being asked, on their phone, right now | Sleep retryAfterMs, then call again with byte-identical arguments — at most once per interval — until signed, denied, or the approval expires (~90s). |
denied | Terminal. The result carries a code: approval_denied, approval_timeout, policy_denied, origin_rejected, not_found | Stop. Report it. Do not retry. |
Pass `clientDataJSON` through untouched. The shim builds those bytes inside the page and the page keeps them to hand to the site; the agent's job is to move the base64url string, not to re-create it. Do not decode and re-encode it, reformat the JSON or reorder its keys. The service hashes what it is sent and the site hashes what the page holds; one differing byte produces a signature that verifies against nothing, after every other gate has passed.
The `origin` is the page's, not the connector's. A site may navigate to another host before it asks for a passkey — start at signinUrl, but send whatever origin the ceremony is actually running on. It must be one of the connector's enrolled origins, and originAdvice says whether those two agree. Scheme and host only: no path and no trailing slash, even though signinUrl usually has one.
The credentialId and domain parameters are enumerations built from your grant, per request. A credential you were not granted is not offered in the schema at all — and is refused by the server as "no such credential" if you send it anyway, indistinguishably from one that does not exist.
passkey_approvals — approvals:read
The approvals this connection has raised that are still open, with the deadline on each. It approves nothing and hurries nobody.
Local tools
passkey_howto
The short local procedure — three calls, in order, and how to read each status.
passkey_list
The same catalogue, over the ed25519 agent key.
passkey_session_status(domain)
Does this agent browser already have a live session? Opens the sign-in URL and reports where it landed. The answer is *inferred*: landing away from a login page is good evidence of a session, not proof of one.
passkey_signin(domain)
The whole ceremony in one call. Statuses: already_signed_in, signed_in, waiting_for_approval, denied, no_assertion (the page never asked for a passkey — read the notes and click the right button first), failed.
About the name passkey_status
It no longer exists on either server. It used to exist on both, answering different questions: locally "is this browser signed in", remotely "what has the service recorded". An agent connected to both saw one name whose meaning depended on which registration won — wrong silently, never with an error. It is now passkey_session_status (local, a question about a browser) and passkey_connector_status (remote, a question about the service's records). No alias is kept: an alias would preserve the collision it exists to fix, and unknown tool is a loud failure a client corrects in one turn.