---
title: "Engine API in an external editor"
description: "The engine describes its whole Luau API (every global namespace, method signature, published type alias and asset-category surface) as a Luau definitions file…"
section: "Topics"
slug: "topics-external-editor"
canonical: "https://origozero.ai/docs/topics-external-editor"
updated: "2026-10-12T20:30:16.917433592+00:00"
tags: ["documentation", "guide"]
---

# Engine API in an external editor

## 1. Write the files

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

```luau
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:

| File | What it is |
| --- | --- |
| `zero.d.luau` | The definitions: every engine global the calling script can reach, with its type. |
| `zero.docs.json` | The 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`). |
| `.luaurc` | Nonstrict 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`:

```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:

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

### A check from a terminal

```sh
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

```luau
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.
