Custom Tools
Code Runners
Code runners handle anything that needs custom logic — calculations, data processing, content generation, and more. Your AI writes the JavaScript; it runs in a secure, isolated container that can't access your other data.
You don't write this code
The JavaScript below is what your AI generates. You just describe what you want: "build a tool that calculates statistics for any list of numbers."
How Code Tools Work
Code tools run in Cloudflare Sandbox - VM-isolated containers that:
- Execute JavaScript/TypeScript in Node.js 20
- Have no access to your Kyew data
- Cannot make unauthorized network requests
- Run with configurable timeout and memory limits
┌─────────────────────────────────────────┐
│ Kyew Worker │
│ │
│ ┌────────────────────────────────────┐ │
│ │ Cloudflare Sandbox Container │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Your Code (Isolated VM) │ │ │
│ │ │ │ │ │
│ │ │ export default { │ │ │
│ │ │ fetch(request) { ... } │ │ │
│ │ │ } │ │ │
│ │ └──────────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
Creating Code Tools
Use create_code_tool to define a new code tool:
create_code_tool({
name: "calculate_statistics",
description: "Calculate statistical measures for a list of numbers",
input_schema: {
type: "object",
properties: {
numbers: {
type: "array",
items: { type: "number" },
description: "Array of numbers to analyze"
}
},
required: ["numbers"]
},
runtime: "javascript",
code: `
export default {
async fetch(request) {
const { numbers } = await request.json();
const sum = numbers.reduce((a, b) => a + b, 0);
const mean = sum / numbers.length;
const sorted = [...numbers].sort((a, b) => a - b);
const median = sorted[Math.floor(sorted.length / 2)];
const variance = numbers.reduce((acc, n) => acc + Math.pow(n - mean, 2), 0) / numbers.length;
const stdDev = Math.sqrt(variance);
return Response.json({
count: numbers.length,
sum,
mean,
median,
min: Math.min(...numbers),
max: Math.max(...numbers),
variance,
standardDeviation: stdDev
});
}
}
`
})
Code Structure
Your code must export a default object with a fetch method:
export default {
async fetch(request) {
// Get input from request body
const input = await request.json();
// Do your processing
const result = processInput(input);
// Return Response with JSON
return Response.json(result);
}
}
Required Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string | Unique tool name |
| description | string | What the tool does |
| input_schema | object | JSON Schema for inputs |
| code | string | JavaScript/TypeScript code |
| runtime | string | "javascript" or "typescript" |
Optional Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| timeout_ms | number | 10000 | Execution timeout in ms |
| memory_limit_mb | number | 64 | Memory limit in MB |
| allowed_domains | string[] | [] | Domains the code can fetch from |
| env_vars | object | {} | Environment variables |
Approval Workflow
Code tools require approval before they can be used. This is a security measure since code execution carries more risk than data transforms.
1. Create (Draft Status)
create_code_tool({ ... })
// → Tool created with status: "draft"
2. Review Pending Tools
"list pending code tools"
3. Approve or Reject
"approve code tool tool-abc123 with note 'reviewed - safe statistical functions'"
Or reject:
"reject code tool tool-abc123 because it attempts to access external URLs without allowed_domains"
4. Use the Tool
Once approved, the tool becomes available as a regular MCP tool:
"use calculate_statistics with numbers [1, 2, 3, 4, 5]"
Example: ASCII Art Generator
create_code_tool({
name: "ascii_art_banner",
description: "Generate ASCII art text banners",
input_schema: {
type: "object",
properties: {
text: { type: "string", description: "Text to convert" },
style: {
type: "string",
enum: ["standard", "banner", "big"],
description: "Art style"
}
},
required: ["text"]
},
runtime: "javascript",
code: `
export default {
async fetch(request) {
const { text, style = 'standard' } = await request.json();
// Simple block letter implementation
const letters = {
'A': [' A ', ' A A ', 'AAAAA', 'A A', 'A A'],
'B': ['BBBB ', 'B B', 'BBBB ', 'B B', 'BBBB '],
// ... more letters
};
const lines = ['', '', '', '', ''];
for (const char of text.toUpperCase()) {
const letter = letters[char] || [' ', ' ', ' ', ' ', ' '];
for (let i = 0; i < 5; i++) {
lines[i] += letter[i] + ' ';
}
}
return Response.json({
banner: lines.join('\\n'),
text: text
});
}
}
`
})
Example: Data Validator
create_code_tool({
name: "validate_config",
description: "Validate configuration objects against a schema",
input_schema: {
type: "object",
properties: {
config: { type: "object", description: "Configuration to validate" },
rules: {
type: "array",
items: {
type: "object",
properties: {
field: { type: "string" },
required: { type: "boolean" },
type: { type: "string" },
pattern: { type: "string" }
}
}
}
},
required: ["config", "rules"]
},
runtime: "javascript",
code: `
export default {
async fetch(request) {
const { config, rules } = await request.json();
const errors = [];
for (const rule of rules) {
const value = config[rule.field];
if (rule.required && value === undefined) {
errors.push(\`Missing required field: \${rule.field}\`);
continue;
}
if (value !== undefined && rule.type && typeof value !== rule.type) {
errors.push(\`\${rule.field} should be \${rule.type}, got \${typeof value}\`);
}
if (value && rule.pattern && !new RegExp(rule.pattern).test(value)) {
errors.push(\`\${rule.field} does not match pattern \${rule.pattern}\`);
}
}
return Response.json({
valid: errors.length === 0,
errors
});
}
}
`
})
Making HTTP Requests
Code tools can fetch external URLs if you specify allowed_domains:
create_code_tool({
name: "fetch_github_repo",
description: "Fetch repository information from GitHub",
input_schema: {
type: "object",
properties: {
owner: { type: "string" },
repo: { type: "string" }
},
required: ["owner", "repo"]
},
runtime: "javascript",
allowed_domains: ["api.github.com"],
code: `
export default {
async fetch(request) {
const { owner, repo } = await request.json();
const response = await fetch(
\`https://api.github.com/repos/\${owner}/\${repo}\`,
{ headers: { 'User-Agent': 'Memory-Alpha-Tool' } }
);
if (!response.ok) {
return Response.json({ error: 'Repository not found' }, { status: 404 });
}
const data = await response.json();
return Response.json({
name: data.name,
description: data.description,
stars: data.stargazers_count,
forks: data.forks_count,
language: data.language
});
}
}
`
})
Testing Code Tools
Before approval, test your tools:
"test code tool tool-abc123 with input { \"numbers\": [1, 2, 3, 4, 5] }"
This runs the tool in the sandbox and returns the result without affecting production.
Security Considerations
What Code Tools CAN Do
- Process input data
- Perform calculations
- Fetch from allowed domains
- Use standard JavaScript APIs
What Code Tools CANNOT Do
- Access Kyew database
- Read other users' data
- Access the worker's environment
- Make requests to non-allowed domains
- Run indefinitely (timeout enforced)
- Use unlimited memory (limit enforced)
Best Practices
- Validate inputs - Don't trust input blindly
- Handle errors - Return meaningful error responses
- Limit scope - Keep tools focused on specific tasks
- Specify allowed_domains - Only add domains you actually need
- Review before approving - Check for security issues
Troubleshooting
"Execution timeout"
Your code took too long. Optimize or increase timeout_ms.
"Memory limit exceeded"
Your code used too much memory. Optimize or increase memory_limit_mb.
"Fetch failed: domain not allowed"
Add the domain to allowed_domains when creating the tool.
"Unexpected token 'export'"
Ensure your code uses ESM syntax (export default { ... }), not CommonJS.