Use a custom tool when the agent needs executable behavior that is not provided by the built-in file and terminal tools.

Complete example

Create agent/extensions/customer-tools.mjs:
export default function customerTools(pi) {
  pi.registerTool({
    name: 'lookup_customer',
    label: 'Look up customer',
    description: 'Look up a customer using their stable customer ID.',
    parameters: {
      type: 'object',
      properties: {
        customerId: {
          type: 'string',
          description: 'Stable customer identifier.',
        },
      },
      required: ['customerId'],
      additionalProperties: false,
    },
    async execute(_toolCallId, { customerId }, _signal, _onUpdate, ctx) {
      const source = ctx.external?.provider ?? 'api';

      return {
        content: [
          {
            type: 'text',
            text: `Customer ${customerId} requested from ${source}.`,
          },
        ],
        details: {
          customerId,
          source,
        },
      };
    },
  });
}

Registration fields

FieldRequirement
name1–64 letters, numbers, underscores, or hyphens
labelOptional human-readable label
descriptionRequired explanation used by the model
parametersRequired JSON-Schema-like object
executeAsync function called in the sandbox extension host
Write descriptions that explain when to use the tool. Keep the schema narrow and reject unexpected fields with additionalProperties: false.

Execution arguments

async execute(toolCallId, params, signal, onUpdate, ctx) {
  // ...
}
ArgumentHosted behavior
toolCallIdStable identifier for this tool invocation
paramsModel arguments validated against the declared schema
signalReserved by the hosted contract; currently may be undefined
onUpdateReserved for progress updates; currently may be undefined
ctxBounded runtime context, including ctx.external
Do not depend on signal or onUpdate until their hosted behavior is documented as supported.

Return a result

Every successful execution returns:
return {
  content: [{ type: "text", text: "Result visible to the model" }],
  details: { optional: "structured diagnostic data" },
};
Hosted custom tools return text content only.

Failure behavior

Throw an Error with a concise, non-secret message when execution cannot complete:
throw new Error('Customer record was not found.');
Tool execution is bounded to 60 seconds. Do not return credentials, raw provider responses containing secrets, or unbounded payloads. Validate with salambo manifest, then use a real deploy and smoke test.