mxscript

command module
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: May 2, 2026 License: MIT Imports: 13 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.

Founded and developed by Jassim Alkharafi.

server {
  port: 8080
}

let users = [
  { id: 1, name: "Jassim Alkharafi", role: "Founder" }
]

route GET /users {
  return json(users)
}

route GET /users/:id {
  let id = num(request.params.id)
  let user = find(users, fn(u) { return u.id == id })
  if (user == null) {
    return status(404, { error: "not found" })
  }
  return json(user)
}

That's it. No framework imports. No middleware setup. mx run app.mx and you have a JSON API on localhost:8080.


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 still a hundred lines.

MX Script is opinionated: the language is the framework. Routes, JSON, middleware, env vars, and AI calls are all first-class. The interpreter is a single Go binary with zero runtime dependencies.

  • Single-file apps. No package.json, no Cargo.toml, no go.mod from your perspective.
  • HTTP server, JSON parsing, env vars, fetch, and AI built into the language.
  • Clean syntax. route GET /users { ... } reads like English.
  • One binary. No virtual machine, no GC tuning, no node_modules.
  • MIT licensed. Built in the open. Pull requests welcome.

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 reads OPENAI_API_KEY from the environment.

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 (founder & 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
builtins.go installs the MX Script standard library into the global environment.
builtins.go installs the MX Script standard library into the global environment.
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 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.

Jump to

Keyboard shortcuts

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