mxscript

command module
v0.66.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 3, 2026 License: MIT Imports: 25 Imported by: 0

README

MX Script

MX Script

CI MIT License Go Report

A modern, open-source scripting language for building web apps and APIs.
One file. Zero boilerplate. Run with mx run app.mx.

Created and developed by Jassim Alkharafi.

server { port: 8080 }

let db = sql.open("./users.db")
sql.migrate(db, [
  "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"
])

group /api/v1 {
  get /users {
    return json(sql.query(db, "SELECT * FROM users"))
  }

  post /users {
    let r = validate(request.body, {
      type: "object",
      required: ["name", "email"],
      properties: {
        name:  { type: "string", min_length: 2 },
        email: { type: "string", format: "email" }
      }
    })
    if (!r.valid) { return status(400, { errors: r.errors }) }
    let res = sql.exec(db, "INSERT INTO users (name, email) VALUES (?, ?)",
                       request.body.name, request.body.email)
    return status(201, { id: res.last_insert_id })
  }
}

get /openapi.json { return json(openapi({ title: "Users API" })) }
get /docs        { return swagger_ui("/openapi.json") }

That's a real validated REST API with persistence and auto-generated docs. mx run app.mx and you're done.


Why MX Script?

Most languages make you choose: ergonomics or speed. JavaScript is fast to write but you assemble a framework, a runtime, a build tool, and a deploy story before you ship anything. Go is fast and simple but the syntax for a JSON API is a hundred lines.

MX Script is opinionated: the language is the framework. Routes, JSON, SQL, JWT, OAuth, WebSockets, AI, and background jobs are all first-class. The interpreter is a single Go binary.

What ships in the box

Layer What you get
Web framework Routes (get /users { ... }), middleware, route groups, static files, cookies, CORS, gzip, rate limiting, body limits, timeouts, graceful shutdown, TLS
Real-time Server-sent events (sse /events) and WebSockets (ws /chat) — both pure stdlib, no external dependencies
Database SQLite + Postgres through one sql namespace. Transactions, hash-tracked migrations, parameterized queries
Auth JWT, signed-cookie sessions, OAuth2 helpers (Google / GitHub / Discord / LinkedIn / Microsoft), password.hash (PBKDF2-SHA256), AES-256-GCM, webhook signature verification
AI OpenAI / Anthropic Claude / Google Gemini through ai.complete. Streaming, tool calling, embeddings
Background jobs Durable, SQLite-backed queue with retries and exponential backoff
API tooling openapi() auto-generates a 3.1 spec from your routes. swagger_ui() and redoc_ui() mount interactive docs in one line
Stdlib (~200 fns) Strings, arrays (map / filter / reduce / sort / group_by / zip), math, JSON, URL parsing, regex, file I/O, image manipulation, markdown rendering, CSV, email (SMTP), templates, schedulers, validation, subprocess, fs.watch
Concurrency spawn { ... } goroutines, channels, wait_group, thread-safe environments
Tooling mx run / init / new / build / repl / test [--cover] / bench / fmt / lsp / upgrade / doctor
Editor support TextMate grammar, VS Code extension, full LSP (diagnostics, format-on-save, hover, completion for all builtins)
Distribution GoReleaser binaries on every tag, Homebrew tap, mx build --vercel adapter

Five starter projects, one command

mx new api my-api         # REST API + OpenAPI + Swagger UI + status page
mx new todo my-todos      # JWT auth + SQLite + groups + validate
mx new chat realtime      # WebSockets + sessions + broadcast
mx new ai my-bot          # Tool-calling agent (5-turn loop)
mx new blog my-blog       # SSR markdown blog with admin

Each scaffolds a complete, runnable app. Read the source, change the bits you don't like, ship.


Try it in your browser

The full interpreter compiles to WebAssembly. Run a program without installing anything:

mx build --wasm                      # produces dist/mx.wasm + wasm_exec.js
# then serve site/playground/ — open the result in any modern browser

The playground at site/playground/ ships a complete editor + run loop with bundled examples (closures, map/filter, match, JSON, tight numeric loops). See site/playground/README.md for deploy instructions.

Install

Option 1 — pre-built binary (coming soon)

Download from the releases page and put mx on your $PATH.

Option 2 — build from source

Requires Go 1.21+.

git clone https://github.com/jlkdevelop/mxscript.git
cd mxscript
go build -o mx .
./mx version

Option 3 — go install

go install github.com/jlkdevelop/mxscript@latest

The binary is named mxscript after go install. Rename or alias it to mx:

mv $(go env GOPATH)/bin/mxscript $(go env GOPATH)/bin/mx

Quickstart

mx init my-api
cd my-api
mx run app.mx

Then in another terminal:

curl http://localhost:8080/
curl http://localhost:8080/hello/Jassim

CLI

mx run <file.mx>               # run an MX Script program
mx run <file.mx> --port 3000   # override the server port
mx run <file.mx> --watch       # restart on file changes (hot reload)
mx run <file.mx> --debug       # print tokens and AST
mx test [path]                 # run *_test.mx files
mx init [name]                 # scaffold a new project
mx build <file.mx>             # parse & validate without running
mx repl                        # interactive REPL
mx version                     # print version
mx help                        # show help

Language tour

Variables

let name = "Jassim"
let age = 30
let active = true
let scores = [10, 20, 30]
let user = { id: 1, name: "Ada" }

// String interpolation
print("Hello, ${name}! Score sum: ${scores[0] + scores[1] + scores[2]}")

// Optional chaining + nullish coalescing
let city = user?.profile?.city ?? "unknown"

Functions

fn greet(name) {
  return "Hello, " + name
}

print(greet("world"))

Anonymous functions:

let doubled = map([1, 2, 3], fn(n) { return n * 2 })

Control flow

if (age >= 18) {
  print("adult")
} else {
  print("minor")
}

loop scores as s {
  print(s)
}

loop 5 as i {
  print(i)
}

let n = 0
while (n < 100) {
  n = n + 1
  if (n == 50) { break }
}

HTTP routes

// Shorthand form (recommended)
get /             { return text("hello") }
get /users/:id    { return json({ id: request.params.id }) }
post /users       { return status(201, request.body) }
delete /users/:id { return json({ deleted: true }) }

// Verbose form (equivalent)
route GET /users  { return json(users) }

Middleware

middleware auth {
  if (request.headers["authorization"] != "Bearer secret") {
    return status(401, { error: "unauthorized" })
  }
}

route POST /admin {
  use auth
  return json({ ok: true })
}

Built-in functions

Category Functions
Output print, println
HTTP json, text, html, status, redirect, fetch
Env env(name)
String len, upper, lower, split, trim, contains, replace, starts_with, ends_with
Array push, pop, map, filter, find, join, reverse, range
Math round, floor, ceil, abs, min, max, random
Types typeof, isString, isNumber, isBool, isNull, isArray, isObject
JSON json_parse, json_stringify(v, pretty?)
File I/O read_file, write_file, file_exists, list_files, delete_file
Crypto hash_sha256, hmac_sha256, base64_encode, base64_decode, uuid
Regex re_match, re_find, re_find_all, re_replace
JWT jwt.sign(payload, secret), jwt.verify(token, secret)
URL parse_url, url_encode, url_decode
Time now(), now_iso(), sleep(ms), parse_date, format_date
Test assert(cond, msg?), assert_eq(a, b, msg?)
KV store kv_get, kv_set, kv_delete, kv_keys, kv_clear
Misc retry(fn, attempts, delay_ms?)
AI ai.complete(prompt), ai.embed(text)

See docs/api.md for the full reference.

AI built-ins

route POST /summarise {
  let summary = ai.complete("Summarise: " + request.body.text)
  return json({ summary: summary })
}

ai.complete(prompt, opts?) works against ten providers behind a single API. Pass { provider: "..." } to switch:

Provider provider: value API key env var
OpenAI (default) "openai" OPENAI_API_KEY
Anthropic Claude "anthropic" ANTHROPIC_API_KEY
Google Gemini "gemini" / "google" GEMINI_API_KEY
xAI Grok "grok" / "xai" XAI_API_KEY
Mistral "mistral" MISTRAL_API_KEY
DeepSeek "deepseek" DEEPSEEK_API_KEY
Groq "groq" GROQ_API_KEY
OpenRouter "openrouter" OPENROUTER_API_KEY
Together AI "together" TOGETHER_API_KEY
Ollama (local) "ollama" none — runs locally

ai.stream(prompt, on_chunk, opts?), ai.embed(text), ai.vision(prompt, images) and ai.similarity(a, b) round out the namespace. See examples/ai_providers.mx for a copy-pasteable cheat sheet.

Pattern matching

let label = match status_code {
  200 => "OK"
  404 => "Not Found"
  500 => "Server Error"
  _   => "Status ${status_code}"
}

Authentication with JWT

route POST /login {
  let token = jwt.sign({ sub: request.body.username, exp: now() / 1000 + 3600 }, env("JWT_SECRET"))
  return json({ token: token })
}

route GET /me {
  let claims = jwt.verify(request.headers["authorization"], env("JWT_SECRET"))
  if (claims == null) {
    return status(401, { error: "invalid token" })
  }
  return json(claims)
}

Error handling

try {
  let data = json_parse(request.body.payload)
  return json(data)
} catch (e) {
  return status(400, { error: e.message })
}

Deploy to Vercel

MX Script ships a built-in Vercel adapter. From any project with an app.mx:

mx build --vercel
git add main.go go.mod vercel.json
git commit -m "Deploy via mx build --vercel"
git push   # Vercel autodeploys on push

mx build --vercel generates three files at the project root:

File Role
main.go A 30-line Go entrypoint that //go:embeds app.mx, lexes/parses/loads it via the interpreter library, and serves the resulting handler on $PORT.
go.mod Pins the mxscript runtime to the version of the CLI that generated the build.
vercel.json Declares Vercel's Go framework preset so the build is detected automatically.

Vercel does the rest: detects the Go module, compiles the binary, and runs it as a long-running serverless function on Fluid Compute. Your .mx source is the source of truth — re-run mx build --vercel anytime you upgrade the mx CLI.

Embedding MX in your own Go server? The same building blocks that power the Vercel adapter — interpreter.Load(prog), interpreter.Handler(), interpreter.HasRoutes() — are public. Drop an MX app into any Go binary that speaks net/http.


Editor support

.mx files have full syntax highlighting in VS Code (and any TextMate-compatible editor):

git clone https://github.com/jlkdevelop/mxscript.git
cd mxscript/extras/vscode
code --install-extension .

The grammar lives at extras/syntax/mxscript.tmLanguage.json — drop it into Sublime, Atom, or any other TextMate-aware editor.

GitHub itself doesn't yet recognise .mx natively (we're working through github-linguist), but the .gitattributes in this repo gives the best fallback.


Project structure

mxscript/
├── main.go                 # CLI entry point
├── lexer/lexer.go          # tokenizer
├── parser/
│   ├── ast.go              # AST node types
│   └── parser.go           # recursive-descent parser
├── interpreter/
│   ├── interpreter.go      # tree-walking interpreter + HTTP server
│   ├── builtins.go         # standard library
│   └── parse_helper.go     # bridge for `import "./file.mx"`
├── examples/               # sample .mx programs
└── docs/                   # full language reference

Roadmap

See ROADMAP.md. Highlights:

  • Lexer, parser, interpreter
  • HTTP routes (GET / POST / PUT / DELETE / PATCH)
  • request.body, request.params, request.headers, request.query
  • Middleware via use
  • Control flow (if, else, loop ... as)
  • try / catch
  • Standard library (string, array, math, JSON, types)
  • ai.complete / ai.embed
  • CLI: --port, --watch, --debug, mx init, mx build
  • Local imports: import "./utils.mx"
  • Pre-built binary releases for macOS / Linux / Windows
  • Package registry (mx install pkg)
  • WebSocket routes
  • Built-in SQLite driver
  • LSP for editor support

Contributing

MX Script is open source under the MIT license. Pull requests, issues, and discussions are welcome.

See CONTRIBUTING.md for the full workflow. The TL;DR:

git clone https://github.com/jlkdevelop/mxscript.git
cd mxscript
go build -o mx .
go test ./...
./mx run examples/app.mx

License

MIT © Jassim Alkharafi


Credits

MX Script was created by Jassim Alkharafi (creator & lead developer). If MX Script is useful to you, a ⭐ on GitHub goes a long way.

Documentation

Overview

main is the MX Script command-line entry point. It provides:

mx run app.mx [--port N] [--watch] [--debug]
mx init [name]
mx build app.mx
mx version
mx help

The CLI is intentionally tiny — every feature delegates to one of the language packages (lexer / parser / interpreter).

Directories

Path Synopsis
checker is the static-analysis pass behind `mx check`.
checker is the static-analysis pass behind `mx check`.
cmd
mxwasm command
mxwasm is the WebAssembly entry point for MX Script.
mxwasm is the WebAssembly entry point for MX Script.
Package formatter produces a canonical, opinionated formatting of an MX Script source file.
Package formatter produces a canonical, opinionated formatting of an MX Script source file.
auth_passwordless.go — magic-link tokens (signed, time-limited) and RFC 6238 TOTP (Google Authenticator–compatible).
auth_passwordless.go — magic-link tokens (signed, time-limited) and RFC 6238 TOTP (Google Authenticator–compatible).
Package lexer tokenizes MX Script source code into a stream of tokens that the parser can consume.
Package lexer tokenizes MX Script source code into a stream of tokens that the parser can consume.
Package lsp implements a tiny Language Server Protocol server for MX Script.
Package lsp implements a tiny Language Server Protocol server for MX Script.
Package parser defines the AST node types for MX Script and the recursive-descent parser that builds an AST from a token stream.
Package parser defines the AST node types for MX Script and the recursive-descent parser that builds an AST from a token stream.
pkg implements MX Script's package manager.
pkg implements MX Script's package manager.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL