Define one Action per file
A direct actions/*.py file contains exactly one decorated async def run.
Its filename, description, parameters, Integration ids, and return annotation become the reviewed contract.
Use a narrow verb and object in the filename
from typing import TypedDict
from shimpz import Context, action
class ZoneResult(TypedDict):
zone_id: str
status: str
@action(
integrations=["cloudflare"],
effect="read_only",
description="Show whether one of your domains is active on Cloudflare.",
)
async def run(domain: str, *, ctx: Context) -> ZoneResult:
response = await ctx.fetch("GET", f"https://api.cloudflare.com/client/v4/zones?name={domain}")
zone = response.json()["result"][0]
return {"zone_id": zone["id"], "status": zone["status"]}inspect_zone.pybecomes the Action idinspect-zone.- The only decorated function is named
runand is asynchronous. integrations=[...]lists only Integration ids declared inshimpz.toml.effect="read_only"declares that the Action has no business side effect; an Action without it is mutating.description="..."is required: one line of 1 to 80 characters that says what the Action does for the person.- Typed parameters become required Brain input;
ctxis invocation context. - The typed return value defines the output the Brain may receive.
The description is shown beside the Action id on the Assistant's page. Write it as a string literal directly in @action(...): the SDK reads it with the message catalog before any Assistant code is imported and
refuses a computed value, such as a variable, a concatenation, or an f-string. It joins that catalog and is
translated into every interface language, so it must be NFC-normalized English without braces, and every
translation must fit 120 characters.
Use the closed annotation subset
- Scalar values
str,int,float, andbool.- Closed choices
Literalwith unique, non-empty string values.- Collections and objects
list[T]and nestedTypedDict. Action results must be aTypedDict.- Optional object fields
NotRequired[T]orRequired[T]inside aTypedDict.- Descriptions and constraints
Annotatedmay add a description and type-appropriate string, number, or list bounds. Parameter defaults, unions, tuples, arbitrary dictionaries, positional-only arguments,*args, and**kwargsare rejected.
Do not hand-write transport or schemas
{
"id": "inspect-zone",
"description": "Show whether one of your domains is active on Cloudflare.",
"integrations": ["cloudflare"],
"stored_inputs": [],
"input_files": [],
"human_requests": [],
"effect": "read_only",
"input_schema": {
"type": "object",
"properties": {"domain": {"type": "string"}},
"required": ["domain"],
"additionalProperties": false
},
"output_schema": {
"type": "object",
"properties": {
"zone_id": {"type": "string"},
"status": {"type": "string"}
},
"required": ["zone_id", "status"],
"additionalProperties": false
}
}The entry contains the canonical Action id and description, its declared Integrations, Stored Input ids, file inputs, and human-request capabilities, its effect class, and the two closed schemas, plus any declared verifier and idempotency. Every list is present even when empty; an Action declares at most eight Stored Inputs. The Controller addresses the Action by id and validates every input and result against those reviewed schemas; Assistants do not declare HTTP transport.
One Assistant contract contains between 1 and 128 Actions in canonical id order. With SDK 0.5.2, the contract also carries the Assistant's message catalog beside its Actions.