Packages
Section titled “Packages”A Package in Submilli is like a package in npm or pip, a library you install and import. The language is TypeScript, but npm packages can’t be used. Submilli resets the ecosystem with Packages built for AI agents. Each function that reaches outside names its operation and asks the Blueprint before it acts. Why the reset is worth it comes later in this chapter. In the quickstart you wrote one with a single function.
Packages are also Submilli’s answer to MCP servers. Other code-execution platforms take the MCP servers you run and turn them into an interface the agent’s code can call. Submilli can do that too. Declare a server in the Blueprint and it becomes a Package, with each tool a function the Blueprint can allow or deny. But MCP was designed for tool calling. Most MCP servers publish no output schema, so a program can’t know the shape of what a tool returns, and a rule over an MCP tool can see only the tool’s name. A Package needs no server to deploy or maintain, calls the API directly, returns typed values, and tells the runtime what each call means.
A Package can be one you write for an internal system, one you write for a third-party service you consume, or one someone else published. Any Package in a Git repository installs straight from it. Submilli publishes curated Packages that way for common services such as GitHub, Slack, Google Drive, Linear, Notion, and others.
Why not npm
Section titled “Why not npm”Generated code can’t import npm packages or Node.js modules. An npm package
is written for Node, and Node gives it the whole operating system: files,
sockets, processes, anything a system call can reach. Submilli is designed
for agents, and the ways a program can reach the outside world are designed
for that. A program gets a small set of operations, each named and checked. An npm
package also has no semantic permissions
(no check calls), so a Blueprint
would have nothing to govern. You pay by wrapping your systems as Packages.
A Package, from the inside
Section titled “A Package, from the inside”Here is one operation of the billing Package, the one the previous chapter’s rules were about:
import { check } from "submilli:security";import { post } from "submilli:http";import secrets from "submilli:secrets";
const BASE = "https://billing.internal.example.com/v1";
/** * Add a goodwill credit to a customer's account. * @param customerId The customer's id in the billing system, such as `cus_northwind`. * @param amount The credit, in cents; must be positive. * @returns The credit as recorded. * @capability acme.com/credits.apply { customerId: string, customerClass: string, amount: number } */export function applyCredit(customerId: string, amount: number): Credit { // The call names a customer. Whether that customer is premium is a fact // about the account, so the package looks it up before asking. const customerClass = lookUpClass(customerId); check("acme.com/credits.apply", { customerId, customerClass, amount });
const key = secrets.get("BILLING_API_KEY"); if (key === null) { throw new Error("BILLING_API_KEY is not configured for this blueprint"); } const headers = new Map<string, string>(); headers.set("Authorization", "Bearer " + key); const response = post(BASE + "/customers/" + customerId + "/credits", { amount }, headers); if (!response.ok) { throw new Error("billing API failed: HTTP " + response.status.toString()); } return JSON.parse(response.body) as Credit;}Two lines make it an operation. The @capability tag declares it with a
name and the fields a rule may test. The check call, from
submilli:security, enforces it. It takes the capability’s name and
those fields, asks the Blueprint whether the caller may do this with these
values, and throws PermissionDeniedError if not.
This is the semantic permission model from the Package’s side. The Package decides what the operation means and which facts describe it, and hands them to the runtime typed: the customer, the amount, and the customer’s class, which the call didn’t carry and the Package looked up. A Blueprint can then say “premium customers only”, and nobody had to read a payload.
What the agent’s program sees
Section titled “What the agent’s program sees”To the model, a Package is an import. Before it writes a program, the agent searches for Packages and reads a Package’s documentation. You can do the same from the CLI:
submilli search billing@acme/billing — Credits for one customer of Acme's billing service.submilli docs @acme/billing// @acme/billing — Credits for one customer of Acme's billing service.
/** * Add a goodwill credit to a customer's account. * @param customerId The customer's id in the billing system, such as `cus_northwind`. * @param amount The credit, in cents; must be positive. * @capability acme.com/credits.apply { customerId: string, customerClass: string, amount: number } * @returns The credit as recorded. */function applyCredit(customerId: string, amount: number): Credit;
/** * A credit applied to a customer's account. */interface Credit { /** * Amount in cents. */ amount: number; /** * The customer credited. */ customerId: string;}The agent’s documentation tool returns the same declarations, doc comments included, together with the Package’s readme. Then it writes the program:
import { applyCredit } from "@acme/billing";
function main(): string { const credit = applyCredit("cus_northwind", 1500); return `credited ${credit.amount} cents`;}Calls are synchronous. There is no await, and the program gets the value back. Run
under the previous chapter’s Blueprint, bound to cus_northwind:
credited 1500 centsThe program never sees the billing API, its URL, or the key that authenticates the request. The Package holds all three. When the Blueprint refuses the call, the program gets the error you saw in the quickstart, and the model reads it.
The tools to build one
Section titled “The tools to build one”submilli build is to a Package what npm is to a Node project. It
scaffolds the project, compiles it, derives what it can be granted from the
@capability tags, runs its tests, and installs it where programs can
import it. And with the skill installed, your coding assistant does the
writing. Give it a service’s API documentation and what the agent may do,
and it writes the Package, the readme the model reads, and the tests, then
tests it under a Blueprint.
The how-to pages on Packages take each step in turn, starting with start a project, and the build a Package tutorial walks through doing it with your assistant.
Next: the server, the process that compiles the program and answers every check.