AI agents (Claude Code, Codex, Cursor, Gemini CLI)
Decisions D-9100..D-9149 in docs/decisions.md (section "Session 7 - AI"). Sources: compiler/src/mcp/ (library), tools/oxl/cmd_mcp.cpp,
tools/oxl/cmd_ai.cpp, tools/lldb/oxl_snapshot.py, tools/ai/ (skills and instructions). Tests: test_mcp (gtest), mcp_stdio, ai_skills_check,
ai_setup, install_relocatable (ctest).
OxL is new: a model has never seen it. Two things make an agent productive without burning tokens on guessing and on reading files:
- An MCP server built into the compiler binary (
oxl mcp): precise, short answers from the real front end (diagnostics with machine-made fix hints, test failures only, signatures from the std sources, one section of the Standard, a debugger snapshot). - Skills and instructions that teach the language compactly: a cheat sheet, the mistakes models make (each with its diagnostic code), idioms, testing, debugging, project layout. Every code block in them is compiled by CI.
Quick start#
oxl ai setup # in your project: Claude Code (.mcp.json, .claude/skills, CLAUDE.md, AGENTS.md)
oxl ai setup --all # Claude Code, Codex, Cursor, Gemini CLI
oxl ai setup --codex --cursor ./myprojectRestart the agent (Claude Code: /mcp). The agent now sees the server oxl with 11 tools, the skills, and AGENTS.md. oxl ai list prints what each
client gets. A second run changes nothing; existing configuration is merged (other MCP servers are kept).
The tools (server oxl)#
Output is compact plain text by default; format: "json" is available on check, test, run, symbol, std_lookup, debug. Every tool
caps its answer and ends truncated lists with ...N more. isError is set when the answer is a failure (compile errors, failing tests, non-zero exit).
| Tool | Arguments | Answer |
|---|---|---|
check | path (package, file; default .), code (a snippet instead of a file), files (unsaved buffers), limit, format | ok, or file:line:col severity code: message per diagnostic with notes and a one-line fix: replace \a` with `b`(the compiler's quick fix) orhelp:`; a summary line |
build | path, profile, output, timeout | ok build/name (dev, 0.2s) or the compact diagnostics |
run | path, args, stdin, profile, timeout, max_chars, format | exit N (secs) + tails of stdout / stderr (a panic message is in stderr); timeouts kill the process group; compile errors as diagnostics |
test | path, filter (regex), exact, tag, kind, seed, failed, args, limit, format | 1 passed, 2 failed (0.2s) and only the failures: name, location, message, at lines of user code, output tail; passed tests are only counted |
fmt | path (formats in place; check only reports) or code | the formatted text, or already formatted |
symbol | file, (line, col) or name, op = info / def / refs, limit | signature + doc + definition (def: file:line:col); references with their source line |
std_lookup | query (name, Type.name, regex), module, limit | std.core List<T>: var fn push(x: T) - Appends an element.; same-named variants (sum of 14 numeric lists) collapse into one line with [+N similar] |
explain | code, max_chars | the diagnostic page (docs/diagnostics/<code>.md, without the Russian line), near-matches for a mistyped code |
spec | query (§7.2, 7.2, III.9, App. A, or a keyword), lang (en / ru), max_chars | the section text; a chapter answers its introduction and an outline of subsections; a keyword lists matching sections with a snippet |
debug | path, breakpoints (file.oxl:line or a function), expressions, args, stdin, timeout, max_frames, max_locals, format | non-interactive snapshot: stop reason, OxL stack frames, locals of the innermost frame (pretty-printed), values of the expressions; without breakpoints it stops at the first panic |
new | name, template (cli / lib / service), dir | the created files |
Resources: every Markdown / text file of share/oxl/ai is served as oxl://ai/<path> (resources/list, resources/read), so clients without
skill support can still read the skills. initialize carries a 120-word instructions text (the tool loop and the "do not guess" rule).
Protocol: JSON-RPC 2.0, one message per line on stdin / stdout, protocol version 2025-06-18 with fallback to 2025-03-26 and 2024-11-05
(the server answers the client's version when it knows it, else the newest). Methods: initialize, ping, tools/list, tools/call,
resources/list, resources/templates/list, resources/read, logging/setLevel; notifications are accepted and ignored. A malformed line is
answered with -32700 and the server keeps running.
Measured output sizes (e2e test, tests/mcp/test_mcp.py)#
| Call | Bytes |
|---|---|
tools/list (all 11 schemas) | about 8 300 (once per session) |
check clean package | 2 |
check one arg-count error | 91 |
fmt snippet | 24 |
symbol signature + doc + def | 103 - 378 |
std_lookup push | 188 |
explain unhandled-error | 1 013 |
spec "§7.2" | 1 125 (one section instead of a 237 KB file) |
run of a panicking program | 96 |
test with one failure of two | 170 |
debug panic snapshot (stack + locals) | 295 |
Compare: opening std/core/collections.oxl to find push costs about 50 000 tokens; the Standard is about 60 000 tokens.
Where the tools find their files#
oxl mcp finds everything from the installation prefix (<prefix>/bin/oxl), so it works from a release archive, a cmake --install tree and the build
tree: standard library lib/oxl/std, diagnostic pages share/doc/oxl/diagnostics, the Standard share/doc/oxl/spec/oxl-standard{,.en}.md, skills
share/oxl/ai, pretty-printers and the debug script share/oxl/lldb. Overrides: OXL_STD_DIR, OXL_DIAG_DOCS_DIR, OXL_AI_DIR, OXL_LLDB_FORMATTERS,
OXL_LLDB (the lldb binary), oxl mcp --std-dir D --ai-dir D. OXL_MCP_LOG=1 / --log is reserved for traces on stderr.
check, fmt, symbol, std_lookup, explain, spec run in the server process (the language server's analysis: Snapshot, Queries); build, run,
test, new, debug start the same oxl binary (or the program) as a subprocess with a time limit and bounded capture. Paths in answers are relative to
the server's working directory.
debug: how it works#
The tool builds the package with --debug-info full into a private temporary directory, starts lldb --batch with the OxL pretty-printers and
tools/lldb/oxl_snapshot.py, which creates the target, sets the breakpoints plus the runtime hook oxl_dbg_stop_panic (the program runs with
OXL_DEBUG_HOOKS=1), launches with stdin / stdout / stderr redirected to files, waits (with a timeout) for the first stop, walks the stack keeping
frames that have an .oxl source line, formats the locals of the innermost one, evaluates the expressions (p.x, xs[1], m["k"] through the
formatters' synthetic children, else lldb expressions) and kills the process. Needs lldb (lldb-18, Xcode tools); without it the tool answers
lldb not found and nothing else breaks. Interactive debugging stays with oxl dap (docs/debugger.md).
Setup per client (what oxl ai setup writes)#
| Client | --flag | MCP entry | Skills | Instructions |
|---|---|---|---|---|
| Claude Code | --claude | .mcp.json {"mcpServers": {"oxl": {"command": "oxl", "args": ["mcp"]}}} | .claude/skills/<name>/ | AGENTS.md block + CLAUDE.md containing @AGENTS.md |
| OpenAI Codex CLI | --codex | .codex/config.toml [mcp_servers.oxl] (command, args) | .agents/skills/<name>/ | AGENTS.md block |
| Cursor | --cursor | .cursor/mcp.json (same JSON) | .agents/skills/<name>/ | .cursor/rules/oxl.mdc (glob **/*.oxl) + AGENTS.md |
| Gemini CLI | --gemini | .gemini/settings.json mcpServers + context.fileName ["GEMINI.md", "AGENTS.md"] | .agents/skills/<name>/ | AGENTS.md block |
Without a client flag the clients whose directory already exists in the project (.claude, CLAUDE.md, .mcp.json, .codex, .cursor, .gemini) are used,
in an empty project Claude Code. Options: --all, --force (overwrite a configuration file that is not valid), --dry-run (print what would be written),
--link (symlink the skills instead of copying them: they follow the toolchain), --command C (the server command; default oxl when it is in PATH,
else the absolute path of the running binary), --ai-dir D. The AGENTS.md text lives between and
markers: a rerun replaces only that block.
The table is data (kClients in tools/oxl/cmd_ai.cpp): the conventions were taken from the clients' documentation and are not exercised against the
clients themselves in CI (only the files are checked: tests/mcp/test_ai_setup.py). By hand, the same thing is one command per client:
claude mcp add oxl -- oxl mcp # add --scope user for every project
codex mcp add oxl -- oxl mcp # or ~/.codex/config.toml
gemini mcp add oxl oxl mcp # or ~/.gemini/settings.json
# Cursor: Settings > MCP > add server: command `oxl`, args `mcp` (or ~/.cursor/mcp.json)Any other MCP client: a stdio server with command oxl and argument mcp.
Claude Code plugin#
tools/ai/claude-plugin/ (installed as share/oxl/ai/claude-plugin) is a Claude Code plugin: .claude-plugin/plugin.json, skills/ (a copy of
tools/ai/skills, kept identical by scripts/sync_ai_plugin.py and the ai_skills_check test) and .mcp.json (the oxl mcp server). Try it with
claude --plugin-dir <prefix>/share/oxl/ai/claude-plugin, or add the directory to a plugin marketplace of your own.
The skills (tools/ai/skills, Agent Skills format)#
Each skill is a directory with SKILL.md (YAML front matter name, description = when to use it, then a short body) and reference/*.md files that
are opened only when needed (progressive disclosure). The test ai_skills_check keeps SKILL.md bodies under 170 lines.
| Skill | Content |
|---|---|
oxl-language | the 12 differences from Rust / Go / TS, one screen of OxL; references: syntax, types, errors and effects, ownership, concurrency, services, std by task, mistakes (wrong code, its diagnostic code, the fix), idioms |
oxl-testing | test kinds, assertions, fixtures, mocks, sim, fuzz, bench, snapshots; running and reading results with the test tool |
oxl-debugging | the cheapest-step-first workflow, reading panics, debug, dbg, traces, profiler, remote debugger |
oxl-project | layout, oxl.toml, workspaces, profiles, dependencies, the command table |
tools/ai/AGENTS.md is the generic instruction file (use it as AGENTS.md, CLAUDE.md or GEMINI.md content); tools/ai/llms.txt is an index in the
llms.txt convention.
Quality control#
tests/mcp/test_skills.py(ctestai_skills_check) compiles every fenced block oftools/ai/**:oxlblocks must passoxl check;```oxl error:<code>must fail with exactly that diagnostic code;```oxl runis run and its stdout compared with the// expect:lines;```oxl testrunsoxl test(every kind);```oxl packageis a multi-file package (//// pathseparators).oxl nocheckis not allowed. It also checks front matter, description length, relative links, and that the plugin copy equalsskills/.- The mistakes list was derived by writing programs the way a Rust / TS programmer would and reading the diagnostics (
reference/mistakes.md); add a block there when you see a model repeat a mistake.
Token economy rules the skills teach#
Prefer check to building; check with code to a file round trip; test {exact} to the whole suite; std_lookup to opening std files; spec to reading the
Standard; symbol to grepping; debug to print statements; explain to guessing; one fmt at the end.
Limits and notes#
debugstops at the first hit; conditions are written in the program. Runtime breakpoints (rpc,msg,step,error) belong tooxl dap.- The MCP server is stateless: every tool call analyses from the files on disk (plus the
filesoverlay ofcheck). - The server does not write files except
fmt(onpath),build,newand the temporary directory ofdebug(removed afterwards). maintakes no parameters (std.os.args()gives the arguments);fn main(args: List<str>), a genericmainor a result other than()/() | Eis the errorbad-main(§2.4, D-9405).
See also
Source: docs/ai.md