Library API
Every public value, type and error in the library, package by package: what it takes, what it returns, and what it can raise.
scripts/gen_api_reference.py reads each
src/*/pkg.generated.mbti — the interface moon info
writes and moon check holds the source to — and the
/// block above each declaration. Change a signature and this
page changes with it; CI regenerates it and fails on a diff. An entry
without prose has none in the source either, and is left bare rather than
given a sentence invented to fill the space.
Using it #
The library is pure: a string in, a string out. Add the module, import the package, and call it from a build step, a test or a page.
let spec = @moonctl.parse(source) let code = @moonctl.generate(spec) // a moonapi scaffold, as text let tree = @moonctl.generate_tree(spec) // the layered project, file by file
The root package re-exports the types the other packages declare, so a caller
can name @moonctl.Spec, @moonctl.Route and
@moonctl.Template without importing anything else. Each
re-exported type is listed under the package that owns it.
Packages #
Each package has one job. The table gives their weight, sorted by import path; the sections below give their surface.
| Import path | Package | Items |
|---|---|---|
moonbitstack/moonctl | moonctl | 106 |
moonbitstack/moonctl/agent | agent | 1 |
moonbitstack/moonctl/api | api | 26 |
moonbitstack/moonctl/cli | cli | 39 |
moonbitstack/moonctl/client | client | 4 |
moonbitstack/moonctl/doc | doc | 6 |
moonbitstack/moonctl/gen | gen | 15 |
moonbitstack/moonctl/gql | gql | 1 |
moonbitstack/moonctl/model | model | 23 |
moonbitstack/moonctl/plugin | plugin | 11 |
moonbitstack/moonctl/proto | proto | 19 |
moonbitstack/moonctl/scaffold | scaffold | 28 |
moonbitstack/moonctl/style | style | 2 |
moonbitstack/moonctl/template | template | 5 |
moonctl #
Values
pub fn binders_parse_text(@api.Spec) -> Boolpub fn bound_types(@api.Spec) -> Array[(String, Array[@api.Field])]pub fn bug_report(String, String, Array[(String, String)]) -> Stringpub fn cache_file_name(String) -> Stringpub fn client_file(@api.Spec, @client.Lang) -> (String, String)pub fn client_file_name(@client.Lang, String) -> Stringpub fn command_names() -> Array[String]pub fn commands() -> Array[@cli.Command]pub fn completion(String) -> Stringpub fn config_get(@cli.Config, String) -> String?pub fn config_keys() -> Array[String]pub fn config_set(@cli.Config, String, String) -> @cli.Configpub fn config_text(@cli.Config) -> Stringpub let default_branch : Stringpub fn default_config() -> @cli.Configpub fn default_routes(@proto.Proto) -> Array[@proto.GatewayRoute]pub fn deps(String, from? : String) -> Array[String]pub fn env_report(String, Array[(String, String)]) -> Stringpub fn find_template(String) -> @cli.TemplateEntry?pub fn format(String) -> Stringpub fn gateway_file_name(String) -> Stringpub fn gen_files_to_json(Array[@plugin.GenFile]) -> @json.Jsonpub fn generate(@api.Spec, template? : String) -> String raise @template.Refusedpub fn generate_agent(@api.Spec) -> Stringpub fn generate_cached(@api.Spec) -> Stringpub fn generate_client(@api.Spec, @client.Lang) -> Stringpub fn generate_crud(Array[@model.DdlTable]) -> Stringpub fn generate_crud_from_ddl(String) -> Stringpub fn generate_crud_from_reflection(Array[@model.ReflectedColumn]) -> Stringpub fn generate_doc(@api.Spec, version? : @doc.DocVersion, title? : String, api_version? : String) -> Stringpub fn generate_gateway(@proto.Proto, Array[@proto.GatewayRoute]) -> Stringpub fn generate_gql(@api.Spec) -> Stringpub fn generate_gql_tree(@api.Spec, style? : @style.Style, dir? : String) -> Array[@scaffold.TreeFile]pub fn generate_grpc(@proto.Proto) -> Stringpub fn generate_markdown(@api.Spec, title? : String) -> Stringpub fn generate_mcp_from_spec(@api.Spec, style? : @style.Style, dir? : String) -> Array[@scaffold.TreeFile]pub fn generate_mcp_tree(String, style? : @style.Style, dir? : String, tools? : String, registration? : String, checks? : String) -> Array[@scaffold.TreeFile]pub fn generate_migrations(@api.Spec) -> Stringpub fn generate_model(@api.Spec) -> Stringpub fn generate_mongo(@api.Spec) -> Stringpub fn generate_orm_tree(@api.Spec, style? : @style.Style, dir? : String) -> Array[@scaffold.TreeFile]pub fn generate_orm_tree_from_ddl(Array[@model.DdlTable], String, style? : @style.Style, dir? : String) -> Array[@scaffold.TreeFile]pub fn generate_rpc_tree(@proto.Proto, style? : @style.Style, dir? : String) -> Array[@scaffold.TreeFile]pub fn generate_tree(@api.Spec, style? : @style.Style, dir? : String) -> Array[@scaffold.TreeFile]pub fn generate_with(@api.Spec, String) -> String raise @template.Refusedpub fn gql_dropped_fields(@api.Spec) -> Array[String]pub fn has_errors(Array[@api.Diagnostic]) -> Boolpub fn has_method(@proto.Proto, String) -> Boolpub fn is_bundled(@cli.Library) -> Boolpub fn library(home? : String, remote? : String, branch? : String) -> @cli.Librarypub fn markdown_file_name(String) -> Stringpub fn mcp_skipped(@api.Spec) -> Array[String]pub fn migrate_config(String) -> (String, Array[String])pub fn migrate_file_name(String) -> Stringpub fn mod_pins(String) -> Array[(String, String)]pub fn mongo_file_name(String) -> Stringpub fn openapi_document(@api.Spec, version? : @doc.DocVersion, title? : String, api_version? : String) -> @json.Jsonpub fn parse(String) -> @api.Spec raise @api.Refusedpub fn parse_all(String, Map[String, String], from? : String) -> @api.Spec raise @api.Refusedpub fn parse_config(String) -> @cli.Configpub fn parse_ddl(String) -> Array[@model.DdlTable]pub fn parse_dsn(String) -> @model.DataSource raise @model.Refusedpub fn parse_gen_files(@json.Json) -> Array[@plugin.GenFile]pub fn parse_lenient(String) -> @api.Specpub fn parse_proto(String) -> @proto.Protopub fn parse_route_map(@proto.Proto, String) -> (Array[@proto.GatewayRoute], Array[String])pub fn pin_module(String, String, String) -> (String, String?)pub fn pinned_version(String, String) -> String?pub let pins : Map[String, String]pub fn plugin_argv(String) -> (String, Array[String])pub fn plugin_from_json(@json.Json) -> @plugin.Pluginpub fn plugin_plan(Array[@plugin.GenFile]) -> (Array[@plugin.GenFile], Array[String])pub fn plugin_request(@api.Spec, api_file_path? : String, style? : String, dir? : String) -> Stringpub let port : Int32pub fn proto_reserved_conflicts(@proto.ProtoMessage) -> Array[String]pub fn render(String, @template.Value) -> String raise @template.Refusedpub fn render_template_dir(@api.Spec, Array[@gen.TemplateFile]) -> Array[(String, String)] raise @template.Refusedpub fn render_template_file(@api.Spec, @gen.TemplateFile) -> (String, String) raise @template.Refusedpub fn resolve_import(String, String) -> Stringpub fn safe_path(String) -> Boolpub fn scaffold_api(String) -> Array[@plugin.GenFile]pub fn scaffold_docker(String, port? : Int32) -> Array[@plugin.GenFile]pub fn scaffold_gql(String) -> Array[@plugin.GenFile]pub fn scaffold_kube(String, port? : Int32, replicas? : Int32) -> Array[@plugin.GenFile]pub fn scaffold_mcp(String) -> Array[@plugin.GenFile]pub fn scaffold_model(String) -> Array[@plugin.GenFile]pub fn scaffold_rpc(String) -> Array[@plugin.GenFile]pub fn spec_from_json(@json.Json) -> @api.Specpub fn spec_to_json(@api.Spec) -> @json.Jsonpub fn spec_to_value(@api.Spec) -> @template.Valuepub fn subcommands(String) -> Array[String]pub fn swagger_ui_stub(spec_url? : String, title? : String) -> Stringpub fn tables_from_reflection(Array[@model.ReflectedColumn]) -> Array[@model.DdlTable]pub fn template_detail(@cli.TemplateEntry) -> Stringpub fn template_dir(String, @cli.TemplateEntry) -> Stringpub fn template_starter(Map[String, String]) -> Array[(String, String)]pub fn template_table() -> Stringpub fn templates() -> Array[@cli.TemplateEntry]pub fn templates_of(String) -> Array[@cli.TemplateEntry]pub fn tree_plan(Array[@scaffold.TreeFile], (String) -> Bool) -> Array[@plugin.GenFile]pub fn upgrade_pins(String, Array[String], String) -> (String, Array[String])pub fn usage_text(String) -> Stringpub fn validate(@api.Spec) -> Array[@api.Diagnostic]pub fn validate_files(String, Map[String, String], from? : String) -> Array[@api.Diagnostic]pub fn validate_source(String) -> Array[@api.Diagnostic]Aliases
pub type FuncFn = (Array[@template.Value]) -> @template.Value raise @template.RefusedRe-exported types
| Type | Comes from |
|---|---|
Bind | api |
Command | cli |
Config | cli |
DataSource | model |
DdlColumn | model |
DdlTable | model |
Diagnostic | api |
DocVersion | doc |
Field | api |
GatewayRoute | proto |
GenFile | plugin |
Group | api |
Label | proto |
Lang | client |
Library | cli |
PgTarget | model |
Plugin | plugin |
Proto | proto |
ProtoEnum | proto |
ProtoEnumValue | proto |
ProtoField | proto |
ProtoMessage | proto |
ProtoOneof | proto |
ProtoService | proto |
Range | api |
ReflectedColumn | model |
Regen | scaffold |
Route | api |
Rpc | proto |
Severity | api |
Spec | api |
Style | style |
Template | template |
TemplateEntry | cli |
TemplateFile | gen |
TreeFile | scaffold |
TypeDef | api |
Value | template |
agent #
Values
pub fn generate_agent(@api.Spec) -> StringGenerate a moonkoog agent scaffold from spec. Every route becomes a Tool whose descriptor advertises the operation (a single string input parameter to fill out) and whose execute_raw is a stub, and build_agent assembles them into an AIAgent with the service's name in its system prompt. The output compiles against moonbitstack/moonkoog; fill in each execute_raw to make it run.
api #
Values
pub fn deps(String, from? : String) -> Array[String]The files source imports, each resolved against from — the path source itself was read from. A caller that can read files walks a multi-file spec with this: read a file, follow its deps, and hand everything it collected to parse_all.
pub fn find_type(Array[TypeDef], String) -> TypeDef?The TypeDef named name, or None when the spec declared no such block.
pub fn flat_fields(TypeDef, Array[TypeDef]) -> Array[Field]Every field a type block carries with its inline embeds folded in first, which is both the shape an embedded Go struct serialises to and the only one MoonBit can spell — a MoonBit struct has no embedding. An embed naming a block that is not in types, and one that leads back to a block already folded in, contribute nothing.
pub fn format(String) -> StringFormat an .api source. Nesting is read off braces and off the two blocks that open with a parenthesis — a multi-line @server ( and a type ( group; blank lines collapse to one; the result ends in exactly one newline. Comments and struct tags survive verbatim, and the function is idempotent: formatting an already formatted spec returns it unchanged.
pub fn has_errors(Array[Diagnostic]) -> BoolWhether any finding is an Error — what makes mctl api validate exit non-zero.
pub fn index_of(String, UInt16, Int32) -> Int32Index of the first ch in s at or after from, or -1 if absent.
pub fn parse(String) -> Spec raise RefusedRead a goctl-style .api description into a Spec. Grammar (one statement per line):
syntax = "v1"
info (
title: "greet"
version: "v2"
)
type LoginReq {
name: string
}
@server (
group: user
prefix: /api/v1
middleware: Log
)
service greet {
@doc "health check"
@handler ping
get /ping
@handler login
post /login (LoginReq) returns (LoginResp)
get /legacy legacy_handler "the inline form"
}A type block declares a message schema (its fields become a MoonBit struct); field types use goctl's Go spellings (string, int64, []T, map[K]V, …). An @server( … ) block annotates every route of the service block that follows it: the routes carry its prefix in their paths and its Group on their group. Verbs are lower-cased, blank lines and // comments are dropped.
Any other line raises a Refused naming it — a misspelt verb or a missing brace would otherwise generate a quietly truncated program. Use parse_lenient for a best-effort read of a spec that is still being written.
pub fn parse_all(String, Map[String, String], from? : String) -> Spec raise RefusedParse source — the spec at from — together with everything it imports, taken from files (a map from resolved path to source, the shape deps resolves to). What the imports describe is merged in first, in the order they were declared, then the importing file's own: types, routes and @server groups all end up in one Spec. A file is scanned once however many times it is imported, so a cycle terminates.
The service name is the one the importing file declares, or else the last one its imports declared, so a spec that does nothing but import still takes the name from what it imported.
An import naming a file that is not in files raises Missing; a file that does not parse raises its first complaint the way parse does, with the file it came from named alongside the line.
pub fn parse_lenient(String) -> Specparse without the diagnostics: a line the parser cannot read is skipped and whatever the spec does describe is returned. For a caller that generates from a half-written spec on every keystroke; anything that reports to a user should call parse.
pub fn resolve_import(String, String) -> StringAn import path resolved against the file that declared it: taken relative to that file's directory, with ./.. folded away. An already-absolute path is left where it points.
pub fn to_i64(String) -> Int64?s read as a non-negative decimal, or None when it is not one.
pub fn tokens(String) -> Array[String]Whitespace-split a line into tokens, keeping a "quoted string" as one token (with the quotes stripped).
pub fn validate(Spec) -> Array[Diagnostic]Check a parsed spec: the routes, the types and the places one names the other. Findings come in spec order — routes first, then types — so a report reads the way the file does.
pub fn validate_files(String, Map[String, String], from? : String) -> Array[Diagnostic]Validate one file of a spec together with everything it imports, files being every source the walk has read keyed by the path it was read from. Without this a spec that embeds a type from another file is told that the type does not exist — true of the file on its own, false of the spec it belongs to.
pub fn validate_source(String) -> Array[Diagnostic]Validate a spec given as source. A spec the parser cannot read is reported as the one finding it is — syntax, naming the line — rather than raising, so a caller checks a file the same way whether it is broken or merely wrong.
Errors
pub(all) suberror Refused
A spec the parser could not read: Syntax is the 1-based line and what went wrong there, Missing an import whose path was not among the files handed to parse_all.
Syntax(line~ : Int32, msg~ : String)Missing(file~ : String, path~ : String)
Types
pub(all) enum Bind derive(Eq)
Where a field's value is bound from (← goctl's struct tags): the request body (json:, and an untagged field), the query string (form:), a URL segment (path:), or a request header (header:).
BodyQueryPathHeader
Methods
pub fn Bind::equal(Self, Self) -> Boolpub fn Bind::not_equal(Self, Self) -> Boolpub(all) struct Diagnostic
One finding of validate: how bad it is, the rule that produced it, the at — the route, type or field it is about — and a sentence saying what is wrong.
| Field | Type |
|---|---|
severity | Severity |
rule | String |
at | String |
message | String |
pub impl Show for Diagnostic
Methods
pub fn Diagnostic::output(Self, &Logger) -> Unitpub fn Diagnostic::to_string(Self) -> Stringpub(all) struct Field
One field of a type block: its name, the (already MoonBit-mapped) type, and the raw back-tick struct tag (json:"…"/path/form/header, empty when absent) that carries the wire name and binding location.
| Field | Type |
|---|---|
name | String |
type_ | String |
tag | String |
Methods
pub fn Field::bind(Self) -> BindWhere this field's value is bound from: form: off the query string, path: out of a URL segment, header: off a request header, and everything else — an untagged field included — out of the request body.
pub fn Field::default_(Self) -> String?The default= this field's tag declared, written the way the spec wrote it, or None when it declared none.
pub fn Field::json_name(Self) -> StringThe name this field is carried under on the wire: the one its binding tag gives (json:"user_name" → user_name, form:"page" → page, path:"region" → region), with the options after it stripped, falling back to the field's own name when it carries no tag.
pub fn Field::mbt_name(Self) -> StringThe MoonBit spelling of this field: snake_case, because a MoonBit struct field has to start lower-case — UserName is not one. Every generator that emits MoonBit names the field with this, so the struct, the row decoder and the column projection all agree; json_name is the wire side of the same field.
pub fn Field::optional(Self) -> BoolWhether the tag marked this field optional (or Go's omitempty): it may be absent, which is what keeps it out of a schema's required list.
pub fn Field::options(Self) -> Array[String]The values an options=a|b|c tag allows this field to take, empty when the tag declared no such list.
pub fn Field::range(Self) -> Range?The range=[lo:hi] bounds this field's tag declared, or None when it declared none. Either bound may be empty, which leaves that end open.
pub(all) struct Group
The @server( … ) annotations governing a block of routes (← goctl's route group): the name under which its routes are grouped, the path prefix already folded into their paths, the jwt claim type, the middleware chain, the max_bytes request cap (0 when unset), the request timeout, and whether the group's requests are signed. extra keeps every other annotation the block carried, in order — goctl lets a spec invent its own, and dropping them would lose what the author wrote.
| Field | Type |
|---|---|
name | String |
prefix | String |
jwt | String |
middleware | Array[String] |
max_bytes | Int64 |
timeout | String |
signature | Bool |
extra | Array[(String, String)] |
pub(all) struct Range derive(Eq)
A range= constraint, e.g. range=[1:120] or range=(0:]. Each bound is written as the spec wrote it and is empty when that end is open; lo_inc / hi_inc say whether the bracket was inclusive ([ ]) or exclusive (( )).
| Field | Type |
|---|---|
lo | String |
hi | String |
lo_inc | Bool |
hi_inc | Bool |
Methods
pub fn Range::equal(Self, Self) -> Boolpub fn Range::not_equal(Self, Self) -> Boolpub(all) struct Route
One route in a service spec: HTTP verb (lower-cased), path pattern (with its group's prefix applied), handler name, an optional summary, and the @server group it was declared under. A modern goctl route (verb /path (Req) returns (Resp)) also carries its request/response type names (its @doc fills summary); both are empty for the legacy inline form (verb /path handler "summary").
| Field | Type |
|---|---|
verb | String |
path | String |
handler | String |
summary | String |
req | String |
resp | String |
group | Group? |
pub(all) struct Service
One service block: the name it declares and the routes it holds, in the order they were written. Blocks that repeat a name are one service — a spec may split a service across several blocks to give each its own @server annotations — so a name appears at most once, at the place it first did.
| Field | Type |
|---|---|
name | String |
routes | Array[Route] |
pub(all) enum Severity derive(Eq)
How bad a finding is: Error means the generated code would not compile or would not match the spec, Warning means it would compile but probably is not what the author meant.
ErrorWarning
Methods
pub fn Severity::equal(Self, Self) -> Boolpub fn Severity::not_equal(Self, Self) -> Boolpub(all) struct Spec
A parsed .api service specification. groups lists the @server( … ) blocks in the order they were declared; each route also points at the one it belongs to. services keeps the service blocks apart — one entry per name — while routes is the same routes flattened in file order. imports holds the import paths exactly as the file wrote them — relative to the file itself, so resolving one needs the path it was read from; deps and parse_all do that.
| Field | Type |
|---|---|
service | String |
routes | Array[Route] |
services | Array[Service] |
types | Array[TypeDef] |
info | Array[(String, String)] |
groups | Array[Group] |
imports | Array[String] |
pub(all) struct TypeDef
A named message schema declared by a type Name { field: Type … } block, generated into a MoonBit struct (goctl's request/response types). embeds names the blocks it inlined — a bare Base line — whose fields belong to it as if they had been written out; an anonymous nested block instead becomes a TypeDef of its own, named after the two blocks, with a field pointing at it.
| Field | Type |
|---|---|
name | String |
fields | Array[Field] |
embeds | Array[String] |
cli #
Values
pub fn bug_report(String, String, Array[(String, String)]) -> Stringmctl bug: a report with the environment filled in and the questions a maintainer would otherwise have to ask. title may be empty, in which case the report asks for one.
pub fn built_binary(String) -> String?The executable a moon run --build-only built, from what it prints.
pub fn command_names() -> Array[String]The words a shell should complete at the first position.
pub fn commands() -> Array[Command]Every command mctl accepts, in the order help prints them: the generators, the scaffolds, the spec tools, then the housekeeping.
pub fn completion(String) -> StringA shell completion script for shell, or "" for a shell mctl has none for. Completion is generated from commands and subcommands, so a new command is completable the moment it is in the table.
pub fn config_get(Config, String) -> String?The value of one key, or None when mctl has no such key.
pub fn config_keys() -> Array[String]The keys config get and config set accept.
pub fn config_set(Config, String, String) -> Configc with one key set. template appends — a config may name several — while a scalar key replaces what was there. An unknown key is returned unchanged, so a caller checks the key against config_keys first.
pub fn config_text(Config) -> StringThe configuration as the file it is stored in, with a comment saying what the file is for and where the keys come from.
pub let default_branch : StringThe branch --remote reads when --branch names none.
pub fn default_config() -> ConfigThe file mctl writes when there is none yet: the bundled library, the default branch, the gozero spelling.
pub fn env_report(String, Array[(String, String)]) -> Stringmctl env: one line per setting, then a hint at what to change. rows pairs a setting with its value and, in parentheses, where it came from.
pub fn find_template(String) -> TemplateEntry?The template called name, or None.
pub fn is_bundled(Library) -> BoolWhether this is the bundled library, which is code rather than files.
pub fn is_skipped(String) -> BoolWhether a directory is left out of the walk: what the build itself writes, and anything hidden.
pub fn is_watched(String) -> BoolWhether a save to this file calls for a rebuild: MoonBit source, or the manifests that say how it is built.
pub fn library(home? : String, remote? : String, branch? : String) -> LibraryThe library the three flags describe. --home wins over --remote when both are given: a local directory is already checked out.
pub fn migrate_config(String) -> (String, Array[String])A v1 configuration — the uppercase, environment-style keys mctl read before it had a file — rewritten as the current shape. Returns the new text and one line per key that changed, so mctl config migrate can say what it did.
pub fn mod_pins(String) -> Array[(String, String)]The version strings of mod, as (name, version) pairs: every pin the file carries, in order.
pub fn parse_config(String) -> ConfigRead a configuration. A key mctl does not know is kept out of the result rather than refused, so a file written by a newer mctl still loads. An empty or unreadable-text file is the default configuration.
pub fn parse_serve(ArrayView[String]) -> Serve raiseRead mctl serve [package] [--reload] [--reload-dir dir]... [--reload-delay seconds] [-- args]. Everything after -- is the server's own.
pub fn pin_module(String, String, String) -> (String, String?)mod with name pinned at version, or mod unchanged when it does not pin that name. Returns the new text and the change it made, if any.
pub fn pinned_version(String, String) -> String?The version name is pinned at in mod, or None when the file does not pin it.
pub let reload_delay : Int32How long to wait between looks at the sources, in milliseconds (← uvicorn's --reload-delay, 0.25 seconds).
pub let reload_dirs : Array[String]Which directories are watched when none are named (← uvicorn's --reload-dir, the current directory).
pub fn subcommands(String) -> Array[String]The second word of a command that takes a sub-command, for a completion script that completes two levels deep.
pub fn template_detail(TemplateEntry) -> Stringmctl template show <name>: one entry, and where it sits in the library.
pub fn template_dir(String, TemplateEntry) -> StringThe directory t is read from, given the root the library resolves to. The root is the --home directory, or where --remote was checked out.
pub fn template_starter(Map[String, String]) -> Array[(String, String)]The files mctl template init writes: one entry under api/service, small enough to read in a minute and complete enough to serve the spec it is given. Both the file names and the text are templates, so a library is edited as templates and read back the same way.
pub fn template_table() -> Stringmctl template list: a table a reader scans rather than a paragraph they read.
pub fn templates() -> Array[TemplateEntry]The bundled library, in the order it is listed.
pub fn templates_of(String) -> Array[TemplateEntry]The templates of one kind — the ones a gen of that kind may pick.
pub fn upgrade_pins(String, Array[String], String) -> (String, Array[String])mctl upgrade <version>: every name in modules moved to version, plus one line per name that changed. A name the file does not pin is left alone and reported as skipped — pinning it would be a change the user did not ask for.
pub fn usage_text(String) -> StringThe mctl help page: what mctl is, then one line per command, then the flags every generator shares.
Types
pub(all) struct Command
One command: what it is called, the line that shows how to call it, and one sentence saying what it does.
| Field | Type |
|---|---|
name | String |
usage | String |
summary | String |
pub(all) struct Config
One configuration: the fields mctl understands. version is the file format, not the mctl version, and it goes up when migrate_config learns a new shape.
| Field | Type |
|---|---|
version | Int32 |
home | String |
remote | String |
branch | String |
style | String |
templates | Array[String] |
pub(all) struct Library
The template library a run reads from. Empty home and remote mean the bundled library — the scaffolds in code; --home names a directory laid out like it, --remote a git repository to check out at branch.
| Field | Type |
|---|---|
home | String |
remote | String |
branch | String |
pub(all) struct Serve
What mctl serve was asked to do: the package to build and run, whether to rebuild it on change, where and how often to look, and the arguments that go to the server.
| Field | Type |
|---|---|
pkg | String |
reload | Bool |
dirs | Array[String] |
delay | Int32 |
args | Array[String] |
pub(all) struct TemplateEntry
One template: the name a user types, the kind of spec it reads, one sentence on what it produces, and the directory under the template home it lives in.
| Field | Type |
|---|---|
name | String |
kind | String |
description | String |
path | String |
client #
Values
pub fn client_file(@api.Spec, Lang) -> (String, String)The file name and the source of the client lang for spec, which is what mctl api ts <file.api> writes.
pub fn client_file_name(Lang, String) -> StringThe file a client for service lands in: greet_client.ts and friends, named after the service so two specs generated into one directory do not collide.
pub fn generate_client(@api.Spec, Lang) -> StringGenerate the client of lang for spec. The result is one file: the models the spec declares, then the client class, in that order, so a reader meets the data before the calls that carry it.
Types
pub(all) enum Lang
The language a client is generated for: the four goctl generates or that the community asks for.
TypeScriptDartKotlinJava
Methods
pub fn Lang::ext(Self) -> StringThe file extension a client of this language carries (ts, dart, kt, java).
pub fn Lang::label(Self) -> StringHow the language is spelled in a comment, e.g. TypeScript.
doc #
Values
pub fn generate_doc(@api.Spec, version? : DocVersion, title? : String, api_version? : String) -> StringGenerate an OpenAPI / Swagger document from spec, stringified with two-space indentation. version picks the dialect (Swagger 2.0 / OpenAPI 3.0 / 3.1).
pub fn generate_markdown(@api.Spec, title? : String) -> StringThe whole reference: the service, a route table, then a table per type. title replaces the service name in the heading for a reader who wants the document to say what the service is for.
pub fn markdown_file_name(String) -> StringThe file name a Markdown reference lands in: greet → greet_api.md.
pub fn openapi_document(@api.Spec, version? : DocVersion, title? : String, api_version? : String) -> @json.JsonBuild the OpenAPI / Swagger document for spec as a Json value. Routes fold into paths → HTTP method → operation (with operationId, any :name path parameters, and a 200 response); every type block becomes a component schema (definitions in 2.0, components/schemas in 3.x). version selects the document dialect; title/api_version fill the info block.
pub fn swagger_ui_stub(spec_url? : String, title? : String) -> StringA self-contained Swagger UI page for the document served at spec_url. The same stub moonapi ships, so a generated service and a live app render alike.
Types
pub(all) enum DocVersion derive(Eq)
Target OpenAPI / Swagger document version. mctl emits every mainstream version from one .api spec — the same shape moonapi's runtime emitter takes, so a generated service and a hand-built moonapi app document the same way.
Swagger20OpenApi30OpenApi31
Methods
pub fn DocVersion::equal(Self, Self) -> Boolpub fn DocVersion::not_equal(Self, Self) -> Boolgen #
Values
pub fn binders_parse_text(@api.Spec) -> BoolWhether any binder reads a value out of text, which is what moonbitlang/core/string is imported for. A spec whose request fields are all text or all in the body needs none of it, and an unused import fails moon check in a generated project.
pub fn bound_types(@api.Spec) -> Array[(String, Array[@api.Field])]The request types spec's routes declare, in the order they first appear, each with its fields — the types a binder is written for. A route naming a type the spec never declares is left out: there is nothing to build.
pub fn generate(@api.Spec, template? : String) -> String raise @template.RefusedGenerate compilable moonapi scaffolding from a spec: a MoonBit struct for every type block, a build_app that wires every route to its handler, plus a stub for each handler. The routing part depends only on moonbitstack/moonapi and moonbitstack/moonasgi; the emitted schemas are dependency-free.
Pass template to render the spec through the runtime template engine (see generate_with) instead of the built-in generator; omit it for the default scaffold.
pub fn generate_builtin(@api.Spec) -> Stringpub fn generate_with(@api.Spec, String) -> String raise @template.RefusedGenerate code from a Spec with a caller-supplied template instead of the built-in generator — the runtime-template path goctl exposes for custom scaffolds. Equivalent to render(template_source, spec_to_value(spec)).
pub fn group_note(@api.Group) -> StringA group's annotations spelled the way the spec wrote them, e.g. group: user, prefix: /api/v1, middleware: Log. Empty for a group that carries nothing.
pub fn number_json(String) -> @json.Jsonraw as a JSON number, or as a string when it does not read as one — a tag option is text until something asks it to be a number.
pub fn render_binder(String, Array[@api.Field]) -> Stringbind_<type> for one request type: read every field from the place its tag names, then build the struct. A field bound from the body and one bound from the path are read the same way the request puts them there, so a request whose body and path disagree takes each value from where its tag says.
pub fn render_binders(@api.Spec) -> StringEvery binder spec needs, with the helpers they read through, as the internal/types package's generated source tail. Empty when no route declares a request type.
pub fn render_structs(Array[@api.TypeDef], derive_json? : Bool) -> StringA MoonBit struct for every type block, in the order the spec declared them, each followed by whatever its tags asked to be checked (see render_checks).
pub fn render_template_dir(@api.Spec, Array[TemplateFile]) -> Array[(String, String)] raise @template.RefusedRender every file of a template directory. The result is in the order the caller listed them, so a file that has to exist first (a moon.mod) can be put first.
pub fn render_template_file(@api.Spec, TemplateFile) -> (String, String) raise @template.RefusedRender t's path and text over spec, returning the file to write. A path is rendered like any other template, so {{.service}}/moon.mod becomes greet/moon.mod.
pub fn route_path(String) -> StringA spec path as the route template moonapi reads: each :name segment of the spec becomes {name}.
pub fn spec_to_value(@api.Spec) -> @template.ValueExpose a parsed .api Spec as template Value data: a Dict with service (string), services (list of {name, routes}), routes (list of {verb, path, route, handler, summary, hasSummary, group, hasGroup, req, resp, hasReq, hasResp}, where route is path as the framework spells it), types (list of {name, fields:[{name, type}]}), groups (list of the @server blocks) and hasTypes (bool). This is what the template-driven generator ranges over.
Types
pub(all) struct TemplateFile
One file of a template directory: where it sits relative to the directory's root, and what is in it. Both are templates over the spec.
| Field | Type |
|---|---|
path | String |
text | String |
gql #
Values
pub fn generate_gql(@api.Spec) -> StringGenerate a moongql code-first schema from spec's type blocks. Each type becomes an object type with its fields mapped to GraphQL types, and the Query root has ping: String! plus a <type>(id: ID!): <Type> lookup per type.
Only the schema is generated: the resolvers are the author's code. A root field needs one; an object field without one reads its parent's member of that name.
model #
Values
pub fn cache_file_name(String) -> StringThe file a cached layer lands in: greet → greet_cache.mbt, beside the <service>_model.mbt whose records it caches.
pub fn crud_for(DdlTable, String, nullable_aware? : Bool) -> StringThe typed CRUD helpers for one table, against the model model_expr names. nullable_aware binds an optional field of a nullable column as SQL NULL, which is what the annotated form's optional types need.
pub fn generate_cached(@api.Spec) -> StringGenerate the cached CRUD layer for every table of spec: for a type block with a field to look records up by, the key function plus read-through find, invalidating write, and evict. A block with no single-valued field has no key to cache under, so it gets no layer and the file says why.
The output needs no import and no driver: the record types it names are the ones mctl model generates for the same spec.
pub fn generate_crud(Array[DdlTable]) -> StringGenerate a moonorm data-access layer from parsed DDL: for every table, the record struct, its @moonorm.Model (columns + from_row + to_columns), a @moonorm.Table descriptor, an up/down migration pair, and typed CRUD (insert/all, plus find_by_id/update/delete_by_id when the table has a single-column primary key). The output compiles against moonbitstack/moonorm + moonbitstack/moondb; a consuming package imports both. This is the .sql-schema counterpart of goctl's model mysql ddl.
pub fn generate_crud_from_ddl(String) -> StringParse a SQL DDL script and generate its moonorm data-access layer in one step.
pub fn generate_crud_from_reflection(Array[ReflectedColumn]) -> StringGenerate a moonorm data-access layer (models + typed CRUD) straight from a live schema's reflected columns — the in-memory counterpart of generate_crud_from_ddl. The reflect sub-package produces the ReflectedColumns from a real connection.
pub fn generate_migrations(@api.Spec) -> StringThe migration file for a spec: one CREATE TABLE per type. service names the file and the header, so two specs migrated into one directory do not look alike.
pub fn generate_model(@api.Spec) -> StringGenerate a moonorm data layer for every type block in spec. A block whose fields are all storable yields: the model struct; a <table>_model : @moonorm.Model[T] built with @moonorm.Model::new — declared columns (an id column is the primary key), a from_row decoder, and the to_columns projection an INSERT binds; a <table>_table : @moonorm.Table descriptor; and a <table>_up / <table>_down migration pair (create the table idempotently / drop it). This is the explicit MoonBit stand-in for a SQLAlchemy declarative class plus an Alembic revision, which reflection would otherwise synthesise. A block with a non-storable field (a slice, map, or nested message) still gets its struct and a plain @moonorm.Table descriptor.
The output compiles against moonbitstack/moonorm + moonbitstack/moondb; a consuming package imports both.
pub fn generate_mongo(@api.Spec) -> StringGenerate a Mongo document model for every type block of spec: a Doc struct that is the document as stored, and a Repo over the Collection port with insert, all, and — when the spec has a key field — find, replace and delete.
The output compiles against MoonBit core: Json and ToJson are builtin, and the file imports moonbitlang/core/json as @json for from_json. A driver supplies the port; without one, nothing here needs a database to typecheck.
pub fn migrate_file_name(String) -> StringThe name a migration file takes: greet → greet_migrate.sql.
pub fn migration_for(DdlTable, String) -> StringThe <table>_up / <table>_down migration pair for one table, against the model model_expr names.
pub fn migration_updown(String, String) -> StringThe <table>_up / <table>_down migration pair, against the model model_expr names — user_model for the flat generator, User::model() for an annotated one.
pub fn mongo_file_name(String) -> StringThe file a document model lands in: greet → greet_mongo.mbt, named after the service so two specs generated into one directory do not collide.
pub fn parse_ddl(String) -> Array[DdlTable]Parse a SQL DDL script into its CREATE TABLE definitions. Recognises CREATE TABLE [IF NOT EXISTS] name ( … ) (quoted or bare name), skipping any other statement. Column types, primary keys (column- or table-level), NOT NULL and DEFAULT clauses are captured; everything needed to emit a moonorm model.
pub fn parse_dsn(String) -> DataSource raise RefusedParse a datasource DSN into a DataSource. Recognised forms:
- SQLite:
sqlite:PATH,sqlite://PATH,sqlite3:PATH,file:PATH, the literal:memory:, or a bare path ending in.db/.sqlite/.sqlite3. - PostgreSQL:
postgres://[user[:password]@]host[:port][/database][?…](and thepostgresql://spelling). The port defaults to5432, the user topostgres; the database name is required.
Raises BadDsn on an unrecognised scheme or a PostgreSQL URL missing its host or database.
pub fn storage_class(String) -> String?The ColumnType constructor for a field type, or None when the type has no single-column storage class (Array, Map, a nested type, Float, unsigned ints — none map onto one @moondb.Value). Only a type in this set gets a declared column, a from_row read, and a to_columns bind, so a model is only generated when every field is storable.
pub fn tables_from_reflection(Array[ReflectedColumn]) -> Array[DdlTable]Fold reflected columns into DdlTables, grouping by table in first-seen order and mapping each raw SQL type onto its MoonBit scalar (the leading type keyword drives the mapping, so character varying and VARCHAR(255) both land on String). The result feeds generate_crud, so a live schema and a .sql file reach the same model generator.
Errors
pub(all) suberror Refused
Raised when a DSN string cannot be parsed into a DataSource.
BadDsn(String)
Types
pub(all) enum DataSource
A parsed datasource DSN: which backend to read, and how to reach it.
Sqlite(String)Postgres(PgTarget)
pub(all) struct DdlColumn
One parsed column of a CREATE TABLE statement: the SQL column name, the MoonBit type_ its SQL type maps to (Int/Int64/String/Double/Bool/ Bytes), whether it is a primary key, whether it accepts NULL, and its literal DEFAULT clause if any (kept as source text — it is emitted into DDL comments, never bound).
| Field | Type |
|---|---|
name | String |
type_ | String |
mut primary_key | Bool |
nullable | Bool |
default_ | String? |
pub(all) struct DdlTable
A parsed CREATE TABLE: the SQL table name and its ordered columns.
| Field | Type |
|---|---|
name | String |
columns | Array[DdlColumn] |
pub(all) struct PgTarget
A PostgreSQL connection target parsed from a postgres:// DSN.
| Field | Type |
|---|---|
host | String |
port | Int32 |
user | String |
password | String |
database | String |
pub(all) struct ReflectedColumn
One column of a reflected database schema: the table it belongs to, its name, its raw SQL type text (INTEGER, character varying, VARCHAR(255), …), and whether it is a primary key / accepts NULL. This is the neutral shape a live reader (SQLite PRAGMA table_info, PostgreSQL information_schema.columns) produces and tables_from_reflection folds into DdlTables.
| Field | Type |
|---|---|
table | String |
name | String |
sql_type | String |
primary_key | Bool |
nullable | Bool |
plugin #
Values
pub fn gen_files_to_json(Array[GenFile]) -> @json.JsonSerialise a list of generated files into the plugin-reply JSON — the writer a MoonBit plugin uses on its stdout. Emits the {"files": [...]} envelope.
pub fn parse_gen_files(@json.Json) -> Array[GenFile]Parse a plugin's stdout into the files to write. Two shapes are accepted: a bare array [{path, content}, …], or an object {"files": [ … ]} (goctl-style envelope). An element missing path or content is skipped. The list preserves the plugin's order.
pub fn plugin_argv(String) -> (String, Array[String])Split a plugin invocation into the program to run and the arguments to run it with, the way goctl reads its -plugin value: the first whitespace-separated word is the executable, the rest are its argv. A quoted word keeps its spaces.
pub fn plugin_from_json(@json.Json) -> PluginRead the request a plugin was handed on stdin. Missing members degrade to empty rather than raising, so a hand-written request still parses.
pub fn plugin_plan(Array[GenFile]) -> (Array[GenFile], Array[String])What a plugin run will actually write: the files whose path is safe, and a note per file that was refused or superseded. A later file for a path an earlier one already claimed is refused rather than silently overwriting it — a plugin that emits the same file twice has a bug, and the report should say so rather than leave a coin-flip in the output.
pub fn plugin_request(@api.Spec, api_file_path? : String, style? : String, dir? : String) -> StringThe plugin request as a two-space-indented JSON string, ready to write to the plugin's stdin: goctl's four keys — the spec under Api, plus the file it was read from, the --style template and the output directory, which is everything a plugin needs to decide where its output goes and what to call it.
pub fn safe_path(String) -> BoolWhether a path a plugin supplies lands inside the directory mctl writes to. A plugin runs as its own process and its reply is arbitrary text, so the one thing mctl must not do is believe it: ../../.ssh/authorized_keys, /etc/passwd and C:/Windows/system32/… are all accepted by a naive writer, and all three write where a code generator has no business writing. Only a relative path with no .. segment is allowed; a path with one is refused and named in the run's report.
pub fn spec_from_json(@json.Json) -> @api.SpecReconstruct a Spec from the request JSON — the reader a plugin written in MoonBit uses on its stdin. The whole request ({Api, ApiFilePath, Style, Dir}) and a bare spec are both accepted, so a plugin that wants nothing but the routes need not unwrap the envelope. Missing or mistyped members degrade to empty rather than raising, so a partial request still parses.
pub fn spec_to_json(@api.Spec) -> @json.JsonSerialise a Spec into the plugin request JSON. The shape mirrors the Spec itself — service, routes (verb/path/handler/summary, plus the group the route was declared under), types (name plus fields of name/type) and groups — so a plugin in any language parses it with an ordinary JSON reader. type_ is written as type, the name a caller expects.
Types
pub(all) struct GenFile
One file a plugin asks mctl to write: a destination path (relative to the output directory) and its full content.
| Field | Type |
|---|---|
path | String |
content | String |
pub(all) struct Plugin
What a plugin is handed on stdin (← goctl's plugin.Plugin): the parsed spec under api, the .api file it was read from, the --style naming template, and the directory the generated tree goes under.
| Field | Type |
|---|---|
api | @api.Spec |
api_file_path | String |
style | String |
dir | String |
proto #
Values
pub fn commas(Array[String]) -> StringJoin parts with ", " — the codegen workhorse for column/argument lists.
pub fn default_routes(Proto) -> Array[GatewayRoute]Every RPC of proto as the default route: POST /<Service>/<Method> — the gRPC path with its package dropped, because a gateway in front of one package does not need the package repeated in the URL. A POST is the method gRPC itself uses, so the mapping is the one that always works.
pub fn gateway_file_name(String) -> StringThe file a gateway lands in: the .proto's own name, without its directory and without its extension — proto/greet.proto → greet_gateway.mbt. A .proto may declare several services, so the file name comes from the file rather than from one of them.
pub fn generate_gateway(Proto, Array[GatewayRoute]) -> StringGenerate the gateway for proto with routes: the route table, the gateway itself, and the two entry points — handle, which takes a JSON body already parsed, and handle_text, which parses the body a request carried.
Status codes are the four an HTTP client cares about: 200 for a reply, 400 for a body that is not JSON, 404 for a path no route answers, and 502 for a call the upstream failed. A gRPC status does not leak through: an HTTP client reads it where it is meaningful — in the reply body.
pub fn generate_grpc(Proto) -> StringGenerate a moonrpc service stub from a parsed .proto: a MoonBit struct per message, a @moonrpc.Method descriptor per RPC (its gRPC :path is /package.Service/Method), a <Service>Server handler-registration struct (one synchronous-core handler field per RPC, each returning its reply or a gRPC @moonrpc.Status), and a <service>_methods() listing. The output compiles against moonbitstack/moonrpc.
pub fn has_method(Proto, String) -> BoolWhether proto declares the gRPC method target (/package.Service/Method), so a hand-written route that names a method the spec does not have is caught here rather than at the first request.
pub fn parse_proto(String) -> ProtoParse a minimal .proto (proto3). Recognises the package declaration, service blocks with their rpc methods (including stream), message blocks with scalar/repeated/map fields, and top-level enum declarations. syntax, import, and option are skipped.
syntax = "proto3";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }pub fn parse_route_map(Proto, String) -> (Array[GatewayRoute], Array[String])The routes a mapping file declares, in the order it declares them. A line is VERB /path /package.Service/Method; # starts a comment, and a blank line is skipped. A line naming a method the .proto does not declare, or one that streams, is left out — skipped reports it so the caller can say which.
pub fn proto_reserved_conflicts(ProtoMessage) -> Array[String]The names of m's fields (oneof members included) that reuse a reserved field number or name — protobuf forbids reusing either, and this surfaces an accidental reuse. Empty when the message is clean.
Types
pub(all) struct GatewayRoute
One HTTP route and the gRPC method behind it: the verb and path the gateway answers, and the fully-qualified method it forwards to.
| Field | Type |
|---|---|
verb | String |
path | String |
rpc | String |
pub(all) enum Label derive(Eq, @debug.Debug)
Whether a field holds one value, a list, or a map with proto key and value types.
SingleRepeatedMapOf(key~ : String, value~ : String)
Methods
pub fn Label::equal(Self, Self) -> Boolpub fn Label::not_equal(Self, Self) -> Boolpub fn Label::to_repr(Self) -> @debug.Reprpub(all) struct Proto
A parsed .proto file: its package (empty when none) and the services and messages it declares.
| Field | Type |
|---|---|
package_ | String |
services | Array[ProtoService] |
messages | Array[ProtoMessage] |
enums | Array[ProtoEnum] |
pub(all) struct ProtoEnum
A protobuf enum declaration, generated into a MoonBit enum plus an integer mapping (proto3 enums are int-backed, and the value numbered 0 is the default).
| Field | Type |
|---|---|
name | String |
values | Array[ProtoEnumValue] |
pub(all) struct ProtoEnumValue
One value of a protobuf enum: its name and its integer number.
| Field | Type |
|---|---|
name | String |
number | Int32 |
pub(all) struct ProtoField
One field of a protobuf message: its name, its MoonBit type (repeated is Array[T], map<K, V> is Map[K, V]), its proto element type as written, its label and its wire number.
| Field | Type |
|---|---|
name | String |
type_ | String |
proto | String |
label | Label |
number | Int32 |
pub(all) struct ProtoMessage
A protobuf message declaration, generated into a MoonBit struct. oneof groups are lifted out of fields into oneofs; reserved field numbers and names are recorded so a reused one can be caught.
| Field | Type |
|---|---|
name | String |
fields | Array[ProtoField] |
oneofs | Array[ProtoOneof] |
reserved_numbers | Array[Int32] |
reserved_names | Array[String] |
pub(all) struct ProtoOneof
A protobuf oneof group: its name and the members (exactly one may be set). Generated into a MoonBit enum — the faithful equivalent of the "exactly one" invariant, which flattening to optional fields would lose.
| Field | Type |
|---|---|
name | String |
variants | Array[ProtoField] |
pub(all) struct ProtoService
A protobuf service declaration and its RPC methods.
| Field | Type |
|---|---|
name | String |
rpcs | Array[Rpc] |
pub(all) struct Rpc
One rpc method of a service: its name, request and response message names, and whether either side is a stream (client/server/bidi streaming).
| Field | Type |
|---|---|
name | String |
request | String |
response | String |
client_streaming | Bool |
server_streaming | Bool |
scaffold #
Values
pub fn generate_gql_tree(@api.Spec, style? : @style.Style, dir? : String) -> Array[TreeFile]The layered GraphQL tree for spec under dir: internal/schema with the annotated types and root fields (seeded once — the resolvers are yours to write), the rule that regenerates the schema from them, internal/config and the entry point serving it on 12000.
Hand the result to tree_plan to keep what the author has filled in.
pub fn generate_mcp_from_spec(@api.Spec, style? : @style.Style, dir? : String) -> Array[TreeFile]The MCP server tree for the service spec describes: the same module, packages and build rule as generate_mcp_tree, with the spec's routes as its tools.
pub fn generate_mcp_tree(String, style? : @style.Style, dir? : String, tools? : String, registration? : String, checks? : String) -> Array[TreeFile]The MCP server tree for module module_ under dir: the shared module, configuration and context, internal/tools with the annotated functions, their generated registration and a test, and the build rule that keeps the registration in step with the functions.
pub fn generate_orm_tree(@api.Spec, style? : @style.Style, dir? : String) -> Array[TreeFile]The annotated models and migrations for spec: every type block is a table, its id field (when it has one) the primary key, and a field with no single column — a slice, a map, a nested type — is skipped rather than stored.
pub fn generate_orm_tree_from_ddl(Array[@model.DdlTable], String, style? : @style.Style, dir? : String) -> Array[TreeFile]The annotated models, migrations and CRUD helpers for a SQL schema. The table and column names are the schema's; a nullable column is declared optional, so #orm reads it as one.
pub fn generate_rpc_tree(@proto.Proto, style? : @style.Style, dir? : String) -> Array[TreeFile]The layered gRPC tree goctl writes for a .proto, in MoonBit, under dir: pb/ with the messages, codecs and handler tables (rewritten on every run), internal/logic with one function per RPC and internal/config/internal/svc (seeded once), internal/server wiring the logic into each service, client/ with a typed client per service, and the entry point serving it all on 12000.
Hand the result to tree_plan to keep what the author has filled in.
pub fn generate_tree(@api.Spec, style? : @style.Style, dir? : String) -> Array[TreeFile]Generate the layered project tree goctl writes, in MoonBit: the service entry point and its moon.mod, etc/<service>.yaml, internal/config, internal/svc, internal/types, internal/handler (the generated routes plus a stub per handler, under internal/handler/<group>/ for a grouped route), internal/logic, internal/middleware for every middleware an @server block named, and a moon.pkg for each of those packages.
style (default gozero) names the files and the handler, logic and middleware entry points inside them. Names the spec itself chose — the type blocks and their fields — are left as written, since a MoonBit type name has to keep its capital. dir puts the whole tree under a directory.
Each file says whether a regeneration may overwrite it; hand the result to tree_plan to apply that.
pub fn generator_command(String, String) -> StringThe moonx command that runs one of a library's generators, for the rule(...) that wires it into moon.mod.
pub fn gql_dropped_fields(@api.Spec) -> Array[String]The fields of a type block that GraphQL has no type for, as <type>.<field> (<type>), for the command to report.
pub fn gql_expressible(String) -> BoolWhether moongql has a GraphQL type for ty. A slice or an optional is that of its element; Bytes and Map have none, and a field of one of those keeps the generated ToGql impl from compiling, so it is dropped from the schema and reported instead.
pub fn gql_schema_for(@api.Spec) -> StringThe seeded schema for spec: one annotated struct per type block, one root field per route.
pub fn mcp_checks_for(@api.Spec) -> StringThe test a spec-derived server is seeded with: every offered route registered as a tool, counted through tools/list.
pub fn mcp_registration_for(@api.Spec) -> Stringinternal/tools/tools.g.mbt: what moonmcp regenerates from those functions, so a fresh project compiles before its first build.
pub fn mcp_skipped(@api.Spec) -> Array[String]The routes a generated server does not offer, each as <verb> <path>: they declare no request type, so there is nothing for a tool to take.
pub fn mcp_tools_for(@api.Spec) -> Stringinternal/tools/tools.mbt for spec: one annotated function per offered route.
pub let pins : Map[String, String]The version of each library generated code imports.
These are the versions this repository's CI scaffolds a project against and builds, so a generated project is promised to compile with them. A project that wants a later release edits its own moon.mod.
pub let port : Int32The port a scaffolded service listens on.
12000, the one every server in this family defaults to: HTTP, WebSocket and h2c share it because one listener tells them apart, so a generated manifest and a generated configuration name the same number without anyone saying which they meant.
pub fn rule_line(String, String) -> StringThe rule(...) entry that wires a moonx generator into moon.mod, on one line when it fits in the width moon fmt breaks at and one argument per line when it does not — the two shapes the formatter leaves it in, so a generated moon.mod never changes on the first format.
pub fn scaffold_api(String) -> Array[@plugin.GenFile]Scaffold a moonapi service under <name>/: goctl's api new. The layered tree of generate_tree for a sample spec with GET /ping, a test calling it, and an entry point serving it on mooncat.
pub fn scaffold_docker(String, port? : Int32) -> Array[@plugin.GenFile]Scaffold container files for a native mctl-generated service: a Dockerfile (a MoonBit build stage that produces the native binary, then a slim runtime stage that runs it) and a .dockerignore. name is the binary/image name, port the port the service listens on. goctl's goctl docker.
pub fn scaffold_gql(String) -> Array[@plugin.GenFile]Scaffold a moongql service under <name>/: the annotated tree of generate_gql_tree for a sample spec — the types as #gql.type, the routes as root fields — and a test that asks the schema what it is.
pub fn scaffold_kube(String, port? : Int32, replicas? : Int32) -> Array[@plugin.GenFile]Scaffold a Kubernetes deployment for the service under deploy/: a Deployment (replicas pods of the <name> image, a container port, and a liveness probe on /ping) and a Service exposing it. goctl's goctl kube deploy.
The manifests are built as values and written by moonyaml, not joined out of string fragments. A generator that joins fragments has the indentation in its string literals, where a missed space is invisible, and quotes nothing — so the first service name with a colon in it emits a file Kubernetes reads as something else.
pub fn scaffold_mcp(String) -> Array[@plugin.GenFile]Scaffold an MCP server under <name>/: annotated tools, a resource and a prompt, a test calling the tool, and an entry point serving them over Streamable HTTP or, with --stdio, over stdio.
pub fn scaffold_model(String) -> Array[@plugin.GenFile]Scaffold a moonorm data-layer project under <name>/: a moon.mod carrying the orm rule, a sample schema.sql, the tables as #orm annotations under internal/model (through the same generate_orm_tree_from_ddl as gen crud), and a README. goctl's goctl model … new.
pub fn scaffold_rpc(String) -> Array[@plugin.GenFile]Scaffold a moonrpc service under <name>/: goctl's rpc new. The layered tree of generate_rpc_tree for a sample proto with a Ping that answers pong, and a test serving it and calling it through the typed client.
pub fn tree_plan(Array[TreeFile], (String) -> Bool) -> Array[@plugin.GenFile]The files of tree a run should actually write, given a way to ask whether a path is already on disk. A file moonctl owns (Always — the routes and the types) is always written; anything else is written only when it is not there yet, so a second run refreshes what follows the spec and leaves every handler and logic body the author has since filled in exactly as it is.
Types
pub(all) enum Regen derive(Eq)
Whether a regeneration may replace a file that is already on disk. Always is for what moonctl owns — the routes and the types, which must follow the spec — and Once for what it only seeds: handlers, logic, configuration, manifests.
AlwaysOnce
Methods
pub fn Regen::equal(Self, Self) -> Boolpub fn Regen::not_equal(Self, Self) -> Boolpub(all) struct TreeFile
One file of a generated project tree: where it goes, what is in it, and whether regenerating the tree is allowed to overwrite it.
| Field | Type |
|---|---|
path | String |
content | String |
regen | Regen |
style #
Errors
pub(all) suberror Refused
A --style template that is not <before>GO<through>ZERO<after>: either a marker is missing (go, zero), they are the wrong way round, or one is cased neither go/GO/Go nor zero/ZERO/Zero (gOZero, goZEro).
BadStyle(String)
Types
pub struct Style
A --style naming template (← goctl's --style), ready to spell names with. Build one with Style::parse, apply it with Style::format.
| Field | Type |
|---|---|
before | String |
through | String |
after | String |
head | Case |
tail | Case |
Methods
pub fn Style::format(Self, String) -> StringSpell name in this style. name is split into words on _ and before each capital, the first word takes the GO marker's case and the rest take ZERO's, and they are joined with whatever stood between the markers.
pub fn Style::gozero() -> Selfgoctl's default style, gozero: every word lower-cased and run together.
pub fn Style::parse(String) -> Self raise RefusedRead a --style template. It must contain GO and then ZERO — in any case, with anything before, between and after them:
Style::parse("gozero").format("welcome_to_go_zero") // welcometogozero
Style::parse("goZero").format("welcome_to_go_zero") // welcomeToGoZero
Style::parse("go_zero").format("welcome_to_go_zero") // welcome_to_go_zero
Style::parse("Go#zero").format("welcome_to_go_zero") // Welcome#to#go#zeroThe GO marker's own casing (go, GO or Go) says how the first word is spelled, ZERO's says how every later word is, and what stands between the two markers is what stands between the words. A template missing a marker (go, zero), holding them in the wrong order, or casing one of them any other way (gOZero, goZEro) raises Refused.
template #
Values
pub fn render(String, Value) -> String raise RefusedParse and render source against data in one call, with the default function library.
Errors
pub(all) suberror Refused
A template parse or execution failure, carrying a human-readable message.
ParseError(String)ExecError(String)
Types
pub struct Template
A parsed, reusable template — moonctl's text/template equivalent. Build it with Template::new, register extra functions with func, parse a source with parse, then render it against a Value any number of times. The parsed node tree stays private, captured inside renderer.
| Field | Type |
|---|---|
mut renderer | (Value) -> String raise Refused |
funcs | Map[String, (Array[Value]) -> Value raise Refused] |
Methods
pub fn Template::func(Self, String, (Array[Value]) -> Value raise Refused) -> SelfRegister (or override) a pipeline function, returning self for chaining — the analogue of Go's Template.Funcs. Register before parse.
pub fn Template::new() -> SelfA fresh template preloaded with the default function library. Rendering before parse yields the empty string.
pub fn Template::parse(Self, String) -> Self raise RefusedParse source into this template's node tree, returning self for chaining. Raises ParseError on a syntax error (unclosed action, dangling {{end}}, …).
pub fn Template::render(Self, Value) -> String raise RefusedRender the template against data, producing the output string. Raises ExecError on an execution fault (undefined variable/function, bad range target, …).
pub(all) enum Value
Dynamic template data. .Field lookups resolve against Dict; range iterates List (index/element) or Dict (sorted key/value). This is the explicit stand-in for the arbitrary Go values goctl reflects over.
NullBool(Bool)Int(Int64)Float(Double)Str(String)List(Array[Value])Dict(Map[String, Value])
Aliases
pub type FuncFn = (Array[Value]) -> Value raise RefusedA pipeline function: it receives the already-evaluated argument list (with the piped-in value, if any, appended last, mirroring Go) and returns a Value.