Skip to content

Commit 938f943

Browse files
authored
docs: refresh Sandboxes cookbook and API reference (#26188)
## Summary Refresh the vendored Sandboxes cookbook and OpenAPI reference from upstream. Removes standalone MCP gateway start/stop documentation, imports upstream authentication and timeout examples, auto-resume readback guidance, and network-policy selector updates, and regenerates the API presentation data. @netlify /ai/sandboxes-api/cookbook/give-a-sandbox-an-mcp-gateway/ Generated by Codex
1 parent 0bd254d commit 938f943

7 files changed

Lines changed: 269 additions & 819 deletions

File tree

‎content/manuals/ai/sandboxes-api/cookbook/connect-to-cloud-with-a-bearer-token.md‎

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,33 @@ params:
99
group: "Get started"
1010
---
1111

12-
Choose an authenticator when your application creates its client. The client obtains a credential when its first request needs one, then uses it for requests to Docker Cloud Sandboxes. You need an [installed SDK](https://docs.docker.com/ai/sandboxes-api/install/) and a Docker account with Cloud Sandboxes access.
12+
Sign in to Docker so your application can create and use Cloud Sandboxes. Before you begin, [install the SDK](https://docs.docker.com/ai/sandboxes-api/install/) and make sure your Docker account has Cloud Sandboxes access.
1313

1414
Choose interactive sign-in when running an example yourself. Use a personal access token (PAT) for a service or CI job. If your application already manages Docker access tokens, pass a token or a token provider.
1515

1616
## Sign in interactively {#1-sign-in-interactively}
1717

18-
The helper creates a client with an OAuth authenticator. Call an SDK method, such as listing sandboxes, to start sign-in. Open the printed verification URL in your browser and enter the displayed code. Sign in to your Docker account and approve the request. The SDK request proceeds when verification succeeds; a denied or expired request fails. The [complete program](run-a-complete-example.md) shows this flow from start to finish.
18+
Call `const client = await login()` using the function below. It prints a link and a code in your terminal. Open the link in your browser, enter the code, and approve sign-in with your Docker account.
1919

20-
The request that starts sign-in also sets its deadline. Pass `{ timeoutMs: 300_000 }` as that call's request options to give yourself five minutes to sign in and complete the request.
20+
The function waits for sign-in to finish before returning a client you can use to call the API. If you deny sign-in or the verification code expires, it reports an error. Run the program again to get a new code. The [complete program](run-a-complete-example.md) shows how to sign in and create a sandbox.
2121

22-
The SDK holds credentials in memory by default and refreshes the access token as later requests need it, while refresh credentials remain valid. Close the client when finished to release SDK-owned resources. This does not delete sandboxes or revoke Docker sign-in.
22+
The SDK keeps your sign-in details in memory and renews access automatically while your sign-in remains valid. You need to sign in again when you restart the program unless you save these details as described below. Call `await client.close()` when finished. Closing the client does not delete your sandboxes or sign you out of Docker.
2323

2424
You can pass the same authenticator to several clients to reuse their sign-in. Closing one client leaves the authenticator usable by the others.
2525

2626
{{< tabs >}}
2727
{{< tab name="TypeScript" >}}
2828

2929
```typescript
30-
const auth = oauth({
31-
onVerification: ({ verificationUri, userCode }) => {
32-
console.log(`Open ${verificationUri} and enter ${userCode}`);
33-
},
34-
});
35-
return new Sandboxes({ auth });
30+
export const login = async () => {
31+
const auth = oauth({
32+
onVerification: ({ verificationUri, userCode }) => {
33+
console.log(`Open ${verificationUri} and enter ${userCode}`);
34+
},
35+
});
36+
await auth.getAccessToken();
37+
return new Sandboxes({ auth });
38+
};
3639
```
3740

3841
<details>
@@ -45,22 +48,24 @@ import {
4548
Sandboxes,
4649
} from '@docker/sandboxes';
4750

48-
export function login() {
51+
export const login = async () => {
4952
const auth = oauth({
5053
onVerification: ({ verificationUri, userCode }) => {
5154
console.log(`Open ${verificationUri} and enter ${userCode}`);
5255
},
5356
});
57+
await auth.getAccessToken();
5458
return new Sandboxes({ auth });
55-
}
59+
};
5660

57-
export function loginWithSavedCredentials(path: string) {
61+
export async function loginWithSavedCredentials(path: string) {
5862
const storedAuth = oauth({
5963
store: fileOAuthCredentialStore({ path }),
6064
onVerification: ({ verificationUri, userCode }) => {
6165
console.log(`Open ${verificationUri} and enter ${userCode}`);
6266
},
6367
});
68+
await storedAuth.getAccessToken();
6469
return new Sandboxes({ auth: storedAuth });
6570
}
6671
```
@@ -72,9 +77,9 @@ export function loginWithSavedCredentials(path: string) {
7277

7378
## Optional: save credentials between runs {#2-optional-save-credentials-between-runs}
7479

75-
To avoid signing in each time a Node.js program starts, expand the complete example above and use `loginWithSavedCredentials(path)`. It passes `fileOAuthCredentialStore` to the OAuth authenticator.
80+
To reuse your sign-in when a Node.js program restarts, expand the complete example above and use `await loginWithSavedCredentials(path)`. Set `path` to the file where you want to save your sign-in details. The function uses saved details when they are still valid, or asks you to sign in again, before returning a client.
7681

77-
The file store supports Node.js on POSIX systems, not browsers or Windows. Its file contains plaintext access and refresh credentials: keep it out of source control, use a private directory, and do not share it between processes. For a keychain or secret manager, implement `OAuthCredentialStore` with `load` and `save` instead.
82+
The file store works in Node.js on systems such as macOS and Linux, but not in browsers or on Windows. The file is not encrypted. Keep it in a private directory, exclude it from source control, and do not share it between running programs. To save sign-in details in a keychain or secret manager instead, implement `OAuthCredentialStore` with `load` and `save`.
7883

7984
Remove the stored credentials when your application no longer needs them. Your application owns that removal; closing a client does not remove the file or revoke the credentials.
8085

@@ -165,8 +170,6 @@ Authentication errors mean the credential is missing, rejected, or expired. Sign
165170

166171
When you run commands or transfer files through a sandbox handle, the SDK obtains a credential scoped to that sandbox. You do not need to copy your account token into a second client.
167172

168-
The client reuses valid sandbox credentials. Concurrent requests share acquisition only within the same management client and for the same resolved Docker credential, sandbox endpoint and permissions. A later request renews the credential shortly before expiry. Renewal happens when needed, not on a five-minute timer, and does not change a request or stream already in progress.
169-
170173
Docker sign-in is separate from an agent's provider credential. To let an agent call its model provider, follow [Use secrets in a sandbox](get-a-stored-secret-into-a-sandbox.md).
171174

172175
Next, [run a complete program](run-a-complete-example.md) that signs in and launches a kit.

‎content/manuals/ai/sandboxes-api/cookbook/give-a-sandbox-an-mcp-gateway.md‎

Lines changed: 35 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Use an [authenticated client](connect-to-cloud-with-a-bearer-token.md) and serve
1717

1818
Supply the server IDs in the kit's MCP options when creating the sandbox. This makes gateway configuration available when the agent starts. Attach any model-provider secrets the agent needs separately.
1919

20-
Use this creation-time path for Cloud Sandboxes. Adding a gateway after creation can be rejected for a governed sandbox; recreating it with MCP configured avoids that limitation.
20+
To add a gateway to an existing sandbox, recreate the sandbox with MCP configured. There is no separate gateway start or stop operation.
2121

2222
{{< tabs >}}
2323
{{< tab name="TypeScript" >}}
@@ -65,69 +65,54 @@ export async function launchMcpKit(
6565
{{< /tab >}}
6666
{{< /tabs >}}
6767

68-
## Alternatively, start a gateway after creation {#2-alternatively-start-a-gateway-after-creation}
68+
## Read the gateway address {#2-read-the-gateway-address}
6969

70-
For an existing sandbox that supports gateway creation, use its MCP collection and wait until the gateway is ready. The sandbox must already have the required Docker credential. If startup is refused, do not assume that retrying will add missing credentials or change its policy configuration.
71-
72-
Inspect the returned server list and skipped-server reasons. A ready gateway does not guarantee that every requested server was accepted.
73-
74-
{{< tabs >}}
75-
{{< tab name="TypeScript" >}}
76-
77-
```typescript
78-
const gateway = await sandbox.mcp.start({ servers, static: true });
79-
return gateway.waitUntilReady({ timeoutMs: 120_000 });
80-
```
81-
82-
<details>
83-
<summary>Complete TypeScript example: mcp/start.ts</summary>
84-
85-
```typescript
86-
import type { Sandboxes } from '@docker/sandboxes';
87-
88-
export async function startMcpGateway(
89-
client: Sandboxes,
90-
name: string,
91-
servers: string[],
92-
) {
93-
const sandbox = await client.get(name.replace(/\/mcp-gateway$/, ''));
94-
const gateway = await sandbox.mcp.start({ servers, static: true });
95-
return gateway.waitUntilReady({ timeoutMs: 120_000 });
96-
}
97-
```
98-
99-
</details>
100-
101-
{{< /tab >}}
102-
{{< /tabs >}}
103-
104-
## Read the gateway address {#3-read-the-gateway-address}
105-
106-
Get the gateway and use its returned URL only when it is ready. The example checks readiness before returning the URL.
70+
Read the gateway by its `sandboxes/{sandbox}/mcp-gateway` name. The example waits through provisioning with the SDK's bounded waiter and returns the URL only when ready. A failed gateway or an expired wait returns an error. Supply a timeout appropriate for your application.
10771

10872
Keep gateway credentials private. The gateway's address and the published URL of an application inside the sandbox are different endpoints.
10973

11074
{{< tabs >}}
11175
{{< tab name="TypeScript" >}}
11276

11377
```typescript
114-
const gateway = await sandbox.mcp.get();
115-
if (gateway.state !== 'ready' || !gateway.url)
78+
const deadline = AbortSignal.timeout(options.timeoutMs);
79+
const signal = options.signal
80+
? AbortSignal.any([options.signal, deadline])
81+
: deadline;
82+
options = { ...options, signal };
83+
const observed = await client.getMcpGateway({ name }, options);
84+
const gateway = await client
85+
.mcpGateway(observed)
86+
.waitFor(['ready', 'failed'], options);
87+
if (gateway.state !== 'ready')
11688
throw new Error('MCP gateway is not ready');
89+
if (!gateway.url) throw new Error('The ready MCP gateway has no URL');
11790
return gateway.url;
11891
```
11992

12093
<details>
12194
<summary>Complete TypeScript example: mcp/read.ts</summary>
12295

12396
```typescript
124-
import type { Sandboxes } from '@docker/sandboxes';
97+
import type { Sandboxes, WaitOptions } from '@docker/sandboxes';
12598

126-
export async function readGatewayUrl(client: Sandboxes, name: string) {
127-
const sandbox = await client.get(name.replace(/\/mcp-gateway$/, ''));
128-
const gateway = await sandbox.mcp.get();
129-
if (gateway.state !== 'ready' || !gateway.url)
99+
export async function readGatewayUrl(
100+
client: Sandboxes,
101+
name: string,
102+
options: WaitOptions = { timeoutMs: 120_000 },
103+
) {
104+
const deadline = AbortSignal.timeout(options.timeoutMs);
105+
const signal = options.signal
106+
? AbortSignal.any([options.signal, deadline])
107+
: deadline;
108+
options = { ...options, signal };
109+
const observed = await client.getMcpGateway({ name }, options);
110+
const gateway = await client
111+
.mcpGateway(observed)
112+
.waitFor(['ready', 'failed'], options);
113+
if (gateway.state !== 'ready')
130114
throw new Error('MCP gateway is not ready');
115+
if (!gateway.url) throw new Error('The ready MCP gateway has no URL');
131116
return gateway.url;
132117
}
133118
```
@@ -137,7 +122,7 @@ export async function readGatewayUrl(client: Sandboxes, name: string) {
137122
{{< /tab >}}
138123
{{< /tabs >}}
139124

140-
## Add a catalog server {#4-add-a-catalog-server}
125+
## Add a catalog server {#3-add-a-catalog-server}
141126

142127
Add a server to a ready, writable gateway. Adding a server already present does not add a second copy. A shared gateway attached by URL cannot be modified through this sandbox.
143128

@@ -169,12 +154,14 @@ export async function addMcpGatewayServer(
169154
{{< /tab >}}
170155
{{< /tabs >}}
171156

172-
## Complete a server's sign-in {#5-complete-a-server-s-sign-in}
157+
## Complete a server's sign-in {#4-complete-a-server-s-sign-in}
173158

174159
Start authorization for a server that requires user sign-in. Present its authorization URL to the user, then read the authorization resource to check progress. The example performs the start and read calls; your application decides how to display and poll the flow.
175160

176161
Only an authorized result means the credential is ready. Keep the authorization identity so a later attempt is not mistaken for completion of an earlier one. Request reauthorization only when you intend a new sign-in.
177162

163+
Delete the sandbox when it is no longer needed. Its managed gateway is cleaned up with it; a shared gateway attached by URL remains available to its other users.
164+
178165
{{< tabs >}}
179166
{{< tab name="TypeScript" >}}
180167

@@ -214,33 +201,3 @@ export async function getMcpAuthorization(
214201

215202
{{< /tab >}}
216203
{{< /tabs >}}
217-
218-
## Stop the gateway {#6-stop-the-gateway}
219-
220-
Stop the gateway when the sandbox no longer needs its tools. This does not delete the sandbox. Detaching from a shared gateway does not remove that gateway for its other users.
221-
222-
Existing agent processes may still hold old connection settings. Plan their restart or reconfiguration when changing the gateway.
223-
224-
{{< tabs >}}
225-
{{< tab name="TypeScript" >}}
226-
227-
```typescript
228-
await sandbox.mcp.stop();
229-
```
230-
231-
<details>
232-
<summary>Complete TypeScript example: mcp/stop.ts</summary>
233-
234-
```typescript
235-
import type { Sandboxes } from '@docker/sandboxes';
236-
237-
export async function stopMcpGateway(client: Sandboxes, name: string) {
238-
const sandbox = await client.get(name.replace(/\/mcp-gateway$/, ''));
239-
await sandbox.mcp.stop();
240-
}
241-
```
242-
243-
</details>
244-
245-
{{< /tab >}}
246-
{{< /tabs >}}

‎content/manuals/ai/sandboxes-api/cookbook/let-a-stopped-sandbox-resume-on-demand.md‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ export async function createWithAutoResume(
5858

5959
## Read the effective setting {#2-read-the-effective-setting}
6060

61-
Get the sandbox and inspect the effective automatic-resume value together with its state. This checks what the service recorded; it does not send a request to the application or prove that the application can start.
61+
Get the sandbox and inspect the effective lifecycle automatic-resume value together with its state. The example falls back to the timeout value for older responses and reports an error if neither value is present. An absent value is unknown, not disabled.
62+
63+
This checks what the service recorded; it does not send a request to the application or prove that the application can start. Authentication is still required.
6264

6365
To verify the whole flow, stop the sandbox, request its published application URL, and confirm that the application becomes ready. Allow for startup time and keep the application's own authentication enabled.
6466

@@ -67,8 +69,14 @@ To verify the whole flow, stop the sandbox, request its published application UR
6769

6870
```typescript
6971
const sandbox = await client.get(name);
72+
const active =
73+
sandbox.effectiveFeatures?.lifecycle?.autoResume ??
74+
sandbox.effectiveFeatures?.timeouts?.autoResume;
75+
if (active == null) {
76+
throw new Error(`sandbox ${name}: auto-resume setting is unknown`);
77+
}
7078
return {
71-
autoResume: sandbox.effectiveFeatures?.timeouts?.autoResume ?? false,
79+
autoResume: active,
7280
status: sandbox.status,
7381
};
7482
```
@@ -81,8 +89,14 @@ import type { Sandboxes } from '@docker/sandboxes';
8189

8290
export async function autoResumeInForce(client: Sandboxes, name: string) {
8391
const sandbox = await client.get(name);
92+
const active =
93+
sandbox.effectiveFeatures?.lifecycle?.autoResume ??
94+
sandbox.effectiveFeatures?.timeouts?.autoResume;
95+
if (active == null) {
96+
throw new Error(`sandbox ${name}: auto-resume setting is unknown`);
97+
}
8498
return {
85-
autoResume: sandbox.effectiveFeatures?.timeouts?.autoResume ?? false,
99+
autoResume: active,
86100
status: sandbox.status,
87101
};
88102
}

‎content/manuals/ai/sandboxes-api/cookbook/run-a-complete-example.md‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ Run a small program that signs in to Docker, creates a sandbox from the `shell`
1717

1818
Expand the complete example and copy the whole program into a file. Save it as `example.mts`, then run `npx tsx example.mts`.
1919

20-
The program prints a verification URL and code when its first API request needs authentication. Open the URL, enter the code, and approve sign-in. Your terminal then shows the sandbox's name and `Hello from Docker Sandboxes`. The program checks the command's exit status and cleans up its sandbox before closing the client.
20+
The program prints a verification URL and code. Open the URL, enter the code, and approve sign-in. Your terminal then shows the sandbox's name and `Hello from Docker Sandboxes`. The program checks the command's exit status and attempts to delete its sandbox before closing the client.
2121

22-
If authentication fails, check the steps in [Authenticate to Docker](connect-to-cloud-with-a-bearer-token.md). If creation is refused, check account access and [resource limits](work-within-the-limits.md). A timeout does not prove that an accepted sandbox was deleted; keep any sandbox name reported with a cleanup error so you can inspect it.
22+
The program waits for sign-in to finish or the verification code to expire. After sign-in, creating the sandbox and running the command share a five-minute time limit. Once the program receives the new sandbox's details, it allows another 30 seconds to delete that sandbox, even if a later step fails.
23+
24+
If sign-in fails, follow the steps in [Authenticate to Docker](connect-to-cloud-with-a-bearer-token.md). If sandbox creation fails, check account access and [resource limits](work-within-the-limits.md).
25+
26+
A creation timeout can occur after Docker creates the sandbox but before the program receives its details. In that case, the program cannot delete it. [List your sandboxes](page-through-and-filter-lists.md) to check for a sandbox created by this run and [delete it](delete-a-cloud-sandbox.md) if needed. If deletion fails or times out, use the sandbox name printed in your terminal to check whether it still exists.
2327

2428
This example deletes its sandbox because it is a one-off demonstration. Next, [keep a sandbox for later work](create-your-first-sandbox.md) or [run an agent kit](add-tools-with-kits.md).
2529

@@ -52,7 +56,11 @@ export async function main() {
5256
const client = new Sandboxes({ auth });
5357
let sandbox: Sandbox | undefined;
5458
try {
55-
const operation = { timeoutMs: 300_000 };
59+
await auth.getAccessToken();
60+
const operation = {
61+
signal: AbortSignal.timeout(300_000),
62+
timeoutMs: 300_000,
63+
};
5664
sandbox = await client.kits.launch(
5765
'shell',
5866
{

0 commit comments

Comments
 (0)