Lua scripting
Shell syntax gets awkward for multi-step transforms, structured-data manipulation, or
logic that would sprawl across a chain of pipes. Reach for lua when it does. The shell
runs a sandboxed Lua 5.4 interpreter with the virtual filesystem wired into its io and
os libraries, so a script reads and writes the same sandboxed filesystem your commands
see, mediated by the same Kernel boundary.
Run a script
Section titled “Run a script”lua is a command like any other in the shell. Run a script from a file in the VFS, or
pipe one in:
lua transform.luaThe working directory, environment, and VFS carry over from the surrounding shell, and a script’s file access goes through the Kernel just as a command’s does.
What the sandbox allows
Section titled “What the sandbox allows”The interpreter is Lua 5.4 with a restricted global environment:
ioandosare backed by the VFS, so file access stays inside the sandbox.- Metatables are disabled:
setmetatableandgetmetatableare removed. os.dateand thedebuglibrary are unavailable.- The escape hatches that would compile arbitrary chunks or reach around the sandbox are
removed, including
load,collectgarbage, the raw accessors (rawget,rawset,rawequal,rawlen), andwarn.
Under a bare strands-shell -c, a Lua script has no wall-clock timeout. Set a
timeout on the shell, or pass a per-request
timeout_ms through the MCP shell tool, to bound a
long-running script.
Nested MCP servers as Lua modules
Section titled “Nested MCP servers as Lua modules”A shell config can declare other MCP servers under [[mcp]] entries. Each one becomes a
Lua module inside the shell, so a script can call your existing MCP tools through the
same sandboxed shell, with each tool’s response arriving as an ordinary Lua table.
Declare the servers in your
TOML config, then require a server
by name from lua. A server named my-tools is exposed as the module my_tools
(hyphens become underscores):
local tools = require("my_tools")local result = tools.search({ query = "deny by default" })print(result)Nested MCP servers are a native-target feature. They aren’t available under the WASM
build, which has no MCP client and no --config flag.