Skip to main content

Private beta: Request private beta access

zenitya

SDK reference

The Project registry, the s schema builder and the tool context.

View as markdown

@zenitya/sdk is the only package a project installs. It is MIT licensed and published on npm.

shell
bun add @zenitya/sdk
typescript
import { Project, type ToolContext } from "@zenitya/sdk";
import { s } from "@zenitya/sdk/schema";

Project

The registry your entrypoint exports as its default export.

typescript
const project = new Project();
export default project;

project.tool(key, config)

Registers a tool and returns the project, so calls can be chained. Throws when the key is taken, invalid, or the project already has 128 tools.

FieldTypeRequiredNotes
titlestringyesAt most 120 characters
descriptionstringyesAt most 2,000 characters; agents read it
schemas.object(...)yesThe input; always an object
outputSchemaany s schemanoValidated against what execute returns
hintsToolHintsnoSee below
execute(input, context) => Output | Promise<Output>yesinput is typed from schema

key starts with a letter and has only letters, digits and underscores, at most 64 characters.

ToolHints

All optional booleans, as in MCP tool annotations: readOnlyHint, destructiveHint, idempotentHint, openWorldHint. A tool cannot be both read-only and destructive.

ToolContext

The second argument of execute.

FieldTypeNotes
invocationIdstringUnique per call
signalAbortSignalAborts when the call times out
loggerToolLoggerdebug, info, warn, error

Each logger method takes (fields: JsonObject, message?: string):

typescript
context.logger.warn({ orderId, status: response.status }, "carrier lookup failed");

s: schema builders

Every builder returns a schema that is both the JSON Schema agents see and the TypeScript type of the value.

BuilderJSON SchemaTypeScript
s.string(){ type: "string" }string
s.number(){ type: "number" }number
s.integer(){ type: "integer" }number
s.boolean(){ type: "boolean" }boolean
s.enum("a", "b"){ enum: ["a", "b"] }"a" | "b"
s.object({ ... })object, all properties required, no extra properties{ readonly ... }
s.array(item, { minItems? }){ type: "array", items }; minItems is 0 or 1readonly Item[]
s.anyOf(a, b, ...){ anyOf: [...] }union
s.ref("#/properties/customer"){ $ref }, local pointers onlyJSON value

String formats, each { type: "string", format } and typed as string: s.date(), s.time(), s.datetime(), s.duration(), s.email(), s.uuid(), s.hostname(), s.ipv4(), s.ipv6().

s.enum takes all strings or all finite numbers, unique, at least one.

Modifiers

  • .describe(text): adds the description agents read. At most 2,000 characters.
  • .nullable(): allows null as well. Properties are always required, so this is how a value becomes optional: the caller passes null.
typescript
const schema = s.object({
  query: s.string().describe("Text to search for."),
  status: s.enum("open", "closed").nullable().describe("Filter by status, or null for any."),
  tags: s.array(s.string(), { minItems: 1 }).describe("At least one tag."),
});

References

s.ref(path) points at another node of the same schema with a local JSON Pointer. The target must exist in the final schema:

typescript
const schema = s.object({
  customer: s.object({ id: s.string() }),
  previousCustomer: s.ref("#/properties/customer"),
});

Limits

A schema may be at most 256 KiB, 32 levels deep and 10,000 nodes. Tool input and output are each at most 1 MiB of JSON, nested at most 64 levels.

Types

InferSchema<typeof schema> gives the TypeScript type of a schema. JsonValue, JsonObject, JsonArray and JsonPrimitive describe JSON values.