@finalai/agent-provider 0.1.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.

Potentially problematic release.


This version of @finalai/agent-provider might be problematic. Click here for more details.

Files changed (176) hide show
  1. package/README.md +12 -0
  2. package/dist/modules/channel-context.d.mts +102 -0
  3. package/dist/modules/channel-context.mjs +52 -0
  4. package/dist/modules/channel-context.mjs.map +7 -0
  5. package/dist/modules/channel-socket.d.mts +65 -0
  6. package/dist/modules/channel-socket.mjs +188 -0
  7. package/dist/modules/channel-socket.mjs.map +7 -0
  8. package/dist/modules/channel.d.mts +382 -0
  9. package/dist/modules/channel.mjs +146 -0
  10. package/dist/modules/channel.mjs.map +7 -0
  11. package/dist/modules/conformance/agent.d.mts +20 -0
  12. package/dist/modules/conformance/agent.mjs +623 -0
  13. package/dist/modules/conformance/agent.mjs.map +7 -0
  14. package/dist/modules/conformance/fixtures.d.mts +41 -0
  15. package/dist/modules/conformance/fixtures.mjs +190 -0
  16. package/dist/modules/conformance/fixtures.mjs.map +7 -0
  17. package/dist/modules/conformance/harness-kit.d.mts +82 -0
  18. package/dist/modules/conformance/harness-kit.mjs +1303 -0
  19. package/dist/modules/conformance/harness-kit.mjs.map +7 -0
  20. package/dist/modules/conformance/image.d.mts +2 -0
  21. package/dist/modules/conformance/image.mjs +79 -0
  22. package/dist/modules/conformance/image.mjs.map +7 -0
  23. package/dist/modules/conformance/index.d.mts +8 -0
  24. package/dist/modules/conformance/index.mjs +12 -0
  25. package/dist/modules/conformance/index.mjs.map +7 -0
  26. package/dist/modules/conformance/local-harness.d.mts +25 -0
  27. package/dist/modules/conformance/local-harness.mjs +108 -0
  28. package/dist/modules/conformance/local-harness.mjs.map +7 -0
  29. package/dist/modules/conformance/protocol.d.mts +30 -0
  30. package/dist/modules/conformance/protocol.mjs +56 -0
  31. package/dist/modules/conformance/protocol.mjs.map +7 -0
  32. package/dist/modules/conformance/sandbox-kit.d.mts +19 -0
  33. package/dist/modules/conformance/sandbox-kit.mjs +320 -0
  34. package/dist/modules/conformance/sandbox-kit.mjs.map +7 -0
  35. package/dist/modules/conformance/scripted-model/chat-completions.d.mts +30 -0
  36. package/dist/modules/conformance/scripted-model/chat-completions.mjs +125 -0
  37. package/dist/modules/conformance/scripted-model/chat-completions.mjs.map +7 -0
  38. package/dist/modules/conformance/scripted-model/directives.d.mts +84 -0
  39. package/dist/modules/conformance/scripted-model/directives.mjs +304 -0
  40. package/dist/modules/conformance/scripted-model/directives.mjs.map +7 -0
  41. package/dist/modules/conformance/scripted-model/handler.d.mts +14 -0
  42. package/dist/modules/conformance/scripted-model/handler.mjs +133 -0
  43. package/dist/modules/conformance/scripted-model/handler.mjs.map +7 -0
  44. package/dist/modules/conformance/scripted-model/index.d.mts +6 -0
  45. package/dist/modules/conformance/scripted-model/index.mjs +7 -0
  46. package/dist/modules/conformance/scripted-model/index.mjs.map +7 -0
  47. package/dist/modules/conformance/scripted-model/messages.d.mts +39 -0
  48. package/dist/modules/conformance/scripted-model/messages.mjs +139 -0
  49. package/dist/modules/conformance/scripted-model/messages.mjs.map +7 -0
  50. package/dist/modules/conformance/scripted-model/responses.d.mts +62 -0
  51. package/dist/modules/conformance/scripted-model/responses.mjs +273 -0
  52. package/dist/modules/conformance/scripted-model/responses.mjs.map +7 -0
  53. package/dist/modules/conformance/scripted-model/sse.d.mts +15 -0
  54. package/dist/modules/conformance/scripted-model/sse.mjs +75 -0
  55. package/dist/modules/conformance/scripted-model/sse.mjs.map +7 -0
  56. package/dist/modules/conformance/v1-agent.d.mts +24 -0
  57. package/dist/modules/conformance/v1-agent.mjs +40 -0
  58. package/dist/modules/conformance/v1-agent.mjs.map +7 -0
  59. package/dist/modules/credential-routes.d.mts +24 -0
  60. package/dist/modules/credential-routes.mjs +25 -0
  61. package/dist/modules/credential-routes.mjs.map +7 -0
  62. package/dist/modules/credentials.d.mts +206 -0
  63. package/dist/modules/credentials.mjs +207 -0
  64. package/dist/modules/credentials.mjs.map +7 -0
  65. package/dist/modules/dynamic.d.mts +32 -0
  66. package/dist/modules/dynamic.mjs +77 -0
  67. package/dist/modules/dynamic.mjs.map +7 -0
  68. package/dist/modules/harness.d.mts +397 -0
  69. package/dist/modules/harness.mjs +213 -0
  70. package/dist/modules/harness.mjs.map +7 -0
  71. package/dist/modules/index.d.mts +15 -0
  72. package/dist/modules/index.mjs +40 -0
  73. package/dist/modules/index.mjs.map +7 -0
  74. package/dist/modules/json.d.mts +12 -0
  75. package/dist/modules/json.mjs +19 -0
  76. package/dist/modules/json.mjs.map +7 -0
  77. package/dist/modules/local-channel-socket.d.mts +23 -0
  78. package/dist/modules/local-channel-socket.mjs +141 -0
  79. package/dist/modules/local-channel-socket.mjs.map +7 -0
  80. package/dist/modules/manifest.d.mts +558 -0
  81. package/dist/modules/manifest.mjs +266 -0
  82. package/dist/modules/manifest.mjs.map +7 -0
  83. package/dist/modules/mcp-content.d.mts +21 -0
  84. package/dist/modules/mcp-content.mjs +86 -0
  85. package/dist/modules/mcp-content.mjs.map +7 -0
  86. package/dist/modules/provider-ref.d.mts +29 -0
  87. package/dist/modules/provider-ref.mjs +55 -0
  88. package/dist/modules/provider-ref.mjs.map +7 -0
  89. package/dist/modules/sandbox/bytes.d.mts +9 -0
  90. package/dist/modules/sandbox/bytes.mjs +154 -0
  91. package/dist/modules/sandbox/bytes.mjs.map +7 -0
  92. package/dist/modules/sandbox/client.d.mts +51 -0
  93. package/dist/modules/sandbox/client.mjs +500 -0
  94. package/dist/modules/sandbox/client.mjs.map +7 -0
  95. package/dist/modules/sandbox/confined-files.d.mts +22 -0
  96. package/dist/modules/sandbox/confined-files.mjs +142 -0
  97. package/dist/modules/sandbox/confined-files.mjs.map +7 -0
  98. package/dist/modules/sandbox/convenience.d.mts +10 -0
  99. package/dist/modules/sandbox/convenience.mjs +116 -0
  100. package/dist/modules/sandbox/convenience.mjs.map +7 -0
  101. package/dist/modules/sandbox/editor.d.mts +49 -0
  102. package/dist/modules/sandbox/editor.mjs +43 -0
  103. package/dist/modules/sandbox/editor.mjs.map +7 -0
  104. package/dist/modules/sandbox/frame-channel.d.mts +47 -0
  105. package/dist/modules/sandbox/frame-channel.mjs +153 -0
  106. package/dist/modules/sandbox/frame-channel.mjs.map +7 -0
  107. package/dist/modules/sandbox/index.d.mts +18 -0
  108. package/dist/modules/sandbox/index.mjs +70 -0
  109. package/dist/modules/sandbox/index.mjs.map +7 -0
  110. package/dist/modules/sandbox/mcp-channel.d.mts +9 -0
  111. package/dist/modules/sandbox/mcp-channel.mjs +77 -0
  112. package/dist/modules/sandbox/mcp-channel.mjs.map +7 -0
  113. package/dist/modules/sandbox/mcp-hosted.d.mts +46 -0
  114. package/dist/modules/sandbox/mcp-hosted.mjs +221 -0
  115. package/dist/modules/sandbox/mcp-hosted.mjs.map +7 -0
  116. package/dist/modules/sandbox/mcp-search.d.mts +51 -0
  117. package/dist/modules/sandbox/mcp-search.mjs +129 -0
  118. package/dist/modules/sandbox/mcp-search.mjs.map +7 -0
  119. package/dist/modules/sandbox/mcp-shell.d.mts +21 -0
  120. package/dist/modules/sandbox/mcp-shell.mjs +205 -0
  121. package/dist/modules/sandbox/mcp-shell.mjs.map +7 -0
  122. package/dist/modules/sandbox/mcp-testing.d.mts +11 -0
  123. package/dist/modules/sandbox/mcp-testing.mjs +33 -0
  124. package/dist/modules/sandbox/mcp-testing.mjs.map +7 -0
  125. package/dist/modules/sandbox/mcp.d.mts +83 -0
  126. package/dist/modules/sandbox/mcp.mjs +971 -0
  127. package/dist/modules/sandbox/mcp.mjs.map +7 -0
  128. package/dist/modules/sandbox/memory-sandbox.d.mts +32 -0
  129. package/dist/modules/sandbox/memory-sandbox.mjs +416 -0
  130. package/dist/modules/sandbox/memory-sandbox.mjs.map +7 -0
  131. package/dist/modules/sandbox/memory-shell.d.mts +29 -0
  132. package/dist/modules/sandbox/memory-shell.mjs +278 -0
  133. package/dist/modules/sandbox/memory-shell.mjs.map +7 -0
  134. package/dist/modules/sandbox/open-scope-node.d.mts +4 -0
  135. package/dist/modules/sandbox/open-scope-node.mjs +19 -0
  136. package/dist/modules/sandbox/open-scope-node.mjs.map +7 -0
  137. package/dist/modules/sandbox/open-scope.d.mts +16 -0
  138. package/dist/modules/sandbox/open-scope.mjs +66 -0
  139. package/dist/modules/sandbox/open-scope.mjs.map +7 -0
  140. package/dist/modules/sandbox/paths.d.mts +7 -0
  141. package/dist/modules/sandbox/paths.mjs +50 -0
  142. package/dist/modules/sandbox/paths.mjs.map +7 -0
  143. package/dist/modules/sandbox/rpc-peer.d.mts +35 -0
  144. package/dist/modules/sandbox/rpc-peer.mjs +224 -0
  145. package/dist/modules/sandbox/rpc-peer.mjs.map +7 -0
  146. package/dist/modules/sandbox/seeds.d.mts +26 -0
  147. package/dist/modules/sandbox/seeds.mjs +228 -0
  148. package/dist/modules/sandbox/seeds.mjs.map +7 -0
  149. package/dist/modules/sandbox/server.d.mts +66 -0
  150. package/dist/modules/sandbox/server.mjs +567 -0
  151. package/dist/modules/sandbox/server.mjs.map +7 -0
  152. package/dist/modules/sandbox/snapshots.d.mts +49 -0
  153. package/dist/modules/sandbox/snapshots.mjs +55 -0
  154. package/dist/modules/sandbox/snapshots.mjs.map +7 -0
  155. package/dist/modules/sandbox/stream-mux.d.mts +18 -0
  156. package/dist/modules/sandbox/stream-mux.mjs +285 -0
  157. package/dist/modules/sandbox/stream-mux.mjs.map +7 -0
  158. package/dist/modules/sandbox/testing.d.mts +25 -0
  159. package/dist/modules/sandbox/testing.mjs +64 -0
  160. package/dist/modules/sandbox/testing.mjs.map +7 -0
  161. package/dist/modules/sandbox/types.d.mts +352 -0
  162. package/dist/modules/sandbox/types.mjs +86 -0
  163. package/dist/modules/sandbox/types.mjs.map +7 -0
  164. package/dist/modules/sandbox/wire.d.mts +671 -0
  165. package/dist/modules/sandbox/wire.mjs +537 -0
  166. package/dist/modules/sandbox/wire.mjs.map +7 -0
  167. package/dist/modules/sandbox/workspace-root.d.mts +8 -0
  168. package/dist/modules/sandbox/workspace-root.mjs +79 -0
  169. package/dist/modules/sandbox/workspace-root.mjs.map +7 -0
  170. package/dist/modules/schemas.d.mts +121 -0
  171. package/dist/modules/schemas.mjs +146 -0
  172. package/dist/modules/schemas.mjs.map +7 -0
  173. package/dist/modules/semantic-events.d.mts +31 -0
  174. package/dist/modules/semantic-events.mjs +23 -0
  175. package/dist/modules/semantic-events.mjs.map +7 -0
  176. package/package.json +199 -0
@@ -0,0 +1,971 @@
1
+ import {
2
+ createMcpHandler,
3
+ fromJsonSchema,
4
+ McpServer,
5
+ ResourceTemplate
6
+ } from "@modelcontextprotocol/server";
7
+ import * as z from "zod";
8
+ import { SandboxAccess, ToolApproval } from "../manifest.mjs";
9
+ import { bytesToBase64, utf8Decode } from "./bytes.mjs";
10
+ import {
11
+ createHostedMcpConnections,
12
+ EMPTY_MCP_CATALOG,
13
+ hostedTools,
14
+ SandboxHostedServerKind
15
+ } from "./mcp-hosted.mjs";
16
+ import { globFiles, grepFiles } from "./mcp-search.mjs";
17
+ import { createSandboxShell, SandboxShellMode } from "./mcp-shell.mjs";
18
+ import { resolvePosixPath } from "./paths.mjs";
19
+ import {
20
+ PortProtocol,
21
+ SandboxCapability,
22
+ SandboxEntryType
23
+ } from "./types.mjs";
24
+ import {
25
+ COMPUTER_USE_SERVER_NAME,
26
+ SandboxHostedServerKind as SandboxHostedServerKind2,
27
+ withHostedMcpEndpoints
28
+ } from "./mcp-hosted.mjs";
29
+ import { SandboxShellMode as SandboxShellMode2 } from "./mcp-shell.mjs";
30
+ var SandboxMcpTool = /* @__PURE__ */ ((SandboxMcpTool2) => {
31
+ SandboxMcpTool2["Run"] = "run";
32
+ SandboxMcpTool2["Spawn"] = "spawn";
33
+ SandboxMcpTool2["ProcessOutput"] = "process_output";
34
+ SandboxMcpTool2["ProcessInput"] = "process_input";
35
+ SandboxMcpTool2["ProcessTerminate"] = "process_terminate";
36
+ SandboxMcpTool2["ReadFile"] = "read_file";
37
+ SandboxMcpTool2["WriteFile"] = "write_file";
38
+ SandboxMcpTool2["EditFile"] = "edit_file";
39
+ SandboxMcpTool2["ListDir"] = "list_dir";
40
+ SandboxMcpTool2["Stat"] = "stat";
41
+ SandboxMcpTool2["Grep"] = "grep";
42
+ SandboxMcpTool2["Glob"] = "glob";
43
+ SandboxMcpTool2["GetHost"] = "get_host";
44
+ return SandboxMcpTool2;
45
+ })(SandboxMcpTool || {});
46
+ var SandboxMcpScopeKind = /* @__PURE__ */ ((SandboxMcpScopeKind2) => {
47
+ SandboxMcpScopeKind2["All"] = "all";
48
+ SandboxMcpScopeKind2["Hosted"] = "hosted";
49
+ SandboxMcpScopeKind2["ComputerUse"] = "computerUse";
50
+ SandboxMcpScopeKind2["LocalServer"] = "localServer";
51
+ return SandboxMcpScopeKind2;
52
+ })(SandboxMcpScopeKind || {});
53
+ const SANDBOX_FILE_RESOURCE_SCHEME = "sandbox-file";
54
+ const objectSchema = (properties, required) => ({
55
+ type: "object",
56
+ properties,
57
+ required,
58
+ additionalProperties: false
59
+ });
60
+ const str = (description) => ({ type: "string", description });
61
+ const int = (description, minimum = 0) => ({
62
+ type: "integer",
63
+ minimum,
64
+ description
65
+ });
66
+ const envSchema = {
67
+ type: "object",
68
+ additionalProperties: { type: "string" },
69
+ description: "Extra environment variables for this command only."
70
+ };
71
+ const readOnly = {
72
+ readOnlyHint: true,
73
+ destructiveHint: false,
74
+ idempotentHint: true,
75
+ openWorldHint: false
76
+ };
77
+ const mutating = {
78
+ readOnlyHint: false,
79
+ destructiveHint: true,
80
+ idempotentHint: false,
81
+ openWorldHint: false
82
+ };
83
+ const tool = (args) => {
84
+ const annotations = {
85
+ title: args.title,
86
+ ...args.write ? mutating : readOnly
87
+ };
88
+ if (args.destructive != null) annotations.destructiveHint = args.destructive;
89
+ if (args.idempotent != null) annotations.idempotentHint = args.idempotent;
90
+ return {
91
+ descriptor: {
92
+ name: args.name,
93
+ description: args.description,
94
+ inputSchema: args.inputSchema,
95
+ annotations
96
+ },
97
+ capability: args.capability,
98
+ write: args.write,
99
+ policyKeys: args.policyKeys
100
+ };
101
+ };
102
+ const BUILT_IN_TOOLS = [
103
+ tool({
104
+ name: "run" /* Run */,
105
+ title: "Run command",
106
+ description: "Run a shell command in the sandbox and wait for it. The shell persists between calls (cd and exported variables carry over). Returns exit_code, stdout and stderr.",
107
+ inputSchema: objectSchema(
108
+ {
109
+ command: str("The bash command to run."),
110
+ cwd: str("Directory to run in (absolute, or relative to the working directory)."),
111
+ timeout_ms: int("Kill the command after this many milliseconds.", 1),
112
+ env: envSchema
113
+ },
114
+ ["command"]
115
+ ),
116
+ capability: SandboxCapability.Process,
117
+ write: true,
118
+ policyKeys: ["shell", "process"]
119
+ }),
120
+ tool({
121
+ name: "spawn" /* Spawn */,
122
+ title: "Start background process",
123
+ description: "Start a long-running command (a server, a watcher) without waiting. Returns process_id; read its output with process_output.",
124
+ inputSchema: objectSchema(
125
+ {
126
+ command: str("The bash command to start."),
127
+ cwd: str("Directory to run in."),
128
+ env: envSchema
129
+ },
130
+ ["command"]
131
+ ),
132
+ capability: SandboxCapability.Process,
133
+ write: true,
134
+ policyKeys: ["shell", "process"]
135
+ }),
136
+ tool({
137
+ name: "process_output" /* ProcessOutput */,
138
+ title: "Read process output",
139
+ description: "Read output of a process started with spawn, from byte offsets (pass the next_offset values from the previous call). Optionally waits for new output.",
140
+ inputSchema: objectSchema(
141
+ {
142
+ process_id: str("The id returned by spawn."),
143
+ stdout_offset: int("Byte offset into stdout."),
144
+ stderr_offset: int("Byte offset into stderr."),
145
+ max_bytes: int("Maximum bytes per stream to return (default 65536).", 1),
146
+ wait_ms: int("Wait up to this long for new output or exit (max 30000).")
147
+ },
148
+ ["process_id"]
149
+ ),
150
+ capability: SandboxCapability.Process,
151
+ write: false,
152
+ policyKeys: ["shell", "process"],
153
+ idempotent: false
154
+ }),
155
+ tool({
156
+ name: "process_input" /* ProcessInput */,
157
+ title: "Write process input",
158
+ description: "Write text to the stdin of a process started with spawn; set end to close stdin.",
159
+ inputSchema: objectSchema(
160
+ {
161
+ process_id: str("The id returned by spawn."),
162
+ data: str("Text to write."),
163
+ end: { type: "boolean" }
164
+ },
165
+ ["process_id"]
166
+ ),
167
+ capability: SandboxCapability.Process,
168
+ write: true,
169
+ policyKeys: ["shell", "process"],
170
+ destructive: false
171
+ }),
172
+ tool({
173
+ name: "process_terminate" /* ProcessTerminate */,
174
+ title: "Terminate process",
175
+ description: "Stop a process started with spawn.",
176
+ inputSchema: objectSchema(
177
+ {
178
+ process_id: str("The id returned by spawn."),
179
+ signal: str("Signal name (default SIGTERM).")
180
+ },
181
+ ["process_id"]
182
+ ),
183
+ capability: SandboxCapability.Process,
184
+ write: true,
185
+ policyKeys: ["shell", "process"]
186
+ }),
187
+ tool({
188
+ name: "read_file" /* ReadFile */,
189
+ title: "Read file",
190
+ description: "Read a text file. Use offset (1-based line) and limit (line count) for large files; large or binary files also come back as a resource link.",
191
+ inputSchema: objectSchema(
192
+ {
193
+ path: str("File path (absolute, or relative to the working directory)."),
194
+ offset: int("First line to read (1-based).", 1),
195
+ limit: int("Number of lines to read.", 1)
196
+ },
197
+ ["path"]
198
+ ),
199
+ capability: SandboxCapability.Files,
200
+ write: false,
201
+ policyKeys: ["files.read", "files"]
202
+ }),
203
+ tool({
204
+ name: "write_file" /* WriteFile */,
205
+ title: "Write file",
206
+ description: "Create or overwrite a text file. Parent directories are created.",
207
+ inputSchema: objectSchema({ path: str("File path."), content: str("Full file content.") }, [
208
+ "path",
209
+ "content"
210
+ ]),
211
+ capability: SandboxCapability.Files,
212
+ write: true,
213
+ policyKeys: ["files.write", "files"]
214
+ }),
215
+ tool({
216
+ name: "edit_file" /* EditFile */,
217
+ title: "Edit file",
218
+ description: "Replace an exact string in a file. old_string must occur exactly once unless replace_all is true; include surrounding lines to make it unique.",
219
+ inputSchema: objectSchema(
220
+ {
221
+ path: str("File path."),
222
+ old_string: str("Exact text to replace."),
223
+ new_string: str("Replacement text."),
224
+ replace_all: { type: "boolean", description: "Replace every occurrence." }
225
+ },
226
+ ["path", "old_string", "new_string"]
227
+ ),
228
+ capability: SandboxCapability.Files,
229
+ write: true,
230
+ policyKeys: ["files.write", "files"]
231
+ }),
232
+ tool({
233
+ name: "list_dir" /* ListDir */,
234
+ title: "List directory",
235
+ description: "List the entries of a directory (name, type, size).",
236
+ inputSchema: objectSchema(
237
+ { path: str("Directory path (default: the working directory).") },
238
+ []
239
+ ),
240
+ capability: SandboxCapability.Files,
241
+ write: false,
242
+ policyKeys: ["files.read", "files"]
243
+ }),
244
+ tool({
245
+ name: "stat" /* Stat */,
246
+ title: "Stat path",
247
+ description: "Type, size, modification time and mode of a path, or exists: false.",
248
+ inputSchema: objectSchema({ path: str("Path to inspect.") }, ["path"]),
249
+ capability: SandboxCapability.Files,
250
+ write: false,
251
+ policyKeys: ["files.read", "files"]
252
+ }),
253
+ tool({
254
+ name: "grep" /* Grep */,
255
+ title: "Search file contents",
256
+ description: "Search files under a path for a regular expression. Returns path:line:text matches. Skips .git and node_modules.",
257
+ inputSchema: objectSchema(
258
+ {
259
+ pattern: str("JavaScript regular expression."),
260
+ path: str("File or directory to search (default: the working directory)."),
261
+ glob: str('Only search files matching this glob (e.g. "*.ts").'),
262
+ ignore_case: { type: "boolean" },
263
+ max_results: int("Maximum matches (default 200).", 1)
264
+ },
265
+ ["pattern"]
266
+ ),
267
+ capability: SandboxCapability.Files,
268
+ write: false,
269
+ policyKeys: ["files.read", "files"]
270
+ }),
271
+ tool({
272
+ name: "glob" /* Glob */,
273
+ title: "Find files",
274
+ description: 'Find files whose path (relative to path) matches a glob such as "**/*.ts".',
275
+ inputSchema: objectSchema(
276
+ {
277
+ pattern: str("Glob pattern."),
278
+ path: str("Directory to search (default: the working directory)."),
279
+ max_results: int("Maximum paths (default 500).", 1)
280
+ },
281
+ ["pattern"]
282
+ ),
283
+ capability: SandboxCapability.Files,
284
+ write: false,
285
+ policyKeys: ["files.read", "files"]
286
+ }),
287
+ tool({
288
+ name: "get_host" /* GetHost */,
289
+ title: "Get port URL",
290
+ description: "The URL (and any required headers) at which a port served inside the sandbox is reachable.",
291
+ inputSchema: objectSchema(
292
+ {
293
+ port: int("Port number.", 1),
294
+ protocol: {
295
+ type: "string",
296
+ enum: Object.values(PortProtocol),
297
+ description: "Default http."
298
+ }
299
+ },
300
+ ["port"]
301
+ ),
302
+ capability: SandboxCapability.Ports,
303
+ write: false,
304
+ policyKeys: ["ports"]
305
+ })
306
+ ];
307
+ const scopeOf = (options) => options.scope ?? { kind: "all" /* All */ };
308
+ const hostedServersOf = (options) => {
309
+ if (options.access === SandboxAccess.Read) return { computerUse: false, localMcp: false };
310
+ const capabilities = new Set(options.capabilities);
311
+ const scope = scopeOf(options);
312
+ const computerUse = capabilities.has(SandboxCapability.ComputerUse) && scope.kind !== "localServer" /* LocalServer */;
313
+ const localMcp = capabilities.has(SandboxCapability.LocalMcp) && scope.kind !== "computerUse" /* ComputerUse */;
314
+ return scope.kind === "localServer" /* LocalServer */ ? { computerUse, localMcp, localServer: scope.server } : { computerUse, localMcp };
315
+ };
316
+ const hostedSpec = (hosted) => {
317
+ const annotations = { ...mutating, ...hosted.tool.annotations };
318
+ return {
319
+ descriptor: { ...hosted.tool, name: hosted.name, annotations },
320
+ capability: hosted.server.kind === SandboxHostedServerKind.ComputerUse ? SandboxCapability.ComputerUse : SandboxCapability.LocalMcp,
321
+ write: true,
322
+ policyKeys: hosted.policyKeys,
323
+ hosted
324
+ };
325
+ };
326
+ const toolSpecs = (options) => {
327
+ const capabilities = new Set(options.capabilities);
328
+ const builtIn = scopeOf(options).kind === "all" /* All */ ? BUILT_IN_TOOLS.filter((spec) => capabilities.has(spec.capability)) : [];
329
+ const visible = options.access === SandboxAccess.Read ? builtIn.filter((spec) => !spec.write) : options.access === SandboxAccess.Execute ? builtIn.filter((spec) => !spec.write || spec.capability !== SandboxCapability.Files) : builtIn;
330
+ const hosted = hostedTools({
331
+ catalog: options.catalog ?? EMPTY_MCP_CATALOG,
332
+ ...hostedServersOf(options),
333
+ taken: new Set(builtIn.map((spec) => spec.descriptor.name))
334
+ });
335
+ return [...visible, ...hosted.map(hostedSpec)];
336
+ };
337
+ const sandboxMcpToolDescriptors = (options) => toolSpecs(options).map((spec) => spec.descriptor);
338
+ const sandboxMcpToolPolicyKeys = (options, name) => {
339
+ const spec = toolSpecs(options).find((candidate) => candidate.descriptor.name === name);
340
+ return spec == null ? null : [name, ...spec.policyKeys];
341
+ };
342
+ const DEFAULT_LIMITS = {
343
+ maxOutputChars: 64e3,
344
+ maxInlineBytes: 256 * 1024,
345
+ processBufferBytes: 4 * 1024 * 1024,
346
+ maxProcesses: 32,
347
+ exitReportGraceMs: 2e3,
348
+ exitOutputChars: 4e3
349
+ };
350
+ const textResult = (text) => ({ content: [{ type: "text", text }] });
351
+ const jsonResult = (value) => textResult(JSON.stringify(value, null, 2));
352
+ const errorResult = (message) => ({
353
+ content: [{ type: "text", text: message }],
354
+ isError: true
355
+ });
356
+ const truncateMiddle = (text, max) => {
357
+ if (text.length <= max) return text;
358
+ const half = Math.floor(max / 2);
359
+ return `${text.slice(0, half)}
360
+ [... ${text.length - max} characters omitted ...]
361
+ ${text.slice(text.length - half)}`;
362
+ };
363
+ const listRequestSchema = z.object({ method: z.literal("tools/list") });
364
+ const runArgsSchema = z.object({
365
+ command: z.string().min(1),
366
+ cwd: z.string().optional(),
367
+ timeout_ms: z.number().int().positive().optional(),
368
+ env: z.record(z.string(), z.string()).optional()
369
+ });
370
+ const spawnArgsSchema = runArgsSchema.omit({ timeout_ms: true });
371
+ const processOutputArgsSchema = z.object({
372
+ process_id: z.string().min(1),
373
+ stdout_offset: z.number().int().nonnegative().optional(),
374
+ stderr_offset: z.number().int().nonnegative().optional(),
375
+ max_bytes: z.number().int().positive().optional(),
376
+ wait_ms: z.number().int().nonnegative().optional()
377
+ });
378
+ const processInputArgsSchema = z.object({
379
+ process_id: z.string().min(1),
380
+ data: z.string().optional(),
381
+ end: z.boolean().optional()
382
+ });
383
+ const processTerminateArgsSchema = z.object({
384
+ process_id: z.string().min(1),
385
+ signal: z.string().optional()
386
+ });
387
+ const readFileArgsSchema = z.object({
388
+ path: z.string().min(1),
389
+ offset: z.number().int().positive().optional(),
390
+ limit: z.number().int().positive().optional()
391
+ });
392
+ const writeFileArgsSchema = z.object({ path: z.string().min(1), content: z.string() });
393
+ const editFileArgsSchema = z.object({
394
+ path: z.string().min(1),
395
+ old_string: z.string().min(1),
396
+ new_string: z.string(),
397
+ replace_all: z.boolean().optional()
398
+ });
399
+ const pathArgsSchema = z.object({ path: z.string().min(1) });
400
+ const listDirArgsSchema = z.object({ path: z.string().optional() });
401
+ const grepArgsSchema = z.object({
402
+ pattern: z.string().min(1),
403
+ path: z.string().optional(),
404
+ glob: z.string().optional(),
405
+ ignore_case: z.boolean().optional(),
406
+ max_results: z.number().int().positive().optional()
407
+ });
408
+ const globArgsSchema = z.object({
409
+ pattern: z.string().min(1),
410
+ path: z.string().optional(),
411
+ max_results: z.number().int().positive().optional()
412
+ });
413
+ const getHostArgsSchema = z.object({
414
+ port: z.number().int().min(1).max(65535),
415
+ protocol: z.nativeEnum(PortProtocol).optional()
416
+ });
417
+ const appendOutput = (buffer, chunk, cap) => {
418
+ buffer.chunks.push(chunk);
419
+ buffer.size += chunk.byteLength;
420
+ while (buffer.size > cap && buffer.chunks.length > 1) {
421
+ const dropped = buffer.chunks.shift();
422
+ if (dropped == null) break;
423
+ buffer.size -= dropped.byteLength;
424
+ buffer.base += dropped.byteLength;
425
+ }
426
+ };
427
+ const readOutput = (buffer, offset, maxBytes) => {
428
+ const start = Math.max(offset, buffer.base);
429
+ const end = buffer.base + buffer.size;
430
+ const out = new Uint8Array(Math.max(0, Math.min(end - start, maxBytes)));
431
+ let position = buffer.base;
432
+ let written = 0;
433
+ for (const chunk of buffer.chunks) {
434
+ const chunkEnd = position + chunk.byteLength;
435
+ if (chunkEnd > start && written < out.byteLength) {
436
+ const from = Math.max(0, start - position);
437
+ const take = Math.min(chunk.byteLength - from, out.byteLength - written);
438
+ out.set(chunk.subarray(from, from + take), written);
439
+ written += take;
440
+ }
441
+ position = chunkEnd;
442
+ }
443
+ return {
444
+ data: utf8Decode(out),
445
+ offset: start,
446
+ next_offset: start + written,
447
+ skipped: start - offset,
448
+ available: end
449
+ };
450
+ };
451
+ const createSandboxMcpServer = (args) => {
452
+ const limits = { ...DEFAULT_LIMITS, ...args.limits };
453
+ let catalog = args.catalog ?? EMPTY_MCP_CATALOG;
454
+ let specs = toolSpecs({ ...args, catalog });
455
+ let byName = new Map(specs.map((spec) => [spec.descriptor.name, spec]));
456
+ const processes = /* @__PURE__ */ new Map();
457
+ const served = hostedServersOf(args);
458
+ let handler = null;
459
+ let learning = null;
460
+ let discovering = false;
461
+ const checked = /* @__PURE__ */ new Set();
462
+ const asError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
463
+ const reportError = (error) => args.onError?.(error);
464
+ const mergeCatalog = (next) => {
465
+ const merged = {
466
+ computerUse: next.computerUse ?? catalog.computerUse,
467
+ localMcp: next.localMcp ?? catalog.localMcp
468
+ };
469
+ if (JSON.stringify(merged) === JSON.stringify(catalog)) return false;
470
+ catalog = merged;
471
+ specs = toolSpecs({ ...args, catalog });
472
+ byName = new Map(specs.map((spec) => [spec.descriptor.name, spec]));
473
+ handler?.notify.toolsChanged();
474
+ return true;
475
+ };
476
+ const unknownCatalogs = () => ({
477
+ computerUse: served.computerUse && catalog.computerUse == null,
478
+ localMcp: served.localMcp && catalog.localMcp == null
479
+ });
480
+ const connections = createHostedMcpConnections({
481
+ getSandbox: () => sandboxFor(),
482
+ fetch: args.fetch,
483
+ name: args.name,
484
+ version: args.version
485
+ });
486
+ const learnFrom = (sandbox) => {
487
+ const needed = unknownCatalogs();
488
+ if (!needed.computerUse && !needed.localMcp) return Promise.resolve();
489
+ learning ??= (async () => {
490
+ try {
491
+ if (mergeCatalog(await connections.learn(sandbox, needed))) args.onCatalog?.(catalog);
492
+ } finally {
493
+ learning = null;
494
+ }
495
+ })();
496
+ return learning;
497
+ };
498
+ const sandboxFor = async () => {
499
+ const sandbox = await args.getSandbox();
500
+ learnFrom(sandbox).catch((cause) => reportError(asError(cause)));
501
+ return sandbox;
502
+ };
503
+ const beforeList = async (waitForDiscovery = false) => {
504
+ const needed = unknownCatalogs();
505
+ if (!needed.computerUse && !needed.localMcp) return;
506
+ const live = args.peekSandbox?.() ?? null;
507
+ if (live != null) {
508
+ const discovered = learnFrom(live).catch((cause) => reportError(asError(cause)));
509
+ if (waitForDiscovery || scopeOf(args).kind !== "all" /* All */) await discovered;
510
+ return;
511
+ }
512
+ if (discovering) return;
513
+ discovering = true;
514
+ sandboxFor().then((sandbox) => learnFrom(sandbox)).catch((cause) => {
515
+ discovering = false;
516
+ reportError(asError(cause));
517
+ });
518
+ };
519
+ const checkCatalog = (server) => {
520
+ const key = server.kind === SandboxHostedServerKind.Local ? `local:${server.name}` : "cu";
521
+ if (checked.has(key)) return;
522
+ checked.add(key);
523
+ void (async () => {
524
+ const tools = await connections.tools(server);
525
+ const next = server.kind === SandboxHostedServerKind.ComputerUse ? { computerUse: tools } : catalog.localMcp == null ? {} : { localMcp: { ...catalog.localMcp, [server.name]: tools } };
526
+ if (mergeCatalog(next)) args.onCatalog?.(catalog);
527
+ })().catch((cause) => {
528
+ checked.delete(key);
529
+ reportError(asError(cause));
530
+ });
531
+ };
532
+ const shell = createSandboxShell({ getSandbox: sandboxFor, mode: args.shell });
533
+ const isAllowed = async (spec, name, input) => {
534
+ for (const key of [name, ...spec.policyKeys]) {
535
+ const decision = args.toolPolicy?.[key];
536
+ if (decision === ToolApproval.Allow) return true;
537
+ if (decision === ToolApproval.Ask)
538
+ return args.approve == null ? false : args.approve(name, input);
539
+ }
540
+ return true;
541
+ };
542
+ const backgroundProcess = (id) => {
543
+ const entry = processes.get(id);
544
+ if (entry == null) throw new Error(`no process ${id}; start one with spawn`);
545
+ return entry;
546
+ };
547
+ const notifyProcess = (entry) => {
548
+ for (const listener of [...entry.changed]) listener();
549
+ };
550
+ const tailOf = (buffer) => {
551
+ const end = buffer.base + buffer.size;
552
+ const text = readOutput(
553
+ buffer,
554
+ end - limits.exitOutputChars * 4,
555
+ limits.exitOutputChars * 4
556
+ ).data;
557
+ return text.length <= limits.exitOutputChars ? text : text.slice(text.length - limits.exitOutputChars);
558
+ };
559
+ let closed = false;
560
+ const reportExit = (id, entry) => {
561
+ const report = args.onProcessExit;
562
+ if (report == null) return;
563
+ setTimeout(() => {
564
+ if (closed || entry.exitSeen) return;
565
+ entry.exitSeen = true;
566
+ report({
567
+ processId: id,
568
+ command: entry.command,
569
+ exitCode: entry.exitCode,
570
+ stdout: tailOf(entry.stdout),
571
+ stderr: tailOf(entry.stderr)
572
+ });
573
+ }, limits.exitReportGraceMs);
574
+ };
575
+ const pumpOutput = async (entry, stream, buffer) => {
576
+ const reader = stream.getReader();
577
+ try {
578
+ for (; ; ) {
579
+ const { done, value } = await reader.read();
580
+ if (done) break;
581
+ appendOutput(buffer, value, limits.processBufferBytes);
582
+ notifyProcess(entry);
583
+ }
584
+ } catch {
585
+ }
586
+ };
587
+ const run = async (name, rawArgs, signal) => {
588
+ switch (name) {
589
+ case "run" /* Run */: {
590
+ const input = runArgsSchema.parse(rawArgs);
591
+ const result = await shell.run({
592
+ command: input.command,
593
+ cwd: input.cwd,
594
+ env: input.env,
595
+ timeoutMs: input.timeout_ms ?? 12e4,
596
+ signal
597
+ });
598
+ return jsonResult({
599
+ exit_code: result.exitCode,
600
+ stdout: truncateMiddle(result.stdout, limits.maxOutputChars),
601
+ stderr: truncateMiddle(result.stderr, limits.maxOutputChars)
602
+ });
603
+ }
604
+ case "spawn" /* Spawn */: {
605
+ const input = spawnArgsSchema.parse(rawArgs);
606
+ if (processes.size >= limits.maxProcesses) {
607
+ for (const [id2, entry2] of processes)
608
+ if (entry2.exited && entry2.exitSeen) processes.delete(id2);
609
+ }
610
+ if (processes.size >= limits.maxProcesses) {
611
+ return errorResult(
612
+ `too many background processes (${limits.maxProcesses}); terminate one first`
613
+ );
614
+ }
615
+ const sandbox = await sandboxFor();
616
+ const child = await sandbox.spawn({
617
+ command: input.command,
618
+ shell: true,
619
+ cwd: input.cwd,
620
+ env: input.env
621
+ });
622
+ const id = `p${crypto.randomUUID().slice(0, 8)}`;
623
+ const entry = {
624
+ process: child,
625
+ stdout: { chunks: [], base: 0, size: 0 },
626
+ stderr: { chunks: [], base: 0, size: 0 },
627
+ exitCode: null,
628
+ exited: false,
629
+ changed: /* @__PURE__ */ new Set(),
630
+ command: input.command,
631
+ exitSeen: false
632
+ };
633
+ processes.set(id, entry);
634
+ void pumpOutput(entry, child.stdout, entry.stdout);
635
+ void pumpOutput(entry, child.stderr, entry.stderr);
636
+ child.wait().then(
637
+ (result) => {
638
+ entry.exited = true;
639
+ entry.exitCode = result.exitCode;
640
+ notifyProcess(entry);
641
+ reportExit(id, entry);
642
+ },
643
+ () => {
644
+ entry.exited = true;
645
+ notifyProcess(entry);
646
+ reportExit(id, entry);
647
+ }
648
+ );
649
+ return jsonResult({ process_id: id, pid: child.pid });
650
+ }
651
+ case "process_output" /* ProcessOutput */: {
652
+ const input = processOutputArgsSchema.parse(rawArgs);
653
+ const entry = backgroundProcess(input.process_id);
654
+ const stdoutOffset = input.stdout_offset ?? 0;
655
+ const stderrOffset = input.stderr_offset ?? 0;
656
+ const hasNews = () => entry.exited || entry.stdout.base + entry.stdout.size > stdoutOffset || entry.stderr.base + entry.stderr.size > stderrOffset;
657
+ const waitMs = Math.min(input.wait_ms ?? 0, 3e4);
658
+ if (waitMs > 0 && !hasNews()) {
659
+ await new Promise((resolve) => {
660
+ const done = () => {
661
+ clearTimeout(timer);
662
+ entry.changed.delete(listener);
663
+ signal?.removeEventListener("abort", done);
664
+ resolve();
665
+ };
666
+ const listener = () => {
667
+ if (hasNews()) done();
668
+ };
669
+ const timer = setTimeout(done, waitMs);
670
+ entry.changed.add(listener);
671
+ signal?.addEventListener("abort", done, { once: true });
672
+ });
673
+ }
674
+ const maxBytes = input.max_bytes ?? 65536;
675
+ if (entry.exited) entry.exitSeen = true;
676
+ return jsonResult({
677
+ stdout: readOutput(entry.stdout, stdoutOffset, maxBytes),
678
+ stderr: readOutput(entry.stderr, stderrOffset, maxBytes),
679
+ running: !entry.exited,
680
+ exit_code: entry.exitCode
681
+ });
682
+ }
683
+ case "process_input" /* ProcessInput */: {
684
+ const input = processInputArgsSchema.parse(rawArgs);
685
+ const entry = backgroundProcess(input.process_id);
686
+ await entry.process.write(input.data ?? "", { end: input.end === true });
687
+ return jsonResult({ written: input.data?.length ?? 0, stdin_closed: input.end === true });
688
+ }
689
+ case "process_terminate" /* ProcessTerminate */: {
690
+ const input = processTerminateArgsSchema.parse(rawArgs);
691
+ const entry = backgroundProcess(input.process_id);
692
+ entry.exitSeen = true;
693
+ await entry.process.kill(input.signal);
694
+ const result = await entry.process.wait().catch(() => ({ exitCode: entry.exitCode ?? -1 }));
695
+ processes.delete(input.process_id);
696
+ return jsonResult({ process_id: input.process_id, exit_code: result.exitCode });
697
+ }
698
+ case "read_file" /* ReadFile */: {
699
+ const input = readFileArgsSchema.parse(rawArgs);
700
+ const sandbox = await sandboxFor();
701
+ const path = resolvePosixPath(sandbox.defaultWorkingDirectory, input.path);
702
+ const stat = await sandbox.stat({ path, abortSignal: signal });
703
+ if (stat == null) return errorResult(`${path}: no such file`);
704
+ if (stat.type === SandboxEntryType.Directory)
705
+ return errorResult(`${path} is a directory; use list_dir`);
706
+ const link = {
707
+ type: "resource_link",
708
+ uri: `${SANDBOX_FILE_RESOURCE_SCHEME}://${encodeURI(path)}`,
709
+ name: path.split("/").pop() ?? path,
710
+ size: stat.size
711
+ };
712
+ const ranged = input.offset != null || input.limit != null;
713
+ const startLine = input.offset ?? 1;
714
+ const endLine = input.limit == null ? void 0 : startLine + input.limit - 1;
715
+ if (!ranged && stat.size > limits.maxInlineBytes) {
716
+ const head = await sandbox.readTextFile({
717
+ path,
718
+ startLine: 1,
719
+ endLine: 200,
720
+ abortSignal: signal
721
+ }) ?? "";
722
+ return {
723
+ content: [
724
+ {
725
+ type: "text",
726
+ text: `${path} is ${stat.size} bytes, too large to return whole. First 200 lines below; read more with offset and limit, or fetch the resource.
727
+
728
+ ${truncateMiddle(head, limits.maxOutputChars)}`
729
+ },
730
+ link
731
+ ]
732
+ };
733
+ }
734
+ const text = await sandbox.readTextFile({
735
+ path,
736
+ startLine: ranged ? startLine : void 0,
737
+ endLine,
738
+ abortSignal: signal
739
+ });
740
+ if (text == null) return errorResult(`${path}: no such file`);
741
+ if (text.includes("\0")) {
742
+ return {
743
+ content: [
744
+ { type: "text", text: `${path} is a binary file (${stat.size} bytes).` },
745
+ link
746
+ ]
747
+ };
748
+ }
749
+ return textResult(
750
+ truncateMiddle(text, Math.max(limits.maxOutputChars, limits.maxInlineBytes))
751
+ );
752
+ }
753
+ case "write_file" /* WriteFile */: {
754
+ const input = writeFileArgsSchema.parse(rawArgs);
755
+ const sandbox = await sandboxFor();
756
+ const path = resolvePosixPath(sandbox.defaultWorkingDirectory, input.path);
757
+ await sandbox.writeTextFile({ path, content: input.content, abortSignal: signal });
758
+ return textResult(
759
+ `Wrote ${new TextEncoder().encode(input.content).byteLength} bytes to ${path}`
760
+ );
761
+ }
762
+ case "edit_file" /* EditFile */: {
763
+ const input = editFileArgsSchema.parse(rawArgs);
764
+ const sandbox = await sandboxFor();
765
+ const path = resolvePosixPath(sandbox.defaultWorkingDirectory, input.path);
766
+ const text = await sandbox.readTextFile({ path, abortSignal: signal });
767
+ if (text == null) return errorResult(`${path}: no such file`);
768
+ const count = text.split(input.old_string).length - 1;
769
+ if (count === 0) return errorResult(`old_string was not found in ${path}`);
770
+ if (count > 1 && input.replace_all !== true) {
771
+ return errorResult(
772
+ `old_string occurs ${count} times in ${path}; add surrounding context or set replace_all`
773
+ );
774
+ }
775
+ const next = input.replace_all === true ? text.split(input.old_string).join(input.new_string) : text.replace(input.old_string, () => input.new_string);
776
+ await sandbox.writeTextFile({ path, content: next, abortSignal: signal });
777
+ return textResult(
778
+ `Replaced ${input.replace_all === true ? count : 1} occurrence(s) in ${path}`
779
+ );
780
+ }
781
+ case "list_dir" /* ListDir */: {
782
+ const input = listDirArgsSchema.parse(rawArgs);
783
+ const sandbox = await sandboxFor();
784
+ const path = resolvePosixPath(sandbox.defaultWorkingDirectory, input.path ?? ".");
785
+ const entries = await sandbox.readDir({ path, abortSignal: signal });
786
+ return jsonResult({
787
+ path,
788
+ entries: entries.map((entry) => ({
789
+ name: entry.name,
790
+ type: entry.type,
791
+ size: entry.size
792
+ }))
793
+ });
794
+ }
795
+ case "stat" /* Stat */: {
796
+ const input = pathArgsSchema.parse(rawArgs);
797
+ const sandbox = await sandboxFor();
798
+ const path = resolvePosixPath(sandbox.defaultWorkingDirectory, input.path);
799
+ const stat = await sandbox.stat({ path, abortSignal: signal });
800
+ return jsonResult(stat == null ? { path, exists: false } : { exists: true, ...stat });
801
+ }
802
+ case "grep" /* Grep */: {
803
+ const input = grepArgsSchema.parse(rawArgs);
804
+ const sandbox = await sandboxFor();
805
+ const result = await grepFiles({
806
+ sandbox,
807
+ cwd: sandbox.defaultWorkingDirectory,
808
+ pattern: input.pattern,
809
+ path: input.path,
810
+ glob: input.glob,
811
+ ignoreCase: input.ignore_case,
812
+ maxResults: input.max_results ?? 200,
813
+ signal
814
+ });
815
+ const lines = result.matches.map((match) => `${match.path}:${match.line}:${match.text}`);
816
+ if (result.truncated) lines.push("[results truncated]");
817
+ return textResult(lines.length === 0 ? "No matches." : lines.join("\n"));
818
+ }
819
+ case "glob" /* Glob */: {
820
+ const input = globArgsSchema.parse(rawArgs);
821
+ const sandbox = await sandboxFor();
822
+ const result = await globFiles({
823
+ sandbox,
824
+ cwd: sandbox.defaultWorkingDirectory,
825
+ pattern: input.pattern,
826
+ path: input.path,
827
+ maxResults: input.max_results ?? 500,
828
+ signal
829
+ });
830
+ const lines = [...result.paths];
831
+ if (result.truncated) lines.push("[results truncated]");
832
+ return textResult(lines.length === 0 ? "No files matched." : lines.join("\n"));
833
+ }
834
+ case "get_host" /* GetHost */: {
835
+ const input = getHostArgsSchema.parse(rawArgs);
836
+ const sandbox = await sandboxFor();
837
+ const endpoint = await sandbox.getPortEndpoint({
838
+ port: input.port,
839
+ protocol: input.protocol
840
+ });
841
+ return jsonResult({ url: endpoint.url, headers: endpoint.headers ?? {} });
842
+ }
843
+ default:
844
+ return errorResult(`unknown tool ${name}`);
845
+ }
846
+ };
847
+ const callTool = async (name, rawArgs, signal) => {
848
+ const spec = byName.get(name);
849
+ if (spec == null) return errorResult(`unknown tool ${name}`);
850
+ if (!await isAllowed(spec, name, rawArgs)) return errorResult(`${name} was not approved`);
851
+ try {
852
+ if (spec.hosted != null) {
853
+ try {
854
+ return await connections.call(spec.hosted.server, spec.hosted.tool.name, rawArgs);
855
+ } finally {
856
+ checkCatalog(spec.hosted.server);
857
+ }
858
+ }
859
+ return await run(name, rawArgs, signal);
860
+ } catch (error) {
861
+ if (error instanceof z.ZodError)
862
+ return errorResult(`invalid arguments: ${z.prettifyError(error)}`);
863
+ return errorResult(error instanceof Error ? error.message : String(error));
864
+ }
865
+ };
866
+ const readResource = async (uri) => {
867
+ const path = decodeURI(uri.pathname);
868
+ const spec = byName.get("read_file" /* ReadFile */);
869
+ if (spec == null || !await isAllowed(spec, "read_file" /* ReadFile */, { path }))
870
+ throw new Error(`${path}: file read was not approved`);
871
+ const sandbox = await sandboxFor();
872
+ const bytes = await sandbox.readBinaryFile({ path });
873
+ if (bytes == null) throw new Error(`${path}: no such file`);
874
+ const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
875
+ if (!text.includes("\0") && !text.includes("\uFFFD")) {
876
+ return { contents: [{ uri: uri.href, mimeType: "text/plain", text }] };
877
+ }
878
+ return {
879
+ contents: [
880
+ { uri: uri.href, mimeType: "application/octet-stream", blob: bytesToBase64(bytes) }
881
+ ]
882
+ };
883
+ };
884
+ const buildServer = () => {
885
+ const server = new McpServer(
886
+ { name: args.name ?? "final-sandbox", version: args.version ?? "1" },
887
+ // The hosted servers' tools can follow the first list (see beforeList).
888
+ {
889
+ capabilities: {
890
+ tools: { listChanged: served.computerUse || served.localMcp },
891
+ resources: {}
892
+ }
893
+ }
894
+ );
895
+ for (const spec of specs) {
896
+ server.registerTool(
897
+ spec.descriptor.name,
898
+ {
899
+ title: spec.descriptor.annotations?.title,
900
+ description: spec.descriptor.description,
901
+ inputSchema: fromJsonSchema(spec.descriptor.inputSchema),
902
+ annotations: spec.descriptor.annotations
903
+ },
904
+ (input, ctx) => callTool(spec.descriptor.name, input, ctx.mcpReq.signal)
905
+ );
906
+ }
907
+ if (args.capabilities.includes(SandboxCapability.Files)) {
908
+ server.registerResource(
909
+ "sandbox-file",
910
+ new ResourceTemplate(`${SANDBOX_FILE_RESOURCE_SCHEME}://{+path}`, { list: void 0 }),
911
+ { description: "A file in the sandbox, by absolute path." },
912
+ (uri) => readResource(uri)
913
+ );
914
+ }
915
+ return server;
916
+ };
917
+ const isListRequest = async (request) => {
918
+ if (request.method !== "POST") return false;
919
+ try {
920
+ const body = await request.clone().json();
921
+ return (Array.isArray(body) ? body : [body]).some(
922
+ (message) => listRequestSchema.safeParse(message).success
923
+ );
924
+ } catch {
925
+ return false;
926
+ }
927
+ };
928
+ const getHandler = () => {
929
+ handler ??= createMcpHandler(() => buildServer(), { onerror: args.onError });
930
+ return handler;
931
+ };
932
+ return {
933
+ get descriptors() {
934
+ return specs.map((spec) => spec.descriptor);
935
+ },
936
+ fetch: async (request) => {
937
+ if (await isListRequest(request)) await beforeList();
938
+ return getHandler().fetch(request);
939
+ },
940
+ listTools: () => specs.map((spec) => spec.descriptor),
941
+ refreshTools: async () => {
942
+ await beforeList(true);
943
+ return specs.map((spec) => spec.descriptor);
944
+ },
945
+ updateCatalog: (next) => {
946
+ mergeCatalog(next);
947
+ },
948
+ callTool,
949
+ close: async () => {
950
+ closed = true;
951
+ await shell.close();
952
+ for (const entry of processes.values()) await entry.process.kill().catch(() => void 0);
953
+ processes.clear();
954
+ await connections.close();
955
+ await handler?.close();
956
+ }
957
+ };
958
+ };
959
+ export {
960
+ COMPUTER_USE_SERVER_NAME,
961
+ SANDBOX_FILE_RESOURCE_SCHEME,
962
+ SandboxHostedServerKind2 as SandboxHostedServerKind,
963
+ SandboxMcpScopeKind,
964
+ SandboxMcpTool,
965
+ SandboxShellMode2 as SandboxShellMode,
966
+ createSandboxMcpServer,
967
+ sandboxMcpToolDescriptors,
968
+ sandboxMcpToolPolicyKeys,
969
+ withHostedMcpEndpoints
970
+ };
971
+ //# sourceMappingURL=mcp.mjs.map