context. Together they are how an agent does things Lua doesn’t ship: the model reads the skill’s context and each tool’s description, decides which tool to call and with what arguments, and Lua runs your execute function.
How a tool reaches the model
A tool is a class that implementsLuaTool from 'lua-cli' with four members: name (letters, digits, hyphens, underscores), description, a zod inputSchema, and execute. The model’s arguments are validated against inputSchema before execute runs, so the input you receive is typed and complete. Whatever execute returns is serialized to JSON and handed back to the model.
A skill wraps tools with a name, a description, a context, and the tools array. The skill is the unit Lua versions and pushes (lua push skill --name orders); tools are never pushed on their own.
src/skills/orders.skill.ts
context is injected into the prompt whenever the skill is active. Together with each tool’s description, it is how the model decides when to call your tools, so both are prompt text, not documentation. context carries the rules that span tools (when to ask first, what never to do, what to say back); description says what one tool does and when it applies. Write both for the model that reads them, and name the tools you refer to.
Do
Don't
condition runs before each turn; when it returns false or throws, that tool is left out of the prompt but the skill’s name and context stay, so the model can still say it can’t do that. A skill’s condition hides every tool and omits the context, so an end user who fails the check talks to an agent that has never heard of it. Both are async functions that return a boolean and usually read the current end user with User.get(), which is null when no end user is known.
src/skills/tools/ExportOrdersTool.ts
context can also take the { base, voice, text } object form described on the persona page.
Sharing tool logic across agents
When several agents call the same backend, put the shared parts in a base class and let each agent declare a thin subclass that overrides only what differs. The compiler walks the fullextends chain, so a leaf that extends a base that implements LuaTool is detected as a tool, and field initializers on the leaf are ordinary JavaScript inheritance: execute reads this.searchPath and sees the leaf’s value.
src/skills/tools/AcmeSearchTool.ts
new AcmeSearchTool('/acme/search')). The compiler instantiates each tool class once per project with no arguments and reuses that artifact at every reference, so the argument is dropped; lua compile warns with lua/constructor-args-dropped. A field override is the supported way to vary a tool per agent.
Test the leaf tool on its own before a model is involved. lua test skill runs one tool’s execute with the input you pass; --name is the tool name, not the skill name, and condition is not evaluated.
Skills and MCP servers
A skill runs your TypeScript inside Lua; an MCP server exposes tools that live elsewhere and are called over HTTP. Write a skill when the logic is yours (your API, your rules, your data). Attach an MCP server when a vendor already offers one, and connect an integration when the vendor is in Lua’s catalog, because the integration provisions its MCP server for you.When to use a skill
- An end user’s message should cause your code to run: a lookup, a booking, a calculation. Give it a tool.
- Several tools share rules or a domain (orders, bookings, billing): group them in one skill so the rules live in one
context. - The skill must be invisible to some end users: put a
conditionon the skill, not on each tool. - Nothing in the conversation starts the work (a payment event, a nightly report): that isn’t a tool. Use a webhook, a trigger, or a job.
- Every message needs the same transform (redaction, a disclaimer): use a processor, not a tool.
Limits
- Tool
namemust match^[a-zA-Z0-9_-]+$, or adding it to a skill throwsInvalid tool name;lua compilewarns when it isn’t lowercase with hyphens or underscores. - A skill needs a non-empty
nameand acontext; an object-formcontextneeds at least one ofbase,voice,text. - Tools are compiled only when their skill is registered on the
LuaAgentin your entry file; an unregistered file is never bundled. - A tool’s
conditionis evaluated on every turn with a 30-second budget. Keep it to one cheap read.
Next steps
Add a tool to a skill
Write, test, and release your first tool.
Write skill context
Context that makes the model pick the right tool.
LuaSkill reference
Every field, method, and error.
LuaTool reference
The interface, the voice flags, and
condition.
