Skip to content

Arguments and output

Kevin Straub edited this page Aug 5, 2026 · 5 revisions

Passing arguments

httpie-style, because typing JSON at a shell prompt is a punishment.

Form Result
key=value Scalar. Plain ints, floats and true/false are coerced; everything else stays a string.
key:=<json> The value is parsed as JSON — arrays, objects, explicit types.
--args '<json>' Merges a whole object on top. Wins on key conflicts.
--args - Reads that object from stdin.
--args @file.json Reads it from a file.
mduct call tracker create_issue project=42 title="it broke again" draft=false
mduct call tracker list_issues labels:='["bug","p1"]' limit:=20
mduct call tracker create_issue --args '{"fields":{"nested":{"deep":true}}}'
jq -n '{title:"from a pipe"}' | mduct call tracker create_issue --args -

Identifiers survive intact on purpose. id=00123 stays "00123" because of the leading zero, and an integer too large to represent exactly stays a string as well. Only unambiguous, safe numbers are coerced. Nothing quietly mangles an id.

Bad argument forms fail before anything is sent:

bad argument "title" — use key=value, key:=<json>, or --args '<json>'

Getting output back

Flag Effect
(none) The text content of the result, as the server meant it to be read.
--json Only the JSON payload, prose stripped. This is the pipe-ready form.
--raw The full MCP envelope, for when a result looks wrong and you need to see why.
--compact Minify. Also settable as a default: mduct config compact on.
--full Bypass the oversized-list guard when you really do want the whole thing.
--timeout <s> Per-call timeout. The queue slot is not cancelled — the call finishes, you just stop waiting.

--json matters more than it looks. Several servers wrap their payload in conversational text ("Found 20 issues:"), and | jq then fails on output that is otherwise perfectly good JSON. Stripping it is the difference between a pipeline and a copy-paste.

mduct call tracker list_issues limit=20 --json | jq -c '.issues|map({id,title,status})'

This page is about call. --json on status and servers is a different thing with the same name: not payload stripping, but mduct's own state as a documented object — see machine-readable state.

The output contract

  • stdout is the payload, and only the payload. Pipe it.
  • stderr is for humans and for failures. Every error names the next action: unknown tool "x" — see: mduct tools gitlab.
  • exit code is 0 on success, non-zero on failure. set -e behaves.
  • binary content (screenshots, files) is written under $XDG_RUNTIME_DIR/media/ (falling back to ~/.cache/mduct/media/) and the path printed on stdout, because a PNG in a terminal helps nobody. Directories older than an hour are swept on the next call.

The oversized-list guard

A call whose result exceeds defaults.warnAbove characters prints a warning suggesting a jq projection instead of the dump:

⚠ 17 items, ~48 KB — too big for context. Slim it, don't dump it:
  project fields:  mduct call tracker list_issues … --json | jq -c '.issues|map({id,title})'

--json bypasses it (you are already slimming), --full overrides it, and mduct config warnAbove off turns it off entirely.

The failure it prevents is a silent one. Nothing errors when a call spends 40k characters of a context window. You get worse answers later and never find out why.

Exploring a server you don't know

mduct servers                  # what exists, and whether it's connected
mduct tools tracker            # names + signatures, no schemas — cheap
mduct tools kubectl            # for a CLI tool: its own help, through the configured wrapper
mduct schema tracker create_issue   # one full schema, when you need the fields
mduct call tracker some_tool --raw  # the envelope, when the result surprises you

mduct tools gives you create_issue(project, title, labels?, draft?). That is enough to make the call, without the several kilobytes of JSON Schema behind it.

Clone this wiki locally