* Plan: flesh out PR7 (MCP server + Agent Skill) design stdio transport (no HTTP port), mlr mcp terminal in the main binary, SDK-vs-handroll decision, tool list, in-process vs subprocess split, run-tool safety (--no-shell prerequisite), single-sourced skill, tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add mlr mcp: MCP server + agent playbook; --no-shell gate (#2098 PR7) New terminal `mlr mcp` runs a Model Context Protocol server over stdio (spawned by MCP clients; no network port), exposing five tools -- list_capabilities, which, validate_dsl, describe_data, run -- plus an agent playbook as MCP prompt/resource. Catalog tools are served in-process from the help registries; the rest subprocess this same binary with MLR_ERRORS_JSON=1, a timeout, and an output cap. Prerequisite: a new --no-shell flag / MLR_NO_SHELL env var (one-way gate) disables the DSL system/exec functions, piped redirects, and --prepipe/--prepipex; the MCP server sets it on the commands it runs unless started with --allow-shell. Adds the github.com/modelcontextprotocol/go-sdk dependency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Force LF checkout for the embedded SKILL.md (Windows CI fix) go:embed embeds checkout bytes, so a CRLF checkout on Windows made the embedded playbook differ per platform and failed TestPlaybookHasFrontmatter. Pin the file to eol=lf in .gitattributes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Move no-shell test DSL into per-case mlr files (Windows CI fix) Inline single-quoted DSL in cmd files is mangled by the Windows shell (single quotes are not quote characters there); the harness's put -f ${CASEDIR}/mlr pattern avoids shell quoting entirely. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
3.8 KiB
| name | description |
|---|---|
| miller | Drive Miller (mlr) to process CSV/TSV/JSON/etc. data. Use when constructing mlr command lines: discover capabilities from the catalog rather than guessing, learn the data's shape before writing expressions, validate DSL before running, and recover from failures via structured errors. |
Miller agent playbook
Miller (mlr) is a command-line data processor for CSV, TSV, JSON, JSON
Lines, and other tabular/record formats, with SQL-like verbs (cut, sort,
join, stats1, ...) and an awk-like DSL (put, filter).
Work this loop. Each step exists to prevent a specific, common failure.
1. Discover — never invent names
Everything valid is in the catalog; anything not in the catalog does not exist. Hallucinated flag/function names are the top failure mode.
- Route an intent:
whichwith e.g."join two files on a key"→ ranked candidates.confident: truemeans a name matched; trust the top hit. - Browse cheaply:
list_capabilitieswithindex: true→ every verb/function/flag/keyword with one-line summaries. - Drill in:
list_capabilitieswithkind: "verb", names: ["join"]→ the full entry. Prefer the structuredoptionslist (flag, arg, type, enumvalues) when present;usage_textis the prose fallback. - The whole catalog is cacheable against
(mlr_version, catalog_schema_version)— re-fetch only when either changes.
2. Constrain — learn the data before touching it
Call describe_data on the input first. It returns, per field: name, types
seen with counts, occurrence count, null count, cardinality, min/max, and —
for low-cardinality fields — every distinct value.
- Copy field names exactly from
describe_data; never guess casing or spelling. - For flags like
-g(group-by) and DSL comparisons, use values from thevaluesarray, not values you expect to exist. - Fields whose
countis less than other fields' are absent in some records: guard DSL withis_present($field).
3. Validate — check DSL before spending a run
Before any run that includes put or filter, call validate_dsl with the
expression. Cost: parse-only, no data read. On valid: false, the error
document has kind, hint, and did_you_mean — apply the hint, don't
re-guess syntax.
4. Run — and read errors structurally
Call run with argv as a list, one element per shell word (no shell quoting):
{"args": ["--icsv", "--ojson", "cat", "data.csv"]}
Command-line shape rules that prevent most argv errors:
- Main flags (I/O formats etc.) come before the verb:
mlr --icsv sort -f name f.csv. - Format shorthands:
--icsv --ojson(separate in/out),--csv/--c2jetc. (combined). - Chain verbs with
then:["--icsv", "sort", "-f", "k", "then", "head", "-n", "3", "f.csv"]. - If a field value being compared in
filtermight collide with a verb flag, end verb flags with--before filenames. - Inline data goes in
stdin_text; files go at the end ofargs.
On failure, exit_code is nonzero and error (when present) carries kind,
hint, and did_you_mean — hint is often a corrected command line; prefer
executing it over reasoning from the message. stdout_truncated: true means
the output exceeded the server's cap: narrow the query (e.g. head, cut)
rather than re-running the same command.
Notes
runcannot execute external commands (DSLsystem/exec, piped redirects,--prepipe) unless the server was started with--allow-shell; such calls fail cleanly. It can write files viatee,split, and DSL output redirects — treat it as a write-capable tool.- Long inputs: prefer
describe_data+ targeted verbs over dumping whole files throughrun. - One record format in, another out: Miller is format-to-format; there is no separate conversion step.