Building Packages
Cinatra extends through packages. Every domain capability — a new agent, a new connector to an external service, a new asset type, a new entity store, a new background job — lives in its own package under packages/<name>/. Packages do not import each other’s internals; they call each other through capability surfaces, so adding a package does not entangle existing ones.
This document describes the conventions a Cinatra package follows. For end-to-end agent authoring (where the “package” is an OAS Flow file rather than a TypeScript package), see Developing agents.
When you need a package
Section titled “When you need a package”You need a TypeScript package when you have at least one of:
- Custom persistence. A new table or schema definition the agent or feature owns.
- A capability surface other code should call. Domain operations exposed as MCP primitives.
- Background work. A BullMQ job and worker the platform should run.
- A UI surface. Screens that mount into the Next.js app.
- A typed in-process client. Other packages or server actions need a typed wrapper to call you without going over HTTP.
If you do not need any of those — your agent is a sequence of MCP tool calls and a system prompt — you do not need a package. Author it as an OAS Flow file directly under agents/<vendor>/<slug>/ instead.
What a package contains
Section titled “What a package contains”A typical Cinatra package has this shape:
packages/<name>/ package.json — workspace package, named "@cinatra/<name>" AGENTS.md — engineering reference for the package (internal) src/ index.ts — public surface, named re-exports only schema.ts — Drizzle schema for this package's tables store.ts — read/write functions over the schema mcp/ handlers.ts — MCP primitive implementations schemas.ts — Zod input schemas for primitives registry.ts — registers primitives with the MCP server client/ deterministic-client.ts — typed in-process wrapper integration/ module.ts — host-app wiring entry point extension/ definition.ts — extension metadata (id, name, slug, description) screens.tsx — React server components mounted by the route registry ports/ — port types for the package (hexagonal pattern) application/ — use case functions composed from ports presentation/ — UI components used inside screensNot every package needs every directory. The minimum is src/index.ts plus whatever subsystems you are introducing. The structure above is the maximal pattern; smaller packages omit what they do not use.
Conventions
Section titled “Conventions”Naming
Section titled “Naming”- Files: kebab-case (
agent-runner.ts, notagentRunner.tsorAgentRunner.ts). - Functions: camelCase. Common prefixes:
read*/write*/update*/delete*for store access;build*for data-structure assembly;handle*for event handlers;create*for factory functions. - Types and components: PascalCase. Props types end with
Props; port types end withPort; store types end withStore. - MCP primitives: underscore-separated, prefixed by the package’s domain (
accounts_create,objects_save,agent_run). - Package name: the scoped npm-style package name puts the kind last (e.g.
@cinatra-ai/email-drafting-agent,@cinatra-ai/assistant-skills) — see Naming conventions.
TypeScript
Section titled “TypeScript”- Strict mode is on, with
noImplicitAny: falsefor some loose typing. - Module resolution:
bundler. - Always use
import typefor type-only imports. - Path aliases:
@/for the Next.js app’ssrc/,@cinatra/<name>for workspace packages — never relative paths crossing package boundaries.
Imports
Section titled “Imports”- Never import directly from another package’s internal files. Use the declared entry point:
import { foo } from "@cinatra-ai/objects", notimport { foo } from "../../objects/src/internal/foo". - Package
index.tsfiles use explicit named re-exports, notexport *. - Named sub-entry points are declared in
tsconfig.jsonpaths (for example@cinatra-ai/objects/auto-registrar,@cinatra-ai/objects/registry) for cases where the barrel export is host-only.
Validation and errors
Section titled “Validation and errors”- MCP handler inputs are validated with Zod (
schema.parse()— throws on invalid input). - Domain-level errors are typed: a class extending
Errorwithcode,retryable, anddetailsfields, plus atoDomainError(error, fallbackCode)converter for unknown errors. - Server actions redirect on success via
redirect(); they do not return a redirect value.
Wiring a package into the app
Section titled “Wiring a package into the app”A new package becomes part of the running app in three places:
- MCP registry. The package’s
registry.tsregisters its primitives with the platform’s MCP server at startup. - Background jobs. If the package has a worker, its job constants and handler are registered with the BullMQ queue.
- Extension route registry. If the package has UI screens, its
agentScreensexport (or equivalent) is mapped to routes in the host app’s extension route registry. Some legacy code still uses the olderagentPluginScreensidentifier — both resolve to the same screen surface.
The host app reads each package’s src/index.ts and follows its public surface — there is no manifest-driven loader. The result is that adding an extension is a normal TypeScript dependency change, not a configuration ceremony.
When the package surface is OAS, not TypeScript
Section titled “When the package surface is OAS, not TypeScript”Many “agent packages” in Cinatra are not TypeScript packages at all — they are OAS Flow files under agents/<vendor>/<slug>/cinatra/oas.json. These are first-class citizens: they are published, versioned, installed, and run the same way as TypeScript packages, but their entire definition lives in declarative JSON. The compiler in @cinatra-ai/agents converts the OAS Flow into the runtime representation; WayFlow executes it; the platform handles persistence, observability, and HITL automatically.
If your contribution is “an agent that calls these tools in this order with this prompt,” do not build a TypeScript package — author an OAS Flow file. The platform infrastructure is already there. See Developing agents for the authoring workflow.
Where to go next
Section titled “Where to go next”- Author an agent without building a package: Developing agents
- Understand the protocols packages plug into: Open standards in Cinatra