Fireargs is in technical previewShare feedback

Search

Search documentation

LLMs mode

Updated Oct 23, 2026

--llms prints a manifest describing every tool reachable through this program, in the shape of MCP's tools/list response. An LLM agent reads the manifest once and then calls back through --json to actually invoke a tool.

Enabled by default on every command and program. Disable per command or program with llms_option: FlagConfig::Disabled, or rename the flag and its help with FlagConfig::Custom { long, description }.

Manifest shape

{
  "tools": [
    {
      "name": "help",
      "description": "Documentation tool, not callable. Each `tools[].name` is the space-separated path beneath this binary. Invoke a tool with `<binary> <name> --json '<value>'` matching that tool's `inputSchema`; output is JSON on stdout matching `outputSchema`.",
      "inputSchema":  { "type": "object", "properties": {} },
      "outputSchema": { "type": "object", "properties": {} }
    },
    {
      "name": "greet",
      "description": "Greet someone politely",
      "inputSchema":  { "type": "object", "properties": { "name": { "type": "string", "description": "user's name" }, ... }, "required": ["name"], "$schema": "https://json-schema.org/draft/2020-12/schema" },
      "outputSchema": { "type": "object", "properties": { "greeting": { "type": "string" } }, "required": ["greeting"], "$schema": "https://json-schema.org/draft/2020-12/schema" }
    }
  ]
}

MCP requires a tool's outputSchema to describe an object. A command with a Vec or scalar output still gets a valid JSON Schema here, but an MCP client that validates strictly will reject it: return a struct from a CLI meant to be served over MCP.

The manifest types are exported as fireargs::Manifest and fireargs::Tool.

The reserved help tool

The first entry is always a help tool whose description carries the calling convention. MCP descriptions are first-class and always surfaced to the model, so this is the most reliable place to put guidance. Its schemas are empty so an accidental invocation is a no-op.

How it composes for programs

The same shape comes back from a Program's --llms. Each leaf in the tree becomes a tool whose name is the space-separated path:

myapp api greet --json '{"name":"world"}'

⇒ tools[] entry with "name": "api greet". Nested programs flatten the same way — myapp db migrate becomes "db migrate". A leaf mounted under two router keys appears once per key; aliases add no tools.

Why MCP shape

MCP is the standard for exposing tools to LLMs. Matching its tools/list shape means an MCP-aware client can consume a fireargs CLI's --llms output directly, and the JSON Schemas inside follow JSON Schema Draft 2020-12 — what every LLM tool-use framework (Anthropic SDK, OpenAI function calling, …) already understands.

What flows from schemars into the manifest

inputSchema is the input struct's JSON Schema as schemars generates it for deserialization; outputSchema is the output type's for serialization. Both are draft 2020-12, with subschemas inlined. Per property they include:

  • type — derived from the Rust type; Option<T> adds "null" to it.
  • description — from the field's doc comment.
  • default — from #[serde(default)] or #[serde(default = "...")].
  • enum — from a unit enum, after its rename_all.
  • format, pattern, minimum, maxLength, … — from the type (u32 gives format: uint32 and minimum: 0) and the field's schemars attributes.
  • required array — in inputSchema, fields without a default that are not Option<T>; in outputSchema, every field serde always writes.

inputSchema describes what --json accepts: a defaulted field is optional there, keys are serde's names (after rename_all), and a field with a parse shows its own type, since parse applies to CLI strings only. outputSchema describes the value the handler's output serializes to. The root schema's Rust type name (title) is dropped.

The fireargs meta (short flags, env vars, conflicts, …) is not part of the JSON Schema in the manifest — it's CLI-only and irrelevant to a JSON-mode call, so fireargs strips it.

Dates

With schemars' chrono04 feature, a chrono::DateTime is documented as a string with format: date-time and serde reads and writes it as RFC 3339, so a date round-trips through --json and an agent learns the wire format from the manifest. See Outputs for the other rich types.

Created with ❤ and Livemark