Resources and Tools

Declare logical capabilities, expose model-facing Tools, and invoke pinned Resource operations with correct effect semantics.

A Resource is a governed external capability; a Tool is a model-facing operation implemented by Agent code. The deployment manifest binds logical names to fixed or scoped Resources, and the Agent reads only the accepted CRNs from ctx.resources. Secret material remains behind the Resource boundary.

Direct Resource calls

Use ctx.invoke(resource, operation, arguments, options) for a synchronous result and ctx.invokeAsync() for a durable handle. The operation must exist in the deployment-pinned catalog, Policy must allow the exact CRN and action, and arguments must satisfy the pinned schema.

ts
const result = await ctx.invoke<{ status: string }>(
  ctx.resources.github!,
  "issue.create",
  { owner: "acme", repository: "support", title: "Follow up" },
  { dedupeKey: `ticket:${ticketId}`, timeoutMs: 30_000 },
);

Model-facing Tools

ts
import type { Tool } from "@constal/sdk";

export const createIssue: Tool = {
  name: "create_issue",
  title: "Create a support issue",
  version: "1",
  description: "Create one issue in the bound support repository.",
  schema: { type: "object", required: ["title"], additionalProperties: false,
    properties: { title: { type: "string", minLength: 1 } } },
  maxEffect: "idempotent",
  needs: [{ binding: "github", kind: "service", ops: ["issue.create"] }],
  async run(args, ctx) {
    return ctx.invoke(ctx.resources.github!, "issue.create", args, {
      dedupeKey: `create-issue:${JSON.stringify(args)}`,
    });
  },
};

A Tool has two texts. The optional title is one short line for people: the Console, the CLI, Tool approvals and Constal API summaries show it, or the Tool's name when it has none. The description is the model's prompt; the model receives the Tool's name, version, description and schema, never its title.

opTool() and opTools() can derive Tools from named bound operations. A Tool’s maxEffect is a ceiling: read-only may repeat, idempotent repeats with the same key, reconcilable requires a probe after ambiguity, and non-idempotent surfaces an unknown outcome rather than silently retrying.

Schema-aware Table tools

tableDescribe and tableQuery are generic read-only SDK Tools. tableDescribe resolves an exact queryable snapshot and returns its pinned schema and manifest identity. tableQuery accepts a bounded relational plan—selected fields, typed filters, grouping, allowlisted aggregates, ordering, and a hard row limit. It never accepts raw SQL.

ts
import { tableDescribe, tableQuery } from "@constal/sdk";

export default agent({
  // ...
  tools: { table_describe: tableDescribe, table_query: tableQuery },
});

Bind both Tools to the platform table_catalog service. The governed Resource loads the schema by content reference, checks the exact snapshot is queryable, compiles only declared scalar fields, and injects tenant, namespace, Table, branch, snapshot, and row-kind predicates. Storage credentials and unrestricted SQL never enter Agent or model context.

Bindings and scoped resolution

Fixed bindings always resolve one exact Resource. Scoped bindings resolve a tenant, customer, or principal assignment during admission and pin its revision into the Run. Agent input never selects an owner or arbitrary CRN. Read Use Resources from Agents and Scoped bindings for operational setup.