Lampson

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. Without a UI (a VPS over SSH, a script), from the project folder: lampson plugin on postgres (plugin list shows them, plugin off turns one 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:

variablecontent
PLUGIN_TOOLwhich tool was called (query)
PLUGIN_ARGSits arguments, as JSON ({"sql": "select …"})
PLUGIN_DIRthe plugin's folder
PLUGIN_WORKSPACEthe 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" — command runs 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.py finds the plugin's query.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"

The double asterisk is the whole folder and a single one only its first level; an older manifest with a single one still means the whole folder.

Because an exec plugin has no such ceiling, turning any plugin on is always a human decision. Your code, your rules, your risk.

Secrets and settings: config§

A plugin declares the settings it needs, and Lampson passes them in from ~/lampson/.env:

"config": ["DEPLOY_TOKEN", "DEPLOY_URL"]

LAMPSON_DEPLOY_TOKEN from .env reaches the plugin as PLUGIN_DEPLOY_TOKEN — also a Synsema plugin under a ceiling, which only sees PLUGIN_*. That way secrets don't go in the code or in plugin.json.

Instructions: the plugin brings its process§

A tool alone doesn't say how to use it. A plugin can bring its instructions, and Lampson adds them to the prompt of every agent that has any of its tools. That way each plugin is used the way its author meant, without repeating it in every agent's prompt.

Put them in an INSTRUCTIONS.md inside the plugin's folder (read automatically), or in the manifest:

"instructions": "INSTRUCTIONS.md"
"instructions": "Before deploy, run the tests and confirm the branch with the user."

They work for anything the model needs to know to use those tools well:

  • A backend process: "to create a customer: find by tax id first; if it doesn't exist, create with the registry data; never two customers with the same tax id".
  • Pointing elsewhere: "before deploy, read the project's release skill", "for billing questions use mcp_erp_query, not this plugin".
  • A long, precise prompt: the exact format of a report, the steps of a reconciliation, what to do with each error code of your API.
  • A conversation: how to take an order by chat — that's what the orders plugin does.

The instructions only reach agents that have the plugin's tools, and only while the plugin is on. An agent with "tools": ["read"] doesn't see them. They're read when each session starts (up to 12,000 characters).

Channels§

A plugin with "channel" in its plugin.json is a channel: it connects an agent to a messaging platform (Telegram, Chatwoot, whichever you write). Lampson ships telegram and chatwoot. → Channels

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.

~/lampson/plugins/orders/ is a complete one: seven tools, data in the project, a capability ceiling that only opens .lampson/orders/, and its INSTRUCTIONS.md. → Recipe: taking orders by chat

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 missing plugin.json falls back to lamp.json.
  • The on/off state in .lampson/lamps.json is read until .lampson/plugins.json exists.
  • Scripts still receive LAMP_TOOL, LAMP_ARGS, LAMP_DIR and LAMP_WORKSPACE next to the new names; a Synsema plugin may keep require env("LAMP_*").
  • Scheduled tasks saved with "type": "lamp" keep running; /lamps is 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.

View as Markdown