Agent
Custom Tools
There are two common patterns for sandbox-local custom tooling:
| MCP Server | Skill | |
|---|---|---|
| How it works | Agent connects to an MCP server (mcpServers) | Agent follows SKILL.md instructions and runs scripts |
| Best for | Typed tool calls and structured protocols | Lightweight task-specific guidance |
| Requires | MCP server process (stdio/http/sse) | Script + SKILL.md |
Option A: MCP server (stdio)
Section titled “Option A: MCP server (stdio)”-
Write and bundle your MCP server
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";import { z } from "zod";const server = new McpServer({ name: "rand", version: "1.0.0" });server.tool("random_number","Generate a random integer between min and max",{min: z.number(),max: z.number(),},async ({ min, max }) => ({content: [{ type: "text", text: String(Math.floor(Math.random() * (max - min + 1)) + min) }],}),);await server.connect(new StdioServerTransport());Terminal window npx esbuild src/mcp-server.ts --bundle --format=cjs --platform=node --target=node18 --outfile=dist/mcp-server.cjs -
Upload it into the sandbox
import { SandboxAgent } from "sandbox-agent";import fs from "node:fs";const sdk = await SandboxAgent.connect({ baseUrl: "http://127.0.0.1:2468" });const content = await fs.promises.readFile("./dist/mcp-server.cjs");await sdk.writeFsFile({ path: "/opt/mcp/custom-tools/mcp-server.cjs" }, content);Terminal window curl -X PUT "http://127.0.0.1:2468/v1/fs/file?path=/opt/mcp/custom-tools/mcp-server.cjs" \--data-binary @./dist/mcp-server.cjs -
Register MCP config and create a session
await sdk.setMcpConfig({directory: "/workspace",mcpName: "customTools",},{type: "local",command: "node",args: ["/opt/mcp/custom-tools/mcp-server.cjs"],},);const session = await sdk.createSession({agent: "claude",cwd: "/workspace",});await session.prompt([{ type: "text", text: "Use the random_number tool with min=1 and max=10." },]);
Option B: Skills
Section titled “Option B: Skills”-
Write script + skill file
const min = Number(process.argv[2]);const max = Number(process.argv[3]);if (Number.isNaN(min) || Number.isNaN(max)) {console.error("Usage: random-number <min> <max>");process.exit(1);}console.log(Math.floor(Math.random() * (max - min + 1)) + min);---name: random-numberdescription: Generate a random integer between min and max.---Run:```bashnode /opt/skills/random-number/random-number.cjs <min> <max>```Terminal window npx esbuild src/random-number.ts --bundle --format=cjs --platform=node --target=node18 --outfile=dist/random-number.cjs -
Upload files
import fs from "node:fs";const script = await fs.promises.readFile("./dist/random-number.cjs");await sdk.writeFsFile({ path: "/opt/skills/random-number/random-number.cjs" }, script);const skill = await fs.promises.readFile("./SKILL.md");await sdk.writeFsFile({ path: "/opt/skills/random-number/SKILL.md" }, skill); -
Use in a session
const session = await sdk.createSession({agent: "claude",cwd: "/workspace",});await session.prompt([{ type: "text", text: "Use the random-number skill to pick a number from 1 to 100." },]);
- The sandbox runtime must include Node.js (or your chosen runtime).
- For persistent skill-source wiring by directory, see Skills.