Aevum Cadence AI

Tools

A tool is a default-exported object from tools/<name>.ts. The loader derives the model-visible name from the file basename, so tools/example.ts becomes example. Plugin tools land in the same catalog as built-in tools.

What a tool is

The model chooses to call it. You describe what it does and what arguments it takes. The assistant runs execute and feeds the result back into the turn. Every field is optional. A broken tool never blocks the rest of the plugin from loading.

Definition fields

FieldDefaultPurpose
nameFile basenameName the model sees.
descriptionemptyWrite it for the model.
input_schemaempty objectJSON Schema for arguments.
defaultRiskLevelmediumlow, medium, or high. See Permissions.
categorynoneFree-form label for permission policies.
executionTargetby name prefixsandbox or host. host_* and computer_use_* default to host.
executeunimplemented(input, ctx) => Promise<ToolExecutionResult>

low is read-only with no side effects. medium changes state. high is destructive or sensitive and always prompts unless the user chose Full access. Under-stating risk is a safety bug.

Execute context

Stable fields: conversationId, workingDir, requestId, signal, onOutput, assistantId, isInteractive. Return content, optional isError, status, yieldToUser, and contentBlocks.

Resolution order

  1. Core tools (startup). Same-name plugin or MCP tools are skipped.
  2. Workspace tools under /workspace/tools/ (can shadow a core tool).
  3. MCP server tools.
  4. Built-in default plugin tools.
  5. User plugin tools, by install date. Collisions fail registration.

Prefix names (for example myplugin_search) to stay clear.

Anatomy

// tools/example.ts
import type { ToolContext, ToolExecutionResult } from "@aevumcadence/plugin-api";

export default {
  description: "Search saved notes for a phrase.",
  defaultRiskLevel: "low" as const,
  input_schema: {
    type: "object",
    properties: { query: { type: "string" } },
    required: ["query"],
  },
  async execute(input, ctx: ToolContext): Promise<ToolExecutionResult> {
    const query = String(input.query ?? "").trim();
    if (query.length === 0) {
      return { content: "error: query must be non-empty", isError: true };
    }
    return { content: `searched ${ctx.conversationId} for ${query}`, isError: false };
  },
};

When to write a tool

When the assistant needs to do something the model invokes by name. A tool sits in the catalog on every request, so keep descriptions tight. For occasional procedures, prefer a skill.