Skip to main content

Agent API

Reference for the agent-facing surface of @nudojs/lsp. All agent commands live inside the Nudo language server and are reached through standard workspace/executeCommand calls or custom LSP requests — there is no separate server process or protocol to install. For connection setup (LSP→MCP bridges, native LSP clients, VS Code), see the Agent Integration Guide.

Commands​

CommandCustom request aliasPurpose
nudo.checknudo/checkConstraint gate — CheckJson v1 (Abs signatures + actual ⊭ expected)
nudo.infernudo/inferWhole-file inference — InferJson v1 (intension carries lossless Abs)
nudo.hovernudo/hoverLossless Abs at a source position (+ optional inlays + interface tier)
nudo.whatIfnudo/whatIfApply type assumptions to bindings and read the inferred type of a target
nudo.suggestCasenudo/suggestCaseCheck @nudo:case coverage; when every case is synthesized, return paste-ready directives
nudo.tracenudo/traceList each case's argument types → result type for a function
nudo.interfacenudo/interfacePrint effective interface tiers (handwritten / generated / implicit)
nudo.interface.draftnudo/interface.draftCode-first draft: reviewable *.nudo.draft.js from existing code (same as CLI --draft)
nudo.interfaceEmitnudo/interface.emitPersist call-site domains as @generated sidecar segments
nudo.selectCasenudo/selectCaseSwitch the active case used for hover/diagnostics
nudo.getActiveCasesnudo/getActiveCasesRead the active case index of every function in a file

nudo.check / nudo.infer / nudo.hover / nudo.whatIf / nudo.suggestCase / nudo.trace / nudo.interface* return MCP-style text content — { content: [{ type: "text", text }] }. nudo.selectCase returns { success: true }; nudo.getActiveCases returns Record<string, number>. Shared sources are pinned by AGENT_TOOL_SOURCES (E5) — agent tools and CLI/LSP commands call the same service/core entrypoints.

Conventions​

  • file parameter — every command takes a file string, accepting either a file:// URI or a bare path. Files that are not open in an editor are read from disk.
  • Editor-style requests — the nudo/selectCase and nudo/getActiveCases requests additionally accept editor-style { uri, ... } params (this is what the VS Code extension's CodeLens uses). Agents should always use file.
  • Type expressions — see Type expressions below.
  • Abs-first — check / infer / hover expose the lossless algebra (Abs). Extensional strings (args / result / ext) are lossy projections for compatibility, not the type model.

nudo.check​

Constraint gate on Abs (type-as-computation). Same contract as CLI nudo check --json. See nudo check.

Arguments:

NameTypeDescription
filestringfile:// URI or path
sourcestring?Pre-read source (bypasses disk/editor)
format"text" | "json""json" → CheckJson only; default human summary + JSON

Returns (CheckJson v1): { version: 1, file, ok, summary, signatures[], issues[] } where each issue may carry actual / expected. Issue codes (full table: check guide):

CodeMeaning
nudo:constraint-violatedCall argument or return ⊭ precondition
nudo:assign-mismatchAssignment ⊭ existing shape
nudo:arg-structureHOF argument not callable / arity mismatch
nudo:case-inconsistency@nudo:case witness ⊭ refine
nudo:interface-param-mismatchHandwritten contract param name not on formal surface
nudo:interface-conflictHandwritten contract conjunction unsatisfiable
nudo:interface-load / nudo:interface-cycleSidecar load failure / cycle
nudo:interface-domain-exceedsCross-file call evidence ⊄ handwritten contract
nudo:interface-name-clashSidecar export name clashes with source export
nudo:interface-underivableHandwritten contract cannot be derived from source
nudo:interface-drift@generated segment ≠ recomputed (warning)
nudo:no-signatureNo symbolic Abs signature
nudo:opaque-result / nudo:eval-errorOpaque evaluation / evaluation threw
{
"command": "nudo.check",
"arguments": [{ "file": "src/validators.js", "format": "json" }]
}

nudo.infer​

Whole-file inference — same contract as CLI nudo infer --json.

Arguments:

NameTypeDescription
filestringPath or URI
sourcestring?Pre-read source
format"text" | "json""json" → InferJson only
functionsstring[]?Filter to these function names

Returns (InferJson v1): cases[].intension carries abs / term / pred / conf (lossless); args / result are extensional strings (formatShape projections).

{
"command": "nudo.infer",
"arguments": [{ "file": "src/app.js", "functions": ["scale"], "format": "json" }]
}

nudo.hover​

Lossless Abs at a position — same source as editor hover, no projection in between.

Arguments:

NameTypeDescription
filestringPath or URI
linenumber1-based line
columnnumber0-based column
sourcestring?Pre-read source
includeInlaysboolean?Also return all Abs inlays for the file

Returns JSON: { file, line, column, abs, absMultiline, intension, ext, inlays? } — abs is lossless; ext is the extensional projection for comparison only.

nudo.whatIf​

Set type assumptions and observe the inferred type at another position — the primary tool for AI-driven type exploration.

Arguments:

NameTypeDescription
filestringfile:// URI or path to the JavaScript file
bindingsArray<{ name: string, type: string }>Type assumptions to apply. name must be a top-level declaration (a top-level const/let/var, function, or class) — function parameters and locals have no matching declaration and are reported back as not applied. type is a type expression such as number or string | null
targetstringTop-level variable to get the type of

Returns: { content: [{ type: "text", text }] } where text is Type of "<target>": <type> — the inferred type of the target under the assumed bindings, or unknown if it is not a known binding. Trailing note lines report which bindings took effect: Bindings applied: … and, for names with no top-level declaration, Bindings not applied (no top-level declaration found): … (the answer then uses the file's own types).

Example — given src/config.js with const size = raw.length where raw comes from an unknown loader, assume raw is string and ask what size becomes:

const raw = loadRaw();
const size = raw.length;
{
"command": "nudo.whatIf",
"arguments": [
{
"file": "src/config.js",
"bindings": [{ "name": "raw", "type": "string" }],
"target": "size"
}
]
}
Type of "size": number
Bindings applied: raw: string

nudo.suggestCase​

Suggest @nudo:case directives for a function based on its parameter types.

Arguments:

NameTypeDescription
filestringfile:// URI or path to the JavaScript file
functionNamestringName of the function

Returns: { content: [{ type: "text", text }] } with one of four outcomes:

  • Function "<functionName>" not found — the file has no such function.
  • Suggested: /** @nudo:case */ followed by function <functionName>(...) { ... } — the function has no cases at all (only functions skipped by inference end up with zero cases).
  • Function "<functionName>" already has N case(s) — the function has handwritten (or entry-only) @nudo:case cases; they are left untouched.
  • Every case was synthesized from call sites — the reply is directive text that can be pasted into the source directly above the function, e.g.:
Function "add" has 2 synthesized case(s); suggested directives:
/**
* @nudo:case "call@L2" (1, 2)
* @nudo:case "call@L3" ("x", "y")
*/

Cases whose arguments cannot be serialized as directives (functions, Promises, instances, …) are dropped and reported in a trailing (M case(s) skipped: not serializable as directives) line; if none of the cases is serializable, the reply falls back to Function "<functionName>" already has N case(s) (none serializable as directives).

nudo.trace​

Trace how a type transforms from input to output in a function — one line per case.

Arguments:

NameTypeDescription
filestringfile:// URI or path to the JavaScript file
functionNamestringFunction to trace

Returns: { content: [{ type: "text", text }] } with one Input: (<argument types>) => Output: <result type> line per case, or Function "<functionName>" not found / No cases found for "<functionName>".

nudo.selectCase​

Switch the active case of a function. The active case drives hover types, diagnostics, and inlay hints until changed again.

Arguments:

NameTypeDescription
filestringfile:// URI or path to the JavaScript file
functionNamestringName of the function
caseIndexnumber0-based index of the case to activate

Returns: { success: true }. The server revalidates the document with the new active case and refreshes CodeLens.

nudo.getActiveCases​

Read the active case index of every function in a file.

Arguments:

NameTypeDescription
filestringfile:// URI or path to the JavaScript file

Returns: Record<string, number> mapping function name → active case index, e.g. { "parse": 1, "greet": 0 }.

nudo.interface / nudo.interface.draft / nudo.interfaceEmit​

Interface product surface (same data sources as CLI):

CommandArgsBehavior
nudo.interface{ file, functionName?, source? }Print fn [handwritten|generated|implicit] (params) → returns + JSON
nudo.interface.draft{ file, functionName?, source?, write?, dryRun? }Code-first draft module (@nudo:draft); write: true lands *.nudo.draft.js / *.nudo.draft.ts (never ambient-bound). Body-read fields appear as suggestions only. write: true is fail-closed without a project root (nudo config / package.json ancestor) — same as CLI --draft --write (override: NUDO_DRAFT_FORCE=1)
nudo.interfaceEmit / nudo.interface.emit{ file, functionName, mode: "add"|"update", dryRun?: boolean }Persist call-site domains via emitInterface. dryRun: true previews without writing: same result shape (paths, would-change, unifiedDiff) with [dry-run] would update … text; no sidecar file is created or modified. VS Code Persist/CodeLens confirm sends dryRun: true first, then a real write on confirm

loadModule and effective autoBind are server-injected (buffer-aware sidecar loader + project package.json#nudo.interface.autoBind AND client request). They are not JSON-serializable request parameters — do not send them from agents.

Handwritten contracts are never overwritten by draft or emit. Accept a draft by copying reviewed exports into *.nudo.js / *.nudo.ts. Tool errors carry isError: true.

Type expressions​

The type field of nudo.whatIf bindings accepts a primitive or a |-separated union of primitives:

ExpressionMeaning
number | string | booleanThe primitive type
null | undefinedThe corresponding singleton
bigint | symbolThe remaining primitives
string | nullUnion — "string or null"

Constraint-builder forms (number(), lit(...), shape({...}), union(...), array(...)) and structural expressions (object/array literals, => functions) parse through the directive grammar (parseCaseArgExpr); any other name becomes unknown.

Diagnostics​

Type errors (failed @nudo:refine assertions, unreachable code, …) are available as LSP diagnostics in both directions:

  • Push: textDocument/publishDiagnostics after each analysis
  • Pull: textDocument/diagnostic on demand

Pull mode is the natural fit for agents: open (or point at) a file, send textDocument/diagnostic, and read the severity-1 entries — no command call needed.

Migration from the MCP server​

The standalone @nudojs/mcp package is retired; its tools map onto the commands above:

Old MCP toolReplacement
nudo-what-ifnudo.whatIf — bindings are now actually applied (previously ignored)
nudo-checknudo.check (CheckJson v1) — preferred for CI/agent gates. Pull diagnostics include Abs check + A3-filtered evaluator diags for open buffers, but are not a full-file CI substitute.
nudo-type-atnudo.hover (lossless Abs), or nudo.whatIf with empty bindings and target set
nudo-suggest-casenudo.suggestCase
nudo-tracenudo.trace

See the migration section of the guide for connection-level changes.