NOIR
Guides

Create a custom tool

Add one well-bounded operation with schema validation, policy metadata, progress, and idempotency.

Use a custom tool for one operation that does not need a shared client or lifecycle bundle.

import { jsonSchema, tool } from '@noir-agent/agent'

const createTicket = tool({
  description: 'Create one support ticket for the current user.',
  input: jsonSchema<{ title: string; body: string }>({
    type: 'object',
    properties: {
      title: { type: 'string', minLength: 1, maxLength: 120 },
      body: { type: 'string', minLength: 1, maxLength: 8_000 },
    },
    required: ['title', 'body'],
    additionalProperties: false,
  }),
  effect: 'write',
  approval: 'requester',
  retry: 'idempotent',
  approvalSummary: ({ title }) => `Create support ticket “${title}”`,
  maxOutputBytes: 4_000,
  async execute(input, context) {
    await context.progress('Creating the ticket…')
    return support.createTicket({
      ...input,
      requesterId: context.actor.id,
      idempotencyKey: context.idempotencyKey,
      signal: context.signal,
    })
  },
  project(ticket) {
    return { id: ticket.id, url: ticket.url, status: ticket.status }
  },
})

Input validation

input.parse must reject malformed model output. jsonSchema creates a schema whose parser validates common JSON Schema constraints. The optional Zod bridge is available from @noir-agent/agent/schema/zod when your project already uses Zod.

Output projection

Provider responses often contain internal IDs, billing fields, raw headers, or nested data irrelevant to the model. Validate the response when correctness matters, then project a small model-facing shape.

Context

ToolContext includes the execution and call IDs, actor, channel, current attachments, previous messages, normalized connector clients, progress delivery, attachment resolution, abort signal, and stable idempotency key.

Registering

tools: { support: { createTicket } }

The model sees support.createTicket. Keep descriptions specific about when the operation is appropriate and what it returns. Tool policy and authorization remain in code, not in prose alone.

On this page