Defining Tools
Use defineTool() to create a ToolDefinition while preserving literal tool names and handler types.
Basic definition
Section titled “Basic definition”import { defineTool } from "@kaoruisaac/pedelec";
const updateCounter = defineTool({ name: "update_counter", description: "Increase or decrease the visible counter by a signed delta.", argsSchema: { type: "object", properties: { delta: { type: "integer", description: "Signed amount to add to the counter.", minimum: -100, maximum: 100, }, }, required: ["delta"], }, timeoutMs: 10_000, handler: (args: { delta: number }) => { counter.value += args.delta; return { value: counter.value }; },});defineTool() returns the definition unchanged. Its value is type inference and a consistent authoring pattern; runtime validation happens when the tools are normalized for session creation.
ToolDefinition
Section titled “ToolDefinition”type ToolDefinition< TArgs = unknown, TResult = unknown, TName extends string = string,> = { name: TName; description: string; argsSchema: ToolArgsSchema; timeoutMs?: number; handler?: ToolSpecificHandler<TArgs, TResult>;};Name rules
Section titled “Name rules”Tool names must match:
^[a-zA-Z][a-zA-Z0-9_.-]*$That means:
- first character must be an ASCII letter;
- later characters may be letters, digits,
_,., or-; - spaces are not allowed;
- names must be unique within one
skills.toolsarray.
Recommended style is lowercase snake case:
get_current_pageupdate_countereditor.replace_selectionUse stable names. Renaming a tool changes the agent-facing capability and requires handler updates.
Write a useful description
Section titled “Write a useful description”The description helps the agent choose the correct tool. State:
- what the tool does;
- what state it reads or changes;
- important constraints; and
- when a similar tool should be preferred.
// Better"Replace the currently selected editor text. Fails when there is no selection. Use insert_text when the user has no selection."
// Too vague"Edit text."Do not hide destructive behavior. A description should make side effects explicit.
Argument schema
Section titled “Argument schema”The root must be an object schema, even for a no-argument tool.
const noArgs = { type: "object", properties: {}, required: [],} satisfies ToolArgsSchema;For parameters:
const argsSchema = { type: "object", properties: { documentId: { type: "string", description: "Application document identifier.", minLength: 1, }, mode: { type: "string", enum: ["append", "replace"], }, }, required: ["documentId", "mode"],} satisfies ToolArgsSchema;The schema is a Pedelec-supported subset, not full JSON Schema. See Tool Args Schema.
Timeout
Section titled “Timeout”timeoutMs is optional and must be a positive integer.
timeoutMs: 60_000If omitted, Core currently uses its default tool timeout of 60 seconds. Choose a longer value only for intentionally interactive or long-running operations. A large timeout should be paired with visible UI and cleanup behavior.
The timeout does not cancel JavaScript inside the handler. If the runtime stops waiting first, a late result can fail to submit.
Inline handler
Section titled “Inline handler”const readSelection = defineTool({ name: "get_selected_text", description: "Read the text currently selected in the page.", argsSchema: noArgs, handler: () => ({ text: window.getSelection()?.toString() ?? "", }),});The handler is stored in the SDK session and removed from the serializable manifest. It is never written into the workspace.
Inline handlers are convenient when definition and implementation naturally live together. Register a named handler later when implementation belongs to a mounted component or must override the default.
Type the handler
Section titled “Type the handler”You can type the handler parameter directly:
const resize = defineTool({ name: "resize_canvas", description: "Resize the current canvas.", argsSchema: { type: "object", properties: { width: { type: "integer", minimum: 1 }, height: { type: "integer", minimum: 1 }, }, required: ["width", "height"], }, handler: (args: { width: number; height: number }) => { canvas.resize(args.width, args.height); return { width: args.width, height: args.height }; },});Or provide generics:
type ResizeArgs = { width: number; height: number };type ResizeResult = { width: number; height: number };
const resize = defineTool<ResizeArgs, ResizeResult>({ name: "resize_canvas", description: "Resize the current canvas.", argsSchema: resizeSchema, handler: (args) => canvas.resize(args.width, args.height),});TypeScript types do not validate runtime agent input. Validate at the handler boundary.
Preserve tool-name literals
Section titled “Preserve tool-name literals”const tools = [getCurrentPage, updateCounter] as const;
const session = await pedelec.createSession({ provider: "codex", skills: { guidance: "Use the declared tools for page operations.", tools, },});
session.onTool("update_counter", handleCounter);// A misspelled literal can be rejected by TypeScript for this session type.Without a readonly/literal-preserving array, tool names may widen to string, reducing the value of the typed named handler API.
Nested object example
Section titled “Nested object example”const createCard = defineTool({ name: "create_card", description: "Create a card in the active board column.", argsSchema: { type: "object", properties: { card: { type: "object", properties: { title: { type: "string", minLength: 1, maxLength: 120 }, labels: { type: "array", items: { type: "string" }, maxItems: 10, uniqueItems: true, }, }, required: ["title"], }, }, required: ["card"], },});Validation failures during creation
Section titled “Validation failures during creation”Session creation rejects with INVALID_INPUT for issues such as:
- invalid or duplicate name;
- empty description;
- non-positive/non-integer timeout;
- missing or non-object root schema;
- legacy
inputshorthand; or - a schema that cannot be serialized.
Fix the definition in application code. These are developer errors, not user retry conditions.
Continue with Tool Args Schema.