{
  "schema": "leumas.docs.page/1",
  "id": "skill:leumas-lmx#lma",
  "slug": "skills/leumas-lmx/lma",
  "kind": "tools",
  "bucket": "skill",
  "title": "lma — the any-tool language",
  "name": "lma",
  "eyebrow": "the any-tool language",
  "chip": null,
  "summary": "Call anything this install has. Safe mode, needs the host's tools bridge — so POST /api/lmx/run or Studio's LMX Playground, not the CLI.",
  "keywords": [
    "leumas-lmx",
    "playground",
    "bounds script",
    "bridge",
    "safe",
    "idea verb table",
    "mode",
    "post"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# `lma` — the any-tool language\n\nCall anything this install has. **Safe** mode, needs the host's `tools` bridge — so\n`POST /api/lmx/run` or Studio's LMX Playground, not the CLI.\n\n```lma\nPLANES\nDISCOVER \"base64\"\nDESCRIBE a-text.Base64Encode\n\nUSE a-text.Base64Encode { \"text\": \"{{input}}\" } -> encoded\nPRINT \"encoded: {{encoded}}\"\nTRACE\nRETURN encoded\n```\n\n---\n\n## The idea: there is no verb table\n\nEvery other LMX language has a vocabulary someone typed into a file. `lmq` knows `FROM`. `lmai` knows\n`ASK`. Add an adapter to Leumas and neither gains anything.\n\n`lma` has **no verbs of its own**. Its vocabulary is read from live registries on every single call:\n\n| Plane | Prefix | Where the verbs come from |\n|---|---|---|\n| Tools | *(none — default)* | `toolRegistry()` — adapter tools, stored functioncalls, user integrations, grid cells |\n| Actions | `action:` | `actions.listManifests()` / `actions.get(id).run(props)` |\n| MCP | `mcp:` | the MCP server map + `executeTool` |\n| Capabilities | `cap:` | `CAPABILITY_LIST` + the connectors `runCall` pipeline |\n\nRegister an adapter, write a functioncall in Studio, connect an integration, publish an app — and\nevery existing `lma` script can call it. Nothing is rebuilt, no language code changes, no redeploy.\nThe planes are read as *getters*, never memoised, precisely so a script cannot be pinned to the\nvocabulary that existed at boot.\n\n**`cap:` is the interesting one.** A capability names an *outcome*, not a vendor: `cap:email.send`\nresolves at call time to whichever connected provider declares it `fulfills` that capability, using\nwhichever of this caller's credentials apply. Connect a new provider and scripts that already asked\nfor `email.send` start working, with nothing edited.\n\n---\n\n## Statements\n\n`#` starts a comment.\n\n| Statement | Does |\n|---|---|\n| `PLANES` | what planes this install has, and how many verbs each holds |\n| `DISCOVER \"query\"` | search every plane; empty query lists everything (capped at 40) |\n| `DESCRIBE <ref>` | one verb's description and declared parameters |\n| `USE <ref> { …args } -> var` | call it; `{ … }` and `-> var` are both optional |\n| `EACH <item> IN <list> USE <ref> { … } -> var` | fan the call over a list, 4 at a time, order preserved |\n| `SET <name> = <value>` | a variable; JSON-coerced |\n| `WHEN <cond> THEN <statement>` | one guarded statement |\n| `PRINT <path>` · `PRINT \"… {{slot}} …\"` | |\n| `TRACE` | every call made so far — ref, ok, ms, metered |\n| `USING <ref>, <plane>, …` | narrow this script's own scope |\n| `RETURN <path>` | the script's value; stops the script |\n\n### References\n\n```\na-text.Base64Encode          the default (tool) plane\naction:notify                an automation action\nmcp:github.create_issue      <server>.<tool>\ncap:email.send               a vendor-neutral outcome\n```\n\n### `{{slots}}`\n\nArguments are a JSON literal, filled from inputs, `SET` variables and prior results **before** being\nparsed. That ordering is what lets a slot carry a whole structure:\n\n```lma\nUSE a.b { \"rows\": {{results}} }     →   { \"rows\": [{…}, {…}] }\n```\n\nAn unresolved slot is an error, and a literal that stops being valid JSON after filling is an error —\nnever a silently-empty `{}`, which would be a tool call that runs and does the wrong thing.\n\n---\n\n## What bounds a script\n\n| Gate | Effect |\n|---|---|\n| **Host scope** | The mounting app may hand each caller an allow-list. Out-of-scope verbs are invisible to `DISCOVER` and `DESCRIBE`, not merely unusable. |\n| **`USING`** | The script's own narrowing. It can only ever narrow further, never widen. |\n| **Call budget** | 50 `USE` calls per script — a loop cannot drain a wallet. |\n| **Metering** | Each call charges `passnode.charge({ target: 'tool:<ref>' })`. |\n| **Timeout** | 30s default. |\n\n### [critical] On metering\n\n`POST /api/adapters/:system/:fn` is priced by `passnode.guard`. That guard lives in the **HTTP\nrouter** — `registry.run()` itself is not metered, so every non-HTTP caller of an adapter tool has\nalways been free, the in-script `adapter()` builtin included.\n\nThat is a small gap while scripts call a tool occasionally and a large one for a language whose entire\npurpose is calling tools. `lma` therefore charges the **same `tool:` target string** the HTTP route\ncharges, so a script and an HTTP client are priced alike. A refused gate stops the call *before* the\ntool runs.\n\n*(The older `adapter()` builtin remains unmetered. Changing it would alter the behaviour of existing\nscripts, so it is left alone and flagged here instead.)*\n\n---\n\n## `TRACE`, and why it exists\n\nA language whose verbs resolve at run time has to be able to say what it actually called:\n\n```\n--- trace ---\n  ✓ a-text.Base64Encode 3ms · metered\n  ✓ cap:email.send 210ms\n```\n\nFor a `cap:` ref this is the **only** record of which provider served the call — the script named an\noutcome, and the ecosystem chose. Without it a run is not reproducible.\n\n---\n\n## What it deliberately cannot do\n\n- **Validate your arguments.** `DESCRIBE` surfaces the declared schema for a human; nothing enforces\n  it. A registry's schema describes intent, and rejecting a call against a stale or partial one turns\n  a working tool into a broken language feature.\n- **Loop, or define functions.** `EACH` fans one call over one list. Anything more wants `lmx`.\n- **Register anything.** Every plane is read-only from here.\n- **Run from the CLI.** No `tools` bridge there.\n\n---\n\n## Running it\n\n```bash\ncurl -s -X POST localhost:3000/api/lmx/run \\\n  -H 'content-type: application/json' \\\n  -d '{\"mode\":\"lma\",\"source\":\"PLANES\\nDISCOVER \\\"base64\\\"\"}'\n```\n\nBundled examples: `shared/engines/lmx/src/scripts/demo.lma`, `shared/engines/lmx/src/scripts/summarize-rows.lma`.\nTests: `shared/engines/lmx/src/test/lma.test.js`. Guard: `pnpm smoke:lmx`.\n",
  "source": {
    "path": ".claude/skills/leumas-lmx/reference/lma.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 5969,
    "hash": "f3184070c945efcf289518bce724022ba50480b3"
  },
  "urls": {
    "html": "/p/skills/leumas-lmx/lma",
    "json": "/docs/skills/leumas-lmx/lma.json",
    "md": "/docs/skills/leumas-lmx/lma.md"
  },
  "links": {
    "composes": [],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  }
}
