mandala-computer-mcp 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +277 -20
  2. package/dist/api.d.ts +73 -2
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +271 -22
  5. package/dist/api.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +17 -0
  8. package/dist/cli.js.map +1 -1
  9. package/dist/errors.d.ts +183 -5
  10. package/dist/errors.d.ts.map +1 -1
  11. package/dist/errors.js +377 -10
  12. package/dist/errors.js.map +1 -1
  13. package/dist/format.d.ts +90 -0
  14. package/dist/format.d.ts.map +1 -1
  15. package/dist/format.js +102 -2
  16. package/dist/format.js.map +1 -1
  17. package/dist/http.d.ts +42 -0
  18. package/dist/http.d.ts.map +1 -1
  19. package/dist/http.js +392 -6
  20. package/dist/http.js.map +1 -1
  21. package/dist/index.d.ts +4 -3
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/paths.d.ts +74 -3
  26. package/dist/paths.d.ts.map +1 -1
  27. package/dist/paths.js +89 -10
  28. package/dist/paths.js.map +1 -1
  29. package/dist/secret-errors.d.ts +59 -0
  30. package/dist/secret-errors.d.ts.map +1 -0
  31. package/dist/secret-errors.js +199 -0
  32. package/dist/secret-errors.js.map +1 -0
  33. package/dist/secret-store.d.ts +115 -0
  34. package/dist/secret-store.d.ts.map +1 -0
  35. package/dist/secret-store.js +105 -0
  36. package/dist/secret-store.js.map +1 -0
  37. package/dist/server.d.ts +1 -1
  38. package/dist/server.d.ts.map +1 -1
  39. package/dist/server.js +3 -1
  40. package/dist/server.js.map +1 -1
  41. package/dist/session.d.ts +6 -1
  42. package/dist/session.d.ts.map +1 -1
  43. package/dist/session.js +1 -1
  44. package/dist/session.js.map +1 -1
  45. package/dist/tool-filters.d.ts +4 -4
  46. package/dist/tool-filters.d.ts.map +1 -1
  47. package/dist/tool-filters.js +22 -2
  48. package/dist/tool-filters.js.map +1 -1
  49. package/dist/tools/account.d.ts +14 -0
  50. package/dist/tools/account.d.ts.map +1 -1
  51. package/dist/tools/account.js +118 -0
  52. package/dist/tools/account.js.map +1 -1
  53. package/dist/tools/agent.d.ts.map +1 -1
  54. package/dist/tools/agent.js +7 -2
  55. package/dist/tools/agent.js.map +1 -1
  56. package/dist/tools/computers.d.ts.map +1 -1
  57. package/dist/tools/computers.js +277 -57
  58. package/dist/tools/computers.js.map +1 -1
  59. package/dist/tools/guest.d.ts.map +1 -1
  60. package/dist/tools/guest.js +140 -22
  61. package/dist/tools/guest.js.map +1 -1
  62. package/dist/tools/input.d.ts +11 -0
  63. package/dist/tools/input.d.ts.map +1 -1
  64. package/dist/tools/input.js +277 -20
  65. package/dist/tools/input.js.map +1 -1
  66. package/dist/tools/operations.d.ts +44 -0
  67. package/dist/tools/operations.d.ts.map +1 -0
  68. package/dist/tools/operations.js +232 -0
  69. package/dist/tools/operations.js.map +1 -0
  70. package/dist/tools/secrets.d.ts +42 -13
  71. package/dist/tools/secrets.d.ts.map +1 -1
  72. package/dist/tools/secrets.js +306 -15
  73. package/dist/tools/secrets.js.map +1 -1
  74. package/dist/tools/snapshots.d.ts.map +1 -1
  75. package/dist/tools/snapshots.js +44 -15
  76. package/dist/tools/snapshots.js.map +1 -1
  77. package/dist/tools/ssh.d.ts.map +1 -1
  78. package/dist/tools/ssh.js +19 -2
  79. package/dist/tools/ssh.js.map +1 -1
  80. package/dist/tools/templates.js +1 -1
  81. package/dist/tools/templates.js.map +1 -1
  82. package/dist/tools/webhooks.js +1 -1
  83. package/dist/tools/webhooks.js.map +1 -1
  84. package/package.json +1 -1
package/README.md CHANGED
@@ -145,16 +145,29 @@ Parameter and response-mode support remains a separate contract.
145
145
 
146
146
  **Lifecycle** — `create_computer`, `start_computer`, `stop_computer`,
147
147
  `suspend_computer`, `restart_computer`, `update_computer`, `clone_computer`,
148
- `delete_computer`, `move_computer`, `list_moves`
148
+ `delete_computer`, `move_computer`, `list_moves`, `get_operation`,
149
+ `list_operations`, `wait_for_operation` — see [Lifecycle operations](#lifecycle-operations)
149
150
 
150
- **Driving the desktop** — `screenshot`, `click`, `type_text`, `press_key`,
151
- `scroll`, `drag`, `move_mouse`, `mouse_button`, `cursor_position`, `wait`
151
+ **Driving the desktop** — `screenshot`, `click`, `type_text`, `paste_text`,
152
+ `press_key`, `scroll`, `drag`, `move_mouse`, `mouse_button`, `cursor_position`,
153
+ `wait`
152
154
 
153
155
  **Inside the guest** — `exec`, `exec_poll`, `exec_kill`, `get_execution`,
154
156
  `read_execution_output`, `open_url`,
155
157
  `list_windows`, `window_action`, `read_clipboard`, `write_clipboard`,
156
158
  `read_file`, `write_file`, `list_directory`
157
159
 
160
+ `write_file` replaces a file already at the path. With `overwrite: false` it
161
+ creates the file only if nothing is there, and a path that is taken is refused
162
+ without that attempt writing anything (Linux computers only). Incomplete
163
+ contents are never published at the path, but a failure while publishing or
164
+ answering can leave the complete file there, so after any error read the path
165
+ before retrying or overwriting. `read_file` and `write_file` take `no_wake: true`
166
+ to refuse (409) rather than resume a computer that is not running. For
167
+ embedders, the `Api` raises that refusal as `FileExistsError`, and a
168
+ create-only 409 whose reason could not be read as `CreateOnlyConflictError`,
169
+ which says nothing about the path; `isTransient` is false for both.
170
+
158
171
  **Retained versions** — `retain_execution_output`, `get_result`, `read_result_output`,
159
172
  `delete_result`, `publish_artifact`, `get_artifact`, `read_artifact`, `delete_artifact`
160
173
 
@@ -174,6 +187,10 @@ Parameter and response-mode support remains a separate contract.
174
187
 
175
188
  **Account quota** — `get_account`
176
189
 
190
+ **Who you are** — `whoami`, `list_api_keys`, `list_workspaces`,
191
+ `get_workspace`, `list_workspace_members`. Minting and revoking API keys are
192
+ deliberately not tools — see [Who you are, and API keys](#who-you-are-and-api-keys).
193
+
177
194
  **Spending** — `get_usage`
178
195
 
179
196
  **Being told somewhere else** — `list_webhooks`, `create_webhook`,
@@ -186,14 +203,56 @@ was issued to, not to the account, and are accepted by every computer with SSH
186
203
  on, on every account where that person is an owner or member. A
187
204
  workspace-scoped key can read keys but not add or remove them.
188
205
 
189
- **Secret bindings** — `get_computer_secrets`, `set_computer_secrets`, and
190
- `create_computer`'s `secrets`. Which of the account's secrets a computer
191
- receives, at which revision, and where: as an environment variable (`env`) or as
192
- a file under `/run/mandala-secrets/user/files` (`file`). Values never cross these
193
- tools. A set replaces the whole list (`[]` removes every binding) and reaches the
194
- guest at the computer's next start or restart; send the `version` a read
195
- answered to have it refused with 409 if the list changed since. A secret bound
196
- as a file is also rewritten on a running computer when its value is replaced.
206
+ **Secrets** — `list_secrets`, `get_secret`, `create_secret`, `set_secret`,
207
+ `replace_secret`, `delete_secret` for the account's secret store, and
208
+ `get_computer_secrets`, `set_computer_secrets` and `create_computer`'s `secrets`
209
+ for which of them a computer receives. A value goes in through `create_secret`,
210
+ `set_secret` (create the name, or replace its value if the scope holds it —
211
+ names match ignoring ASCII case) or `replace_secret`, and never comes back out. No route answers one, and a store tool's result
212
+ holds only the decoded documented fields (success) or the status, the `reason`
213
+ word and a sentence of its own (refusal). It never includes the platform's
214
+ response text, and the `Api` keeps none for these routes. The bindings carry
215
+ only secret ids, revisions and names. A computer receives a secret as an environment variable (`env`) or as a
216
+ file under `/run/mandala-secrets/user/files` (`file`). A set replaces the whole
217
+ binding list (`[]` removes every binding) and reaches the guest at the
218
+ computer's next start or restart; send the `version` a read answered to have it
219
+ refused with 409 if the list changed since. A replaced value also reaches a
220
+ running computer: a file binding's file is rewritten in place, and on an image
221
+ that supports it an env binding reaches new shells and `exec` with
222
+ `desktop: true` (programs already running keep the old value until a restart).
223
+ A command that needs a bound variable should use `desktop: true`. Whether a
224
+ plain root `exec` sees it depends on the platform version. `replace_secret`
225
+ and `delete_secret` need the current `revision_id`, and a stale one is a 409.
226
+ `delete_secret` also needs `confirm: true`: a computer still bound to a deleted
227
+ secret cannot start again until that binding is removed. A bound computer runs,
228
+ and its guest answers, a few seconds before its secrets land, so
229
+ `wait_for_computer(until="guest")` on one also waits until they have;
230
+ `get_computer` shows the gap as "its secrets are still on their way in".
231
+
232
+ **A proxy for the browsers** — `browser_proxy: {server, bypass}` on
233
+ `create_computer` and `update_computer` sends a Linux computer's browsers
234
+ (Chromium, Chrome, Firefox) through a proxy; nothing else on the computer uses
235
+ it. On `update_computer` it goes alone, replaces the setting whole, and `null`
236
+ removes it. Which proxies are accepted is the platform's rule, and its refusal
237
+ comes back as it is. A running computer has a change within seconds:
238
+ `wait_for_computer(until="guest")` waits until its browsers have it, and
239
+ `get_computer` shows the gap as "its browser proxy is still being applied".
240
+
241
+ **A proxy for all outbound traffic** — `egress_proxy: {server,
242
+ credentials_secret_id}` on `create_computer` and `update_computer` sends ALL of
243
+ a computer's outbound TCP (`exec`, terminals, package managers and browsers
244
+ alike) through a proxy, taken on its host so nothing inside the computer can
245
+ opt out. The server is `http://`, `https://` or `socks5://` with an explicit
246
+ port; there is no bypass list. `credentials_secret_id` names a secret holding
247
+ `user:password` that is not bound to the computer and never reaches it: the
248
+ computer's host signs in to the proxy with it. It fails closed (proxy down or
249
+ refusing, or credentials not on the host yet: the connection fails, nothing goes
250
+ direct), drops UDP to the internet and ICMP, and does not proxy DNS lookups.
251
+ On `update_computer` it goes alone — beside any other field it is refused
252
+ before a request is sent — replaces the setting whole, and `null` removes it.
253
+ `get_computer` names the proxy and, while the host waits for its credentials,
254
+ says "its egress proxy is waiting for credentials; connections are closed until
255
+ they arrive".
197
256
 
198
257
  **Delegating** — `run_agent`, `run_agent_chat`, registered only when a model key is present:
199
258
  `MANDALA_MODEL_KEY` on stdio, or the caller's own `X-Model-Key` header over HTTP.
@@ -218,10 +277,10 @@ with an error listing all valid tags.
218
277
 
219
278
  | Tag | Tools |
220
279
  | --- | --- |
221
- | `account` | `get_account` |
280
+ | `account` | `get_account`, `whoami`, `list_api_keys`, `list_workspaces`, `get_workspace`, `list_workspace_members` |
222
281
  | `computers` | `list_computers`, `get_computer`, `use_computer`, `wait_for_computer`, `get_desktop_url`, `list_sizes` |
223
- | `lifecycle` | `create_computer`, `start_computer`, `stop_computer`, `suspend_computer`, `restart_computer`, `update_computer`, `clone_computer`, `delete_computer`, `move_computer`, `list_moves` |
224
- | `input` | `screenshot`, `click`, `type_text`, `press_key`, `scroll`, `drag`, `move_mouse`, `mouse_button`, `cursor_position`, `wait` |
282
+ | `lifecycle` | `create_computer`, `start_computer`, `stop_computer`, `suspend_computer`, `restart_computer`, `update_computer`, `clone_computer`, `delete_computer`, `move_computer`, `list_moves`, `get_operation`, `list_operations`, `wait_for_operation` |
283
+ | `input` | `screenshot`, `click`, `type_text`, `paste_text`, `press_key`, `scroll`, `drag`, `move_mouse`, `mouse_button`, `cursor_position`, `wait` |
225
284
  | `guest` | `exec`, `exec_poll`, `exec_kill`, `open_url`, `list_windows`, `window_action`, `read_clipboard`, `write_clipboard` |
226
285
  | `files` | `list_directory`, `read_file`, `write_file`, `wait_for_file_change` |
227
286
  | `executions` | `get_execution`, `read_execution_output` |
@@ -233,7 +292,7 @@ with an error listing all valid tags.
233
292
  | `usage` | `get_usage` |
234
293
  | `webhooks` | All webhook tools listed above |
235
294
  | `ssh` | All SSH tools listed above |
236
- | `secrets` | `get_computer_secrets`, `set_computer_secrets` |
295
+ | `secrets` | `list_secrets`, `get_secret`, `create_secret`, `set_secret`, `replace_secret`, `delete_secret`, `get_computer_secrets`, `set_computer_secrets` |
237
296
  | `agent` | `run_agent`, `run_agent_chat` |
238
297
  | `activities` | `list_activities`, `get_activity`, `get_activity_results` |
239
298
  | `signals` | `read_signals` |
@@ -256,6 +315,108 @@ to both transports and the plugin forwards them. Embedders can pass
256
315
  `readOnly: true` and `tags: ['input', 'guest']` in `ServerConfig`; the server
257
316
  does not read the environment itself.
258
317
 
318
+ ### Lifecycle operations
319
+
320
+ Every accepted create, clone, start, stop, suspend, restart, snapshot restore,
321
+ resize, move and delete records a lifecycle operation, and the tool that made the call
322
+ says its `operation_id` — in its sentence, as `(operation op_…)`, and in the
323
+ JSON. `move_computer` carries it onto the outcome it reports. It is absent where
324
+ the platform could not record one; the call happened either way.
325
+
326
+ `get_operation` reads one, and `list_operations` pages through them newest
327
+ first (`computer_id`, `limit`, `cursor`, `idempotency_key`; `computer_id` is not
328
+ defaulted to the selected computer, and `idempotency_key` finds the operation a
329
+ call sent with that key recorded, even when the call's answer was lost). `wait_for_operation` polls one until it is final: it
330
+ answers on `succeeded`, and a `failed` one is an error carrying the platform's
331
+ `error.code` (`start_failed`, `build_failed`, `computer_gone`, `move_failed`,
332
+ `resize_not_applied`, `lost`, and more may be added) and its sentence.
333
+
334
+ `succeeded` means the platform finished its step, not that the desktop has
335
+ booted: `wait_for_computer` is still the wait for a desktop that answers. Most
336
+ operations are already `succeeded` when their tool answers; a clone is
337
+ `running` until its disk is copied, and a move until it lands. All three are
338
+ reads, and all three stay registered when the lifecycle tools are withheld,
339
+ since start, stop, suspend and restart record operations too.
340
+
341
+ The eleven lifecycle tools (`create_computer`, `clone_computer`,
342
+ `start_computer`, `stop_computer`, `suspend_computer`, `restart_computer`,
343
+ `update_computer`, `move_computer`, `delete_computer`, `restore_snapshot`,
344
+ `clone_snapshot`) take an optional `idempotency_key`. Leave it out and a fresh
345
+ one is sent for you; a failure whose outcome is unknown names the key it went
346
+ with. What to do next depends on who answered:
347
+
348
+ - **The answer was lost** (the connection dropped after the request went out,
349
+ or a proxy in front of the platform gave up), or the platform answers
350
+ `idempotency_in_progress`: call the same tool again with the same
351
+ `idempotency_key`. The platform answers with the first call's result, or says
352
+ it is still running; the step is not done twice.
353
+ - **The platform answered a `5xx`**, or `idempotency_outcome_unknown`: the key
354
+ is spent, and resending with it only answers `idempotency_outcome_unknown`.
355
+ The platform then keeps the call's operation `pending` for up to an hour,
356
+ whatever happened, while its host may be carrying it out. What to read
357
+ depends on the tool:
358
+ - `start_computer`, `stop_computer`, `suspend_computer`,
359
+ `delete_computer`, `move_computer` and `update_computer`: read
360
+ `get_computer` (and `get_operation` with the `operation_id` the error
361
+ named: `succeeded` means the step happened, `running` means wait on it,
362
+ and `pending` alone is no reason to wait). If the step did not take
363
+ effect, send the call again with a new key, or none.
364
+ - `restart_computer`, which `get_computer` cannot show: a computer reads
365
+ `running` before and after a reset. Read the operation instead
366
+ (`get_operation` with the `operation_id` the error named, or
367
+ `list_operations` with the key): `succeeded` means the restart happened,
368
+ `running` means wait on it. Anything else leaves the restart possibly
369
+ done, so ask the user before sending it again with a new key, or none; a
370
+ second restart resets the guest again.
371
+ - `create_computer`, `clone_computer`, `clone_snapshot` and
372
+ `restore_snapshot`, where a read of the computer made straight away can
373
+ show no effect for a build or restore that then lands, and a second one
374
+ is a second computer or a second overwrite: read the operation first —
375
+ `get_operation` with the `operation_id` the error named, or
376
+ `list_operations` with the key — and while it is `pending` or `running`,
377
+ wait (`wait_for_operation`) and do not resend with any key. Only once it
378
+ is final as `failed` (`error.code` `lost`, for one) or no longer found, AND
379
+ `get_computer` (`list_computers` after a create or a clone) shows the step
380
+ did not happen, send the call again with a new key, or none. If no
381
+ operation is found at all, read the computer again after a few minutes
382
+ before resending.
383
+ - **`idempotency_key_reused`**: a different call already used that key, and
384
+ nothing was done. Send this one with a new key, or none.
385
+
386
+ Keys last 24 hours.
387
+
388
+ ### Who you are, and API keys
389
+
390
+ `whoami` takes no arguments and reads `GET /api/v1/whoami`: the person this
391
+ server's API key was issued to, the account and role it acts with (the role as
392
+ it is now), the workspace it is confined to, and the key itself — including
393
+ `manage_keys`, whether it may manage API keys. It needs no permission and any
394
+ role, and a suspended account can call it; the answer says so when the account
395
+ is suspended. Behind the hosted server's OAuth sign-in, the key is the Connected
396
+ app's (`prefix` `oauth`).
397
+
398
+ `list_api_keys` lists the key holder's API keys on the account, newest first —
399
+ never a raw key. Each carries `minted_by_key_id`: the key that minted it over
400
+ the API, or `null` for one minted from the dashboard. Revoking a key does not
401
+ revoke the keys it minted. It needs this server's key to have the **Manage keys**
402
+ permission, which only a person can turn on, in the dashboard; without it the
403
+ tool answers the platform's 403, whose sentence says exactly that. A Connected
404
+ app's key never has the permission.
405
+
406
+ **Minting and revoking keys are not offered here, on purpose.** A mint answers
407
+ the new key in full, once, and a model's context — the conversation, the
408
+ client's logs, whatever the transcript is shared with — is the worst place for a
409
+ long-lived credential and one nobody can take it back from. A revoke is
410
+ irreversible, and on a model's reading of a list it can cut off the person's CI,
411
+ another agent, or this very session. People do both from the dashboard or with
412
+ the `mandala api-keys` CLI.
413
+
414
+ `list_workspaces`, `get_workspace` and `list_workspace_members` read the
415
+ account's workspaces, which partition its computers. A key confined to a
416
+ workspace lists only that one, and is refused (403) the member list, which is
417
+ the whole account's: workspaces do not divide people. All three are reads;
418
+ workspaces are created, renamed and deleted in the dashboard.
419
+
259
420
  ### Current account quota
260
421
 
261
422
  `get_account` takes no arguments and reads `GET /api/v1/account` once with the
@@ -307,6 +468,12 @@ anything and keep the hint. When a stretch of work is over, `suspend_computer` (
307
468
  suspend catches the ones a model forgets, but only after 30 minutes untouched.
308
469
  `get_usage` is what says what any of it cost.
309
470
 
471
+ **A screenshot need not be the whole screen.** `screenshot` takes `region` (a
472
+ crop, in the screen pixels `click` takes, before any scaling), `scale` (0 to 1),
473
+ `format` (`png` or `jpeg`) and `quality` (JPEG, 1-100). A cropped or scaled
474
+ picture is in its own pixel space, so the tool says alongside it how to turn a
475
+ position in it back into screen coordinates.
476
+
310
477
  **Ten clicks need not be ten screenshots.** Driving the desktop one tool at a
311
478
  time — `screenshot`, `click`, `screenshot` — puts an image in the calling
312
479
  model's context for every step. `run_agent` hands a task in plain language to
@@ -709,9 +876,15 @@ listing leaves it out — every ordinary caller is asking "what can I restore".
709
876
  `list_snapshots(include_unfinished: true)` is the flag for when the question is
710
877
  about storage instead.
711
878
 
712
- **A 409 usually clears; a 400 never does.** A guest still booting or a busy
713
- guest agent answers 409. The platform's own error messages come through
714
- unedited, because they are written to be acted on.
879
+ **Read the `reason`, not the status.** Whether a refusal clears on its own is
880
+ the platform's `reason` word, and each tool's answer says what it means:
881
+ `contention` (something was in flight) and `starting` (the guest agent is still
882
+ inside its boot window) clear, so the same call works in a moment;
883
+ `unavailable`, `running`, `unsupported`, `exists` and `revoked` do not. A 409
884
+ does not mean "wait": `exec`, input and the clipboard on a stopped computer
885
+ answer 409 `unavailable`, and `start_computer` is what fixes that, while
886
+ `running` needs the computer stopped first. The platform's own error messages
887
+ come through unedited, because they are written to be acted on.
715
888
 
716
889
  HTTP failures preserve their actual response status. An unsupported method on a
717
890
  known path is `MethodNotAllowedError` (405), with the received `Allow` value when
@@ -719,6 +892,25 @@ available. A missing computer, snapshot, route or guest file remains
719
892
  `NotFoundError` (404); other guest failures keep their own status and message.
720
893
  Neither response causes an automatic retry or method switch.
721
894
 
895
+ `isTransient(err)` answers "is this worth sending again unchanged". A `503` is
896
+ transient only for a GET or HEAD. Any change answered `503` may or may not have
897
+ happened, because the platform answers a failure after the request was sent the
898
+ same way. So `isTransient` is false for it whatever `reason` it carries, the
899
+ tool's answer says to read the current state first, and `error.method` records
900
+ the method. A `reason` this version does not
901
+ know, such as a new word, is treated as no classification. `reasonKind` returns
902
+ `undefined` for it, and the status decides.
903
+
904
+ The secret store has typed calls on `api.secrets`: `list`, `create`, `get`,
905
+ `replace` and `delete`. Each answer is decoded strictly to the documented
906
+ fields, and none of them carries a value:
907
+
908
+ ```ts
909
+ const { secrets } = await api.secrets.list();
910
+ const s = await api.secrets.create({ name: 'OPENAI_API_KEY', value: key });
911
+ await api.secrets.replace(s.id, { value: next, revisionId: s.revision_id });
912
+ ```
913
+
722
914
  Embedders can inspect optional diagnostics on every `APIError`:
723
915
 
724
916
  ```ts
@@ -746,7 +938,7 @@ and nested chat accounting. `allow` and `wwwAuthenticate` come only from receive
746
938
  headers. HEAD failures can carry these fields with no body. Older servers,
747
939
  intermediaries and connection failures may supply none of them. Existing
748
940
  constructor arguments retain their meanings; an optional trailing
749
- `APIErrorMetadata` object adds these three fields.
941
+ `APIErrorMetadata` object adds these three fields, and `method`.
750
942
 
751
943
  MCP error results include supplied `reason`, `request_id`, `allow`,
752
944
  `www_authenticate` and `retry_after_ms` as labelled JSON metadata. Diagnostic
@@ -895,6 +1087,69 @@ not list it, and every request is refused with a 403. Set
895
1087
  is in force, so a `403` on a deployment that worked before has a line above it
896
1088
  naming the fix.
897
1089
 
1090
+ ### Hosted, with OAuth
1091
+
1092
+ The endpoint at `https://app.mandala.computer/mcp` is this server run with
1093
+ `--http` behind the platform's own proxy, with the platform as its OAuth 2.1
1094
+ authorization server. Nobody pastes a key:
1095
+
1096
+ ```sh
1097
+ claude mcp add --transport http mandala https://app.mandala.computer/mcp
1098
+ ```
1099
+
1100
+ Two variables make an HTTP server behave that way, and without them nothing
1101
+ changes — self-hosters keep bringing API keys:
1102
+
1103
+ ```sh
1104
+ MANDALA_MCP_RESOURCE_METADATA_URL=https://app.mandala.computer/.well-known/oauth-protected-resource/mcp \
1105
+ MANDALA_MCP_SERVICE_SECRET=… \
1106
+ MANDALA_ALLOWED_HOSTS=app.mandala.computer \
1107
+ npx mandala-computer-mcp --http --host 127.0.0.1 --port 3000
1108
+ ```
1109
+
1110
+ In this mode `X-Forwarded-For` is believed from a loopback peer only, so the
1111
+ proxy in front must be on the same machine and must set it to the real client
1112
+ address.
1113
+
1114
+ - **Every `/mcp` request needs a bearer**, initialize and `tools/list`
1115
+ included. One without is answered `401` with exactly
1116
+ `WWW-Authenticate: Bearer resource_metadata="<that URL>", scope="mcp:tools"`,
1117
+ which is how a client finds where to authorize. `/healthz` stays open.
1118
+ - **The bearer is passed through unchanged** — an `mcpat_…` access token, or an
1119
+ API key, which the platform still accepts. The access token is good for this
1120
+ endpoint only. It is not an API key: sent to the API directly, the platform
1121
+ refuses it with `401` (`reason: "invalid"`). A person revokes it under
1122
+ Settings → Connected apps.
1123
+ - **A bearer is checked with the platform before it gets anything.** An
1124
+ initialize makes no session until the platform accepts its token — a `2xx`
1125
+ from `GET ssh-keys`, which every valid credential gets — so invented tokens
1126
+ cannot fill the session pool. A `401` is the challenge above; any other
1127
+ answer is `503` and nothing is remembered. Refused initializes are budgeted
1128
+ per source address, 20 a minute. Past that, a token already accepted still
1129
+ passes and a new one is still checked, but only one every 5 s per address;
1130
+ the rest get `429` with `Retry-After`. The budget comes back when its minute
1131
+ is up. A POST
1132
+ carrying a request is checked again before it is dispatched, with an
1133
+ acceptance cached for 60 s under the token's digest, so an expired or
1134
+ revoked token is a clean `401` before any stream opens.
1135
+ - **A token the platform refuses during a call comes back as that same
1136
+ `401`**, not as a tool error, so the client refreshes or authorizes again —
1137
+ the answer is held until its first byte for this. The one case that cannot
1138
+ work: a token that dies mid-call AFTER the stream has committed (the SDK's
1139
+ 15 s keep-alive, a progress notification or a partial result). That call
1140
+ ends as a tool error, the session remembers the refusal, and the next
1141
+ request on that token gets the `401` without reaching the platform.
1142
+ - **A refreshed token starts a new session.** A session is bound to the digest
1143
+ of the bearer that opened it, and a different bearer on it is answered
1144
+ `404 Unknown session`, the MCP spec's signal to initialize again. An access
1145
+ token says nothing this server can check about which grant it came from, so
1146
+ rebinding would let any valid credential that learned a session id take over
1147
+ its bound computer, buffered events and retained results.
1148
+ - `X-Mandala-MCP-Service` carries `MANDALA_MCP_SERVICE_SECRET` on every
1149
+ platform request, only ever to `MANDALA_BASE_URL`, so a token cannot be
1150
+ replayed at the API directly by the app it was issued to. A client's own
1151
+ header of that name is never forwarded.
1152
+
898
1153
  ### Configuration
899
1154
 
900
1155
  | Variable | Meaning |
@@ -909,8 +1164,10 @@ naming the fix.
909
1164
  | `MANDALA_READ_ONLY` | Keep only tools annotated `readOnlyHint: true`; strict boolean parsing as described under Tool filters. |
910
1165
  | `MANDALA_TAGS` | Comma-separated lowercase tool tags; see Tool filters for the inventory and intersection rules. |
911
1166
  | `MANDALA_ALLOWED_HOSTS`, `MANDALA_ALLOWED_ORIGINS` | Comma-separated. Which `Host` and `Origin` values this server answers to. On a loopback bind the host list defaults to the address it was given, so DNS-rebinding protection is on without configuration; set this when serving under a name. |
1167
+ | `MANDALA_MCP_RESOURCE_METADATA_URL` | `--http` only. The OAuth protected-resource metadata URL this server is published under. Set, it answers OAuth clients as described under Hosted, with OAuth; unset, callers bring an API key. |
1168
+ | `MANDALA_MCP_SERVICE_SECRET` | `--http` only. Sent as `X-Mandala-MCP-Service` on every platform request, to `MANDALA_BASE_URL` only. Never logged. |
912
1169
 
913
- Every one of these but the model key and the two tool filters has a flag as well, and a flag overrides
1170
+ Every one of these but the model key, the two tool filters and the two OAuth settings has a flag as well, and a flag overrides
914
1171
  the environment: `--http`, `--port`, `--host`, `--base-url`, `--computer`,
915
1172
  `--allowed-hosts`, `--allowed-origins`, `--no-lifecycle`, local `--profile`, plus `--help` and
916
1173
  `--version`. `--key` exists for a caller launching several servers under
package/dist/api.d.ts CHANGED
@@ -1,13 +1,44 @@
1
1
  import { Agent } from 'undici';
2
+ import { type SecretStore } from './secret-store.js';
3
+ export { isSecretStoreRoute } from './secret-errors.js';
2
4
  export declare const DEFAULT_BASE_URL = "https://app.mandala.computer/api/v1";
3
5
  /** Anthropic's own key, forwarded for the one route that runs a model. */
4
6
  export declare const MODEL_KEY_HEADER = "X-Model-Key";
7
+ /**
8
+ * The header every lifecycle call carries so that sending it again cannot do
9
+ * it twice (platform OPL-5127). The platform records a call that carries one
10
+ * before carrying it out, and for 24 hours answers the same key with the same
11
+ * request from that record instead of doing the call again: `409` with `code:
12
+ * "idempotency_in_progress"` while it runs, the original answer once it has
13
+ * finished. A different request under the same key is a `422`.
14
+ */
15
+ export declare const IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
16
+ /** The platform's rule for a key: 1 to 255 characters, each printable ASCII other than a space. */
17
+ export declare const IDEMPOTENCY_KEY_PATTERN: RegExp;
18
+ /**
19
+ * The key for ONE lifecycle tool call — the caller's, already checked by the
20
+ * tool's input schema, or a fresh random one — made once, before the request,
21
+ * so that whatever re-sends this call re-sends the same key.
22
+ */
23
+ export declare const idempotencyKeyFor: (given?: string) => string;
24
+ /** The header that carries it. */
25
+ export declare const idempotencyHeaders: (key: string) => Record<string, string>;
26
+ /**
27
+ * Proof, to the platform, that a request comes through the hosted MCP service
28
+ * (OPL-4982). The platform takes an OAuth access token only alongside it, so a
29
+ * token cannot be replayed at the API directly by the app it was issued to.
30
+ *
31
+ * Only this server's own value is ever sent. A header of this name arriving
32
+ * from anywhere else — a caller, or a per-call header set — is dropped in
33
+ * `#fetch`, so a client cannot supply the secret or learn anything by trying.
34
+ */
35
+ export declare const SERVICE_HEADER = "X-Mandala-MCP-Service";
5
36
  export type RequestOptions = {
6
37
  query?: Record<string, string | number | boolean | undefined>;
7
38
  body?: unknown;
8
39
  /** Raw bytes as the request body, for the file upload. Mutually exclusive with `body`. */
9
40
  raw?: Uint8Array;
10
- /** Extra headers for this call only — currently just the model key. */
41
+ /** Extra headers for this call only: the model key, and a lifecycle call's Idempotency-Key. */
11
42
  headers?: Record<string, string>;
12
43
  signal?: AbortSignal;
13
44
  };
@@ -53,6 +84,12 @@ export type Bytes = {
53
84
  * differently" and "there is no offset that will work on this file".
54
85
  */
55
86
  unrangeable: boolean;
87
+ /**
88
+ * `X-GC-Frame`, when the platform labelled what it served: `suspended` is a
89
+ * screenshot answered with the saved frame of a suspended computer rather
90
+ * than a capture of its screen.
91
+ */
92
+ frame?: string;
56
93
  };
57
94
  /**
58
95
  * The longest foreground guest exec waits 600 seconds before it answers.
@@ -114,10 +151,32 @@ export type SSEEvent = {
114
151
  * result goes into a model's context and from there into transcripts, and an
115
152
  * API key is every computer on the account, forever.
116
153
  */
154
+ /**
155
+ * What an embedding transport adds to every request one client makes.
156
+ *
157
+ * Both halves exist for the hosted install (OPL-4982), where this server stands
158
+ * between an OAuth client and the platform: the platform takes an access token
159
+ * only from a caller that also proves it is this service, and a refused token
160
+ * has to reach the MCP client as an HTTP 401 rather than as a tool error.
161
+ */
162
+ export type ApiOptions = {
163
+ /**
164
+ * Sent as {@link SERVICE_HEADER} with every request to the configured base
165
+ * URL, and nowhere else. Never logged and never put on an error, like the
166
+ * key itself.
167
+ */
168
+ serviceSecret?: string;
169
+ /**
170
+ * Called when the platform refuses THE BEARER: a 401 carrying a `Bearer`
171
+ * challenge. A 401 without one is a model provider refusing a model key,
172
+ * which says nothing about this client's credential, and is not reported.
173
+ */
174
+ onBearerRefused?: () => void;
175
+ };
117
176
  export declare class Api {
118
177
  #private;
119
178
  readonly baseUrl: string;
120
- constructor(apiKey: string, baseUrl?: string, signal?: AbortSignal);
179
+ constructor(apiKey: string, baseUrl?: string, signal?: AbortSignal, options?: ApiOptions);
121
180
  /**
122
181
  * This same client, with every request bound to one tool call's cancellation.
123
182
  *
@@ -182,6 +241,18 @@ export declare class Api {
182
241
  }>;
183
242
  /** For the two routes whose body is not JSON: the screenshot and the download. */
184
243
  bytes(method: string, path: string, opts?: RequestOptions, maxBytes?: number | ((contentType: string) => number)): Promise<Bytes>;
244
+ /**
245
+ * The account's secret store, `GET|POST secrets` and `GET|PUT|DELETE
246
+ * secrets/:id`, as five typed calls.
247
+ *
248
+ * Typed, and decoded strictly, because a value goes IN here and the caller
249
+ * must be able to rely on nothing coming back: every answer is reduced to the
250
+ * documented fields before it is returned, so no answer can hand a value on.
251
+ * A malformed answer is a {@link MandalaError}; for a change it says the
252
+ * change may have been made. Bound to this client, so
253
+ * `api.with(signal).secrets` carries the signal.
254
+ */
255
+ get secrets(): SecretStore;
185
256
  /**
186
257
  * The agent route, which answers with a stream of steps rather than a result.
187
258
  *
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAmE,MAAM,QAAQ,CAAC;AAsBhG,OAAO,EAAsB,KAAK,WAAW,EAA0B,MAAM,mBAAmB,CAAC;AAEjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,eAAO,MAAM,gBAAgB,wCAAwC,CAAC;AAEtE,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AAExD,mGAAmG;AACnG,eAAO,MAAM,uBAAuB,QAAyB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,KAAG,MAA+B,CAAC;AAEnF,kCAAkC;AAClC,eAAO,MAAM,kBAAkB,GAAI,KAAK,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAEpE,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,0BAA0B,CAAC;AA2BtD,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0FAA0F;IAC1F,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD;;;;;;;OAOG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAkCF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,SAAU,CAAC;AACnD,6EAA6E;AAC7E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,mBAAmB,OAG9B,CAAC;AAWH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,aAAa,QAAO,OAAO,UAAU,CAAC,KAG7B,CAAC;AAEvB,iDAAiD;AACjD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAExD;;;;;;;;;;;GAWG;AACH;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,qBAAa,GAAG;;IACd,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAUvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAyB,EAClC,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,GAAE,UAAe;IAsD1B;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,GAAG;IAiW1C;;;;;;;;;;;;OAYG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAe5F,4FAA4F;IACtF,cAAc,CAAC,CAAC,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,CAAC;IAexC,8FAA8F;IACxF,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,YAAY,CAAC;IA2DlB,WAAW,CACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,OAAO,CAAC;IAuBnB;;;;;;OAMG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAazB;;;;;;;;;;;;;;OAcG;IACG,OAAO,CAAC,CAAC,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,GAAG,SAAS,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAmB/D,kFAAkF;IAC5E,KAAK,CACT,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,cAAmB,EACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC,GACpD,OAAO,CAAC,KAAK,CAAC;IAgGjB;;;;;;;;;;OAUG;IACH,IAAI,OAAO,IAAI,WAAW,CA6FzB;IAED;;;;;;OAMG;IACI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,cAAc,CAAC,QAAQ,CAAC;CAyG9F;AAyCD;;;;;;;;;GASG;AACH,wBAAiB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAQnF;AAkgBD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAmD3E;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC"}