Extend
Plugins
A plugin is a folder that teaches Lampson a new tool. Query your database, call your company's API, run a deploy, post a message — anything a script can do, in any language. Plugins are off by default: you turn each one on, and only then can the agent use its tools.
Plugins were called lamps until Lampson 0.2.6. The name now belongs to lamps.sh — see Plugins and lamps below. Old folders keep working: what still works.
Turning one on§
❯ /plugins on postgres
☼ postgres is on — 1 tool available: plugin_postgres_query
In the web UI, click the plugins pill in the header: a list of every plugin with a switch, its tools and a form to try each one yourself. /plugins off <name> turns it off. The agent can ask you to turn a plugin on, but it can never do it by itself.
Anatomy§
~/lampson/plugins/postgres/ ← global: available in every project
plugin.json ← the manifest
query.py ← the code, in any language
my-project/.lampson/plugins/… ← or inside one project (commit it, and your team has it)
plugin.json:
{
"name": "postgres",
"description": "read-only queries on the dev database",
"kind": "exec",
"command": "python query.py",
"timeout": 30,
"tools": [
{
"name": "query",
"description": "Run a SELECT and return the rows as JSON.",
"parameters": {"type": "object", "properties": {"sql": {"type": "string"}}, "required": ["sql"]},
"readonly": true
}
]
}
How a call works§
Every time the agent uses a plugin tool, Lampson starts one short process and passes the call in environment variables:
| variable | content |
|---|---|
PLUGIN_TOOL | which tool was called (query) |
PLUGIN_ARGS | its arguments, as JSON ({"sql": "select …"}) |
PLUGIN_DIR | the plugin's folder |
PLUGIN_WORKSPACE | the project folder |
Whatever the script prints to stdout is the result the model sees. A non-zero exit or a timeout becomes an error the model can read.
# query.py
import os, json, psycopg
tool = os.environ["PLUGIN_TOOL"]
args = json.loads(os.environ.get("PLUGIN_ARGS", "{}"))
if tool == "query":
with psycopg.connect(os.environ["DEV_DATABASE_URL"]) as db:
rows = db.execute(args["sql"]).fetchall()
print(json.dumps(rows, default=str))
Two kinds§
"kind": "exec"—commandruns as is, from the project folder. Any language. A word in the command that names a file in the plugin's folder is resolved automatically (python query.pyfinds the plugin'squery.py). No sandbox: the script can do whatever your user can."kind": "syn"— a Synsema program ("entry": "plugin.syn"). It runs under a capability ceiling taken from"caps"in the manifest: the plugin can do exactly what you approved when you turned it on, and code that asks for more fails. Example:"caps": "file.read=workspace/*,net".
Because an exec plugin has no such ceiling, turning any plugin on is always a human decision. Your code, your rules, your risk.
Permissions§
Plugin tools follow the permission mode: they ask in ask, run in yolo, are denied in strict. Tools marked "readonly": true are also available to the read-only profiles (plan, review, explore).
Let the agent write one§
"Make me a plugin that lists open pull requests with gh." The agent creates the folder and manifest inside your project, validates it (synsema check for a Synsema plugin), and tells you it is ready — turning it on is still yours. In the web chat a ☼ plugin created line appears and the plugins pill blinks.
Scheduling a plugin§
A plugin tool can be the job of a scheduled task: "every morning, run postgres.query with this SQL and send me the result". Turning the plugin on is the authorization.
The example§
~/lampson/plugins/example-hello/ ships with Lampson — a Synsema plugin with one tool, greet. Copy the folder, rename it, and you have a starting point.
Plugins and lamps§
Two different things, on purpose:
- A plugin is local to you and your repo: any language, no distribution, no ceiling unless you write it in Synsema. Quick to make, yours to trust.
- A lamp (lamps.sh) is a portable unit of capability: a manifest that declares exactly what the code may touch, a runtime that enforces it and audits every check, versioned by git tag, pulled from a hub, usable by any MCP agent — Claude Code, Cursor, Lampson…
To use lamps in Lampson: install the CLI, lamp add <ref>, enable it, and add lamp mcp as an MCP server. Its tools then arrive with their ceiling stated in the description.
Renamed from lamps§
If you were using lamps in Lampson before 0.2.7, nothing breaks today:
.lampson/lamps/<name>/folders are still discovered (marked old folder in the UI and in/plugins); a missingplugin.jsonfalls back tolamp.json.- The on/off state in
.lampson/lamps.jsonis read until.lampson/plugins.jsonexists. - Scripts still receive
LAMP_TOOL,LAMP_ARGS,LAMP_DIRandLAMP_WORKSPACEnext to the new names; a Synsema plugin may keeprequire env("LAMP_*"). - Scheduled tasks saved with
"type": "lamp"keep running;/lampsis an alias of/plugins. - Global folders you put in
~/lampson/lamps/are moved to~/lampson/plugins/the first time you run Lampson.
This is a bridge, not a second format: rename the folder to .lampson/plugins/, the manifest to plugin.json, and read the PLUGIN_* variables.