6.13. The interface as data
-h is written for a person. --json-help is the same information written
for a program: a complete, machine-readable description of every command, its
arguments, their types, and what it returns.
$ ./sift --json-help
The top of the document records the compiler version, names the program, and lists its command groups:
{
...
"program": {
"name": "sift",
"description": [
"Search notes and count what turns up"
]
},
"groups": []
}
Each command follows. summarize is the simplest one in sift:
{
"name": "summarize",
"kind": "remote",
"group": null,
"description": [
"Count the hits in each file"
],
"arguments": [
{
"name": "arg0",
"role": "positional",
"position": 0,
"metavar": null,
"required": true,
"variadic": false,
"stdin": false,
"quoted": false,
"default": null,
"description": [
"Hits produced by an earlier search"
],
"type": {
"morloc": "[Hit]",
"wire": "am34paths4linej4texts",
"structure": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": { "type": "string" },
"line": { "type": "integer" },
"text": { "type": "string" }
},
"required": ["path", "line", "text"],
"additionalProperties": false
}
}
},
"named_type_kind": null,
"input": {
"source": "auto",
"form": "auto",
"checks": [],
"list_source": "inline",
"list_form": "auto",
"list_checks": [],
"format": null
}
}
],
"return": {
"description": [],
"streaming": false,
"type": {
"morloc": "[(Str, Int)]",
"wire": "at2sj",
"structure": {
"type": "array",
"items": {
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "integer" }
],
"minItems": 2,
"maxItems": 2
}
}
}
},
"terminals": []
}
Every type appears three ways, because three different readers want it:
morloc is the type as written, wire is the serialization schema, and
structure is JSON Schema, which a validator or a form generator can consume
directly. The wire schema is the general one: the concrete schema the
runtime dispatches on names the container the pool’s language builds, which
would make the published contract move whenever the implementation language
did. The input block carries the shape directives of Input shape,
so a caller can tell that an argument wants a path rather than a value without
parsing prose.
return.streaming says whether the command writes a stream to standard output
rather than returning a value; when it does, return.type describes the batch
that reaches stdout rather than the () the function returns.
terminals lists a command’s output actions, each with the type its flag puts
on the wire. scan has two:
[
{
"short": "c",
"long": "count",
"description": "Report the number of matches instead of the matches",
"render": false,
"default": false,
"type": {
"morloc": "U64",
"wire": "u8",
"structure": {
"type": "integer"
}
}
},
{
"short": "p",
"long": "plain",
"description": "Print one `path:line:text` record per line",
"render": true,
"default": false,
"type": {
"morloc": "Str",
"wire": "s",
"structure": {
"type": "string"
}
}
}
]
None of this is written by hand or kept in a sidecar file. It is derived from the same types and docstrings as the help text, on the same build, which is what makes it worth trusting: a description that can go stale is a description you have to verify, and this one cannot. Point a script at a directory of Morloc programs and you can build an accurate inventory of every command in it, with argument types, without knowing anything about any of them.
6.13.1. Other views of the same commands
Two more flags render the same information for model clients:
| Flag | Output |
|---|---|
|
An MCP |
|
A client MCP server config — an |
Commands whose types cannot cross the MCP boundary are excluded, and the reason is printed on standard error rather than left for you to discover:
$ ./sift --mcp-tools > tools.json
morloc mcp: excluding command 'total' from the tool surface (reads from @stdin)
The MCP surface is covered in Model Context Protocol (MCP), and the same module served over HTTP, TCP, and Unix sockets in Building API interfaces. They are worth reading together with this section, because they are the same point from three directions: the command line is one view of a typed library, not the thing the library is built on. A CLI, an HTTP endpoint, and an MCP tool are three renderings of one set of functions, and none of them is written by hand.