Custom Tools
Add your own tools to extend what the agent can do.
noumen's Tool interface lets you define custom tools that the agent can call during its runs. Custom tools are passed through Code options and are available alongside the built-in tools.
The Tool interface
interface Tool {
name: string;
description: string;
parameters: ToolParameters;
mcpInfo?: { serverName: string; toolName: string };
call(args: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult>;
}Where:
parametersfollows JSON Schema format withtype: "object",properties, andrequiredToolContextprovides access tofs(VirtualFs),computer(VirtualComputer), andcwdToolResulthascontent(string) and optionalisError(boolean)
Adding custom tools
Pass tools in the Code options:
import { Code, OpenAIProvider, LocalFs, LocalComputer } from "noumen";
import type { Tool, ToolContext, ToolResult } from "noumen";
const fetchUrlTool: Tool = {
name: "FetchURL",
description: "Fetch the contents of a URL",
parameters: {
type: "object",
properties: {
url: { type: "string", description: "The URL to fetch" },
},
required: ["url"],
},
async call(args: Record<string, unknown>): Promise<ToolResult> {
const url = args.url as string;
try {
const response = await fetch(url);
const text = await response.text();
return { content: text.slice(0, 10000) };
} catch (err) {
return { content: `Error: ${err}`, isError: true };
}
},
};
const code = new Code({
aiProvider: new OpenAIProvider({ apiKey: "..." }),
virtualFs: new LocalFs({ basePath: "." }),
virtualComputer: new LocalComputer({ defaultCwd: "." }),
options: {
tools: [fetchUrlTool],
},
});Using ToolContext
The call function receives a ToolContext with access to the agent's sandboxed infrastructure. Always use ctx.fs and ctx.computer for file and shell operations — these route through the VirtualFs/VirtualComputer sandbox boundary, so your tool automatically inherits whatever isolation level the consumer configured (local, remote container, etc.):
const myTool: Tool = {
name: "ListProjectFiles",
description: "List all TypeScript files in the project",
parameters: {
type: "object",
properties: {},
},
async call(_args, ctx: ToolContext) {
const entries = await ctx.fs.readdir(ctx.cwd, { recursive: true });
const tsFiles = entries
.filter((e) => e.isFile && e.name.endsWith(".ts"))
.map((e) => e.path);
return { content: tsFiles.join("\n") };
},
};Error handling
Return isError: true to signal that the tool call failed. The model will see the error message and can decide to retry or take a different approach.
async call(args, ctx) {
try {
// ... do work
return { content: "Success" };
} catch (err) {
return { content: `Failed: ${err}`, isError: true };
}
}MCP tools
Tools discovered from MCP servers are automatically added with the same Tool interface. They have an mcpInfo field that identifies their origin server and original tool name. See MCP for details.