Run a desktop session
You want a ScaiGrid agent to call MCP tools running on a user's machine — files, shell, git, a domain tool — with the user explicitly approving each new touchpoint. This tutorial walks through the WebSocket handshake, the capability registration, and the invocation/consent loop.
In practice the official ScaiLink desktop client does this for you. The walkthrough below is for developers building a new client or integrating ScaiLink into an existing desktop product.
1. Discover the auth endpoint#
ScaiLink's first endpoint is unauthenticated and tells the client where to send the user for OAuth:
1 | |
The response includes authorization_endpoint, device_authorization_endpoint, token_endpoint, the JWKS URI, supported scopes (openid profile email groups), and gateway_ws: "/v1/scailink/ws". Run the standard OAuth device-code flow against ScaiKey to mint a user JWT, then proceed.
2. Open the WebSocket and send session_init#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
1 2 3 4 | |
The first frame after the handshake must be scailink/session_init. Anything else closes the socket with code 4002.
3. Heartbeats and reconnection#
The session_init_ack tells you heartbeat_interval_ms (default 30000) and grace_period_ms (default 120000). Send a scailink/heartbeat notification every interval. If the WebSocket drops, you have the grace window to reconnect and resume — re-open the socket, re-send session_init with the same device name and platform, and the server resumes the existing session id rather than creating a new one.
4. Update the catalog mid-session#
When the local MCP server set changes — a new server starts, a tool is removed — send scailink/catalog_update with added and removed rather than reconnecting:
1 2 3 4 5 6 7 8 9 10 | |
5. Handle consent prompts#
When an invocation hits a tool that isn't auto-approved, ScaiLink sends a scailink/consent_request frame. Surface it in your client UI — show the user what the agent wants to do, ask for approve/deny, then reply:
1 2 3 4 5 6 | |
decision is approved or denied. Approval can be one-shot, or with a TTL if your UI offers "approve for the next hour" — the server-side policy handles the rest.
6. Invoke from the server side#
Once the client is connected, REST callers in ScaiGrid can invoke registered tools. The tenant must allow scailink:invoke:
1 2 3 4 | |
The REST call blocks until the desktop client replies (or the timeout fires). Behind the scenes the gateway pushes scailink/tool_invoke over the WebSocket and waits for the matching response by id.
7. Verify in the audit log#
Every invocation lands in the audit log with the action, target name, status, duration, and the device that served it:
1 2 | |
The detail level was set on the original session_init. metadata (default) records names and arguments but not result content; full records everything; off keeps only the action skeleton.
Common gotchas#
- First frame must be session_init. A heartbeat or catalog update sent first closes the socket with code 4002.
- Device id is derived from device_name + platform. Two clients with the same name on the same OS map to the same
device_id; pick distinctive names. - Grace period is 120 seconds. If you reconnect later than that you start a fresh session and the catalog has to be re-sent.
- JWT scope. The JWT must include the
groupsclaim so the server can resolve ScaiGrid permissions.