Summary
Every func subcommand should support a --json flag that emits structured output suitable for machine consumption. Errors must also be returned as JSON when --json is set, not as human-formatted strings.
Motivation
Today the only structured signal callers (MCP server, scripts, IDEs, CI, future Console plugin) can extract from func is the exit code plus stdout/stderr text. This forces every consumer to either substring-match output (brittle — see #3753 for a worked example of why this fails) or invent its own taxonomy by guessing at func's intent. The result is a contract that's secretly the consumer's job to maintain, even though the producer (func) is the only code that actually knows what happened.
Putting structured output behind a --json flag fixes this for every consumer at once. Same pattern as kubectl -o json, gh --json, aws --output json — users already expect it from modern CLIs.
Scope
- Every existing
func subcommand accepts --json.
- On success: command-specific structured payload (e.g.,
func list --json returns the function list; func describe --json returns the deployed-function spec; func deploy --json returns the resulting service URL/image/etc).
- On failure: a structured error object (this is the part most consumers actually need), with at minimum:
category (e.g. REGISTRY_ERROR, CLUSTER_ERROR, BUILD_ERROR, VALIDATION_ERROR, AUTH_ERROR, READONLY_ERROR, TIMEOUT_ERROR, UNKNOWN_ERROR)
code (stable string identifier; finer-grained than category)
retryable / transient boolean
message (human-readable summary)
hint (suggested remediation)
context (resource name, namespace, registry, etc. — whatever's relevant to the failure)
- Default output (no flag) stays exactly as it is.
--json is opt-in.
Open design questions (decide before implementing)
- Envelope shape. Suggested:
{"status": "ok"|"error", "data": ..., "error": {...}} so callers can dispatch with one key.
- Stream choice. All JSON to stdout (recommended —
status carries success/failure, no need to merge streams) vs error JSON to stderr.
- Streaming commands (
func logs, func deploy --verbose): pick NDJSON (one event per line) or explicitly mark --json unsupported for these. "Explicit and unsupported" beats "clever and surprising."
- Schema versioning. Either an
apiVersion field in the envelope, or document that v1 evolves additively only and breaking changes require --json=v2. Pin early.
- Internal refactor. Errors emitted from underlying code paths need to carry enough context to be rendered as JSON. Typed errors at the failure site, translated at the CLI boundary.
Related
Non-goals
- Localization of error messages (separate concern).
- Replacing the existing human-readable output (this is additive).
Summary
Every
funcsubcommand should support a--jsonflag that emits structured output suitable for machine consumption. Errors must also be returned as JSON when--jsonis set, not as human-formatted strings.Motivation
Today the only structured signal callers (MCP server, scripts, IDEs, CI, future Console plugin) can extract from
funcis the exit code plus stdout/stderr text. This forces every consumer to either substring-match output (brittle — see #3753 for a worked example of why this fails) or invent its own taxonomy by guessing atfunc's intent. The result is a contract that's secretly the consumer's job to maintain, even though the producer (func) is the only code that actually knows what happened.Putting structured output behind a
--jsonflag fixes this for every consumer at once. Same pattern askubectl -o json,gh --json,aws --output json— users already expect it from modern CLIs.Scope
funcsubcommand accepts--json.func list --jsonreturns the function list;func describe --jsonreturns the deployed-function spec;func deploy --jsonreturns the resulting service URL/image/etc).category(e.g.REGISTRY_ERROR,CLUSTER_ERROR,BUILD_ERROR,VALIDATION_ERROR,AUTH_ERROR,READONLY_ERROR,TIMEOUT_ERROR,UNKNOWN_ERROR)code(stable string identifier; finer-grained than category)retryable/transientbooleanmessage(human-readable summary)hint(suggested remediation)context(resource name, namespace, registry, etc. — whatever's relevant to the failure)--jsonis opt-in.Open design questions (decide before implementing)
{"status": "ok"|"error", "data": ..., "error": {...}}so callers can dispatch with one key.statuscarries success/failure, no need to merge streams) vs error JSON to stderr.func logs,func deploy --verbose): pick NDJSON (one event per line) or explicitly mark--jsonunsupported for these. "Explicit and unsupported" beats "clever and surprising."apiVersionfield in the envelope, or document that v1 evolves additively only and breaking changes require--json=v2. Pin early.Related
func, not in MCP.Non-goals