Log inGet started

Engine API in an external editor

Updated October 12, 2026

1. Write the files

From any engine session (execute, the in-engine console, a script):

local r = lsp.exportDefinitions("/zero/source/editor")
print(r.files[1], r.bytes, #r.globals, #r.unrepresented)

That writes three files into the VFS directory:

FileWhat it is
zero.d.luauThe definitions: every engine global the calling script can reach, with its type.
zero.docs.jsonThe documentation file: each global's and member's signature, description, arguments and first example, keyed by the documentation symbol luau-lsp looks up (@zero/global/entity.spawn).
.luaurcNonstrict mode for the folder it sits in.

The definitions hold the globals of the script that called it, so call it from the tier whose globals the editor should know: content scripts run in the user tier, which is what execute runs in. lsp.definitions() returns the same source as a string, with the lists of declared globals and types, without writing anything.

The files can stay in the world's content. The engine's own checker reads a *.d.luau file the way upstream does, by loading it as definitions, so a zero.d.luau in /zero/source is checked as definitions and holds up neither play nor a publish. Export again after the engine updates to refresh them.

2. Copy them to your machine

  • In the editor, the Files panel's Save to this device on each file puts it on the machine of the person at the screen (a download in the browser, a save dialog in the native editor). userfile.save(path) does the same from a script.
  • Over HTTP, GET http://127.0.0.1:<mcp-port>/engine/vfs/zero/source/editor/zero.d.luau (and the same for the other two files) reads them from a running engine.

Put zero.d.luau and zero.docs.json wherever your editor configuration can name them (a types/ folder in your workspace works), and .luaurc at the root of the folder holding your Luau sources.

3. Point luau-lsp at them

VS Code

Install the Luau Language Server extension (JohnnyMorganz.luau-lsp) and add to the workspace's .vscode/settings.json:

{
    "luau-lsp.platform.type": "standard",
    "luau-lsp.sourcemap.enabled": false,
    "luau-lsp.types.definitionFiles": { "@zero": "types/zero.d.luau" },
    "luau-lsp.types.documentationFiles": ["types/zero.docs.json"]
}

@zero is the package name the file's documentation symbols are keyed under.

Neovim and other editors

Start the server with the files on its command line:

luau-lsp lsp --definitions=@zero=types/zero.d.luau --docs=types/zero.docs.json

A check from a terminal

luau-lsp analyze --platform=standard --definitions=@zero=types/zero.d.luau my_script.luau

--settings=.vscode/settings.json reads the same definitions from the VS Code settings above.

4. What you get

local id = entity.spawn("crate")   -- no finding: `spawn` is a member of `entity`
local bad = entity.spwan("crate")  -- unknown member `spwan`
local ref: EntityRef? = nil        -- published `--!global-types` aliases resolve

Hover over entity.spawn to read its signature and description.

What the file holds

  • Each type the registry states is declared as written. A type string the definitions cannot state (prose in a doc, typeof(...), a name no type declares) is declared any and listed by lsp.definitions().unrepresented with the doc and the reason, so the doc it came from can be fixed. A declaration upstream Luau refuses to load (an alias that reaches itself through aliases alone) is declared any and listed the same way, with upstream's reason, so the rest of the file still loads.
  • A type a module publishes with --!global-types is in the file once that module has loaded in the engine that wrote it.
  • A name upstream Luau already declares (string, math, pcall, ...) keeps upstream's type.
  • The file types the engine's globals. The engine's own dialect (typed function, a return type after ->) and its require forms (@builtin::a.b, ~pkg.x, dotted paths) are read by the engine's own checks: inside the engine, lsp.check and lsp.upstreamCheck read both, and lsp.upstreamCheck loads the same definitions into every module it checks.
  • The file is regenerated whenever the registry changes (a binding added, a library module hot-reloaded). Export again after updating the engine.
  • documentation
  • guide