Qu

Stop guessing. Run it.

A model writing Qu from memory gets it subtly wrong — a half-open range, a function that does not exist, a keyword spelled the Python way — and nobody finds out until a person runs the script. Qu ships an MCP server so the assistant can check instead: parse it, run it, look up the name, draw the figure and read the labels back.

Model Context Protocol

qu mcp

One command. The server speaks JSON-RPC over stdin and stdout, the standard MCP stdio transport, and it is part of the qu binary — there is nothing else to install, and no separate process to keep in step with the engine you are actually running.

Add it to your client

Claude Code, in one line:

claude mcp add qu -- qu mcp

Or the configuration file directly — the same mcpServers block every MCP client uses. For Claude Desktop that is %APPDATA%\Claude\claude_desktop_config.json on Windows and ~/Library/Application Support/Claude/claude_desktop_config.json on macOS:

{
  "mcpServers": {
    "qu": {
      "command": "qu",
      "args": ["mcp"]
    }
  }
}

If qu is not on the path, give the full path to the binary instead. Restart the client; the tools appear under qu.

The tools

ToolWhat it settles
qu_eval Runs Qu source and returns everything it printed. Printing the shape or the first few values of a result costs one call and settles the question that would otherwise be answered by assertion.
qu_check Parses without executing and returns diagnostics with line and column. Cheaper than running, and safe on code with side effects. It catches the structural mistakes — a missing end for, a Python-style colon — but not undefined names, which are a runtime error in Qu.
qu_builtins Searches the engine's own dispatch table. This is the authority on whether a function exists: the table is the one the interpreter dispatches on, not a list in a document. A model that checks here does not hallucinate an API.
qu_docs Reads a documentation page, optionally filtered to the sections that mention a term. Sixteen pages are compiled into the binary, so this works nowhere near a checkout.
qu_figure Runs Qu that draws and returns the figure as SVG. The axis labels, tick values and legend text come back as text, so a model can verify a plot contains what it intended rather than hoping.
qu_run_file Runs a .qu file from disk, with arguments reaching the script as argv().

The documentation is also exposed as MCP resources under qu://docs/…, for clients that browse resources rather than call tools.

What it is allowed to do

A model will write while true sooner or later. The server is built so that this is uninteresting when it happens.

Isolated

A process per call

Evaluating in-process would save a few milliseconds and give up the three properties that matter: a hard wall-clock limit, a hard memory limit, and surviving a script that aborts. qu run already implements all three as flags, so the server spends a process and gets them.

Sandboxed by default

No network, no writes

Calls run under --sandbox, which denies the network, process-spawning and file-writing builtins at the moment they are called — not by static analysis, which can be talked around.

Bounded

30 seconds, 2 GB

Both adjustable. A run that exceeds either is killed with a message naming which limit it hit, so the model learns something rather than waiting.

Figures come back without lifting the sandbox: qu_figure is written by the CLI rather than by the script, so a sandboxed script that never calls savefig still produces one.

FlagEffect
--allow-write Lifts the sandbox. Needed only if you want scripts to save their own files.
--timeout <s>Wall-clock limit per call. Default 30.
--memory <MB>Memory limit per call. Default 2048.
--root <dir> Confines qu_run_file to one directory. A path outside it is refused by name rather than clamped — silently running a different file than the one asked for is worse than saying no.
qu mcp --root ./analysis --timeout 60 --allow-write

The six rules

The server hands these to the client during initialize, so a model has them before it writes a line. They are not the interesting parts of the language — they are the places Qu differs from what you would assume from Python or MATLAB, which is a different list.

  1. Every range includes both ends. 0 to 9 is ten values, and so is a slice: v[0:4] is five elements, M[0:1, :] is two rows. v[0:n] is n+1 elements; the first n are v[0:n - 1].
  2. Indices start at zero. The last of a thousand is v[999].
  3. * and ^ are matrix operations. Element-by-element is .* and .^. On two vectors of the same length * is an error, not a product.
  4. as matrix(r, c) fills by column. This one is silent: you get a matrix, just not the one you meant.
  5. An unread keyword argument is an error, with the nearest real name suggested. colour= does not quietly do nothing.
  6. Blocks close with a word — end if, end for, end function. Indentation carries no meaning.

Two more worth knowing: assignment inside a function is local unless the function says global name, and () is the empty list.

Without a server

Three files, in the order they are worth reading.

The important one

Agent guide

Every place Qu differs from what a model would assume, each with the error message the wrong assumption produces. It is the set of mistakes actually made porting scientific code to Qu, in the order they cost time.

Read it →

Authoritative

Builtin index

Generated from the interpreter's own table. If a name is not there, the engine does not have it — whatever any other document says.

Search the names →

/llms.txt

The map

Follows the llms.txt convention: a short description and an ordered set of links, so a model can find the right document without crawling the site.

Open →

Why the language cooperates

These were decided for people. They happen to matter more for a model, which cannot look at a figure and notice it is wrong.

No silent drop

Keywords are validated

Tracked from the code itself rather than a table, so it cannot drift. A guessed argument name is refused immediately, not three figures later.

No spooky action

Calls cannot reach out

A function cannot write to its caller's variables. Generated helper code cannot corrupt the surrounding script by reusing a common name.

No hallucinated API

The index is generated

The list of functions comes from the interpreter's own table, and available("part") searches it from inside a running program.

AssumptionWhat Qu doesWhat you see
Ranges are half-open0 to 9 is ten values, both ends index 100000 is outside 0..99999
Slices are half-openthey include both ends too no error — one element too many
as matrix fills by rowsit fills by columns no error — a different matrix
* is elementwise* is matmul, .* is elementwiseno error — a blank field
An unknown keyword is ignoredit is an error unknown keyword argument `colour=`
"\tau" is a backslashit is a tab a label reading au

Three of those six produce no error at all. That is the argument for running the code rather than reading it.