Tools

A tool is a single capability an agent can call mid-run. The runtime ships a large built-in registry (decorator-registered Python functions) plus any custom tools you register through this namespace. Custom tools are DB-backed rows with kindstub, webhook (per the runtime's create_tool validator) — there is no mcp/http/bash/python kind enum, and there is no inputSchema/outputSchema/displayName/httpUrl field anywhere on the real object.

For the runtime tool contract, see API → Tools.

Auth. Every route in this namespace requires the control plane's x-catentio-session header, which this SDK never sends. Neither apiKey mode nor Bearer-session mode authenticates here. See SDKs → Auth: what actually works.

Namespace

catentio.tools — the CRUD surface this SDK wraps:

catentio.tools.list()
catentio.tools.get(slug)
catentio.tools.create(input)
catentio.tools.update(slug, patch)
catentio.tools.delete(slug)

The control plane also has POST /v1/tools/api-test, POST /v1/tools/{slug}/install (+ installs/approve/reject), and POST /v1/tools/{slug}/run — none of them are wrapped here.

Methods

tools.list

Signature. catentio.tools.list(token?): Promise<{ data: ToolListRow[] }>

import { CatentioSaasClient } from '@forjio/catentio-saas-node';

const catentio = new CatentioSaasClient({ session });

const { data: tools } = await catentio.tools.list();
const customs = tools.filter(t => !t.is_builtin);
console.log(`${customs.length} custom tools registered`);

Real list-row shape:

interface ToolListRow {
  slug: string;
  description: string;
  module: string;              // '(db)' for custom tools
  category: string;             // derived from the module path, or 'custom'
  sensitive: boolean;
  param_names: string[];
  param_count: number;
  is_builtin: boolean;          // snake_case, not 'isBuiltin'
  // present only when is_builtin === false:
  kind?: string;
  shape?: string;
  access?: string;
}

There is no displayName, inputSchema, outputSchema, mcpUrl/httpUrl/bashScript/pythonCode, createdAt, or updatedAt field on this row — none of those exist.

tools.get

Signature. catentio.tools.get(slug, token?): Promise<ToolDetail>

const tool = await catentio.tools.get('discord-send-message');
console.log(tool.params_schema);   // JSON Schema — not 'inputSchema'

Real detail shape (built-in):

interface BuiltinToolDetail {
  id: number | null;             // the tool's doc-twin graph node id, if it has one
  slug: string;
  description: string;
  module: string;
  category: string;
  sensitive: boolean;
  params_schema: object;
  recent_invocations: Array<{ id: number; ts: string; run_id: string; agent: string | null; transport: string | null; params_preview: unknown }>;
  is_builtin: true;
}

Custom tools add: kind ('stub' | 'webhook'), shape, access, body (kind-specific — e.g. url/method for webhook), integration, schema, version, and is_builtin: false.

tools.create

Signature. catentio.tools.create(input, token?): Promise<ToolDetail>

Real fields: slug (required, ≤64 chars), description?, kind? ('stub' or 'webhook', default 'stub'), schema? (JSON Schema object), body? (kind-specific object). There is no displayName, mcpUrl, httpUrl/httpMethod/httpHeaders, bashScript, or pythonCode field — none of those exist. Built-in tools cannot be created here. Creation is subject to a custom_tools tier cap; a duplicate slug gets 409.

const tool = await catentio.tools.create({
  slug: 'jira-find-ticket',
  description: 'Look up a Jira ticket by key.',
  kind: 'webhook',
  body: { url: 'https://acme.atlassian.net/rest/api/3/issue/{key}', method: 'GET' },
  schema: {
    type: 'object',
    properties: { key: { type: 'string' } },
    required: ['key'],
  },
});

tools.update

Signature. catentio.tools.update(slug, patch, token?): Promise<ToolDetail>

Built-in tools reject update with 400 ("builtin tools are decorator-registered and cannot be edited via the portal"), not 409 builtin_tool.

await catentio.tools.update('jira-find-ticket', {
  description: 'Look up a Jira ticket by key, includes worklog summary.',
});

tools.delete

Signature. catentio.tools.delete(slug, token?): Promise<void>

Built-in tools reject delete with 400 ("builtin tools cannot be deleted via the portal"), not 409 builtin_tool.

await catentio.tools.delete('jira-find-ticket');

Common patterns

Audit custom vs built-in

const { data: tools } = await catentio.tools.list();
const custom = tools.filter(t => !t.is_builtin);
console.log(custom.map(t => `${t.slug} (${t.kind})`));

Dynamic input validation

import Ajv from 'ajv';
const ajv = new Ajv();

const tool = await catentio.tools.get('jira-find-ticket');
const validate = ajv.compile(tool.params_schema);

if (!validate({ key: 'PAW-123' })) {
  console.error(validate.errors);
}

Errors

The real error body is FastAPI's {"detail": ...}. Because the wire response never has an error key, the SDK doesn't parse out a structured code/message — you get CatentioError with a generic HTTP_ERROR code and the HTTP reason phrase as the message.

Status detail Cause
401 Missing/invalid session — this SDK never sends x-catentio-session.
400 "slug required (1..64 chars)" etc. Bad create() input.
400 "builtin tools are decorator-registered and cannot be edited via the portal" update() on a built-in tool.
400 "builtin tools cannot be deleted via the portal" delete() on a built-in tool.
403 {"code": "plan_limit_exceeded", ...} create() past the custom_tools tier cap.
404 "unknown tool: {slug}" update()/delete() on a slug that doesn't exist.
409 "tool '{slug}' already exists" create() with a slug collision.

Next

  • Skills — versioned bundles of related tools.
  • Agents — the principal that calls tools.
  • API → Tools — HTTP-level reference.