@magda/mgd
v6.1.0
Published
MAGDA local command-line interface
Downloads
428
Readme
@magda/mgd — MAGDA Command-Line Interface
mgd is the local command-line interface for the
MAGDA data catalog platform, developed as
part of epic #3648. It lets you
work with a MAGDA catalog from your terminal — or from an AI coding assistant —
without cloning the repository or running anything inside the cluster. With it
you can:
- authenticate with an API key and manage multiple site profiles;
- search the catalog by keyword or semantic similarity;
- inspect and download datasets, distributions and files (including large ones);
- create and edit dataset records, upload files, and manage custom aspects;
- call any MAGDA REST endpoint directly.
mgd is designed to be script- and agent-friendly: results go to stdout,
progress/diagnostics go to stderr, machine output is available via --json /
--jsonl, and it uses stable exit codes. It ships with an assistant "skill" so
coding agents such as Claude Code can drive it — see
Using mgd with a coding agent.
See also: How to create an API key.
Installation
Choose the installation path that suits you.
Install with a coding agent — easiest
Copy the prompt for your coding agent and paste it into a new task. The agent
will guide you through installing both the mgd CLI and its MAGDA skill.
Help me install and configure the MAGDA mgd CLI for use with Claude Code.
1. Detect my operating system and check `node --version` and `npm --version`.
mgd requires Node.js 22 or newer. If Node.js is missing or too old, explain
the recommended installation or upgrade and ask for my confirmation before
changing my system. Prefer my existing version manager or an official
Node.js distribution. Do not use sudo, change my shell configuration, or
execute a remote installation script without explicit approval.
2. Install the CLI with `npm install -g @magda/mgd`, then verify it with
`mgd --version`. If npm reports a permissions problem, explain it and
recommend a user-level Node.js/npm setup instead of escalating privileges.
3. Install only the Claude Code skill with
`mgd skills install --agent claude`. Tell me if I need to start a new Claude
Code session before the skill will be discovered.
4. When I am ready to configure MAGDA access, offer to open a new visible,
interactive terminal window and start `mgd profile create default`. Ask
before opening it. Launch only that command: do not automate keystrokes,
capture the terminal contents, or inspect credential entry. If you cannot
open a suitable terminal, give me the exact command to run myself.
5. Never ask me to paste an API key into chat and never put credentials in
command arguments, source files, or shell history. I will enter the site URL,
API-key ID, and masked API key directly in the interactive terminal. Empty
key fields should configure anonymous access.
6. After I confirm that profile setup finished, run `mgd auth status` and report
whether installation and connection succeeded. If any step fails, stop and
explain the failed command and the next action; do not claim completion.Help me install and configure the MAGDA mgd CLI for use with Codex.
1. Detect my operating system and check `node --version` and `npm --version`.
mgd requires Node.js 22 or newer. If Node.js is missing or too old, explain
the recommended installation or upgrade and ask for my confirmation before
changing my system. Prefer my existing version manager or an official
Node.js distribution. Do not use sudo, change my shell configuration, or
execute a remote installation script without explicit approval.
2. Install the CLI with `npm install -g @magda/mgd`, then verify it with
`mgd --version`. If npm reports a permissions problem, explain it and
recommend a user-level Node.js/npm setup instead of escalating privileges.
3. Install only the Codex skill with `mgd skills install --agent codex`. Tell me
if I need to start a new Codex session before the skill will be discovered.
4. When I am ready to configure MAGDA access, offer to open a new visible,
interactive terminal window and start `mgd profile create default`. Ask
before opening it. Launch only that command: do not automate keystrokes,
capture the terminal contents, or inspect credential entry. If you cannot
open a suitable terminal, give me the exact command to run myself.
5. Never ask me to paste an API key into chat and never put credentials in
command arguments, source files, or shell history. I will enter the site URL,
API-key ID, and masked API key directly in the interactive terminal. Empty
key fields should configure anonymous access.
6. After I confirm that profile setup finished, run `mgd auth status` and report
whether installation and connection succeeded. If any step fails, stop and
explain the failed command and the next action; do not claim completion.Help me install and configure the MAGDA mgd CLI for use with opencode.
1. Detect my operating system and check `node --version` and `npm --version`.
mgd requires Node.js 22 or newer. If Node.js is missing or too old, explain
the recommended installation or upgrade and ask for my confirmation before
changing my system. Prefer my existing version manager or an official
Node.js distribution. Do not use sudo, change my shell configuration, or
execute a remote installation script without explicit approval.
2. Install the CLI with `npm install -g @magda/mgd`, then verify it with
`mgd --version`. If npm reports a permissions problem, explain it and
recommend a user-level Node.js/npm setup instead of escalating privileges.
3. Install only the opencode skill with
`mgd skills install --agent opencode`. Tell me if I need to start a new
opencode session before the skill will be discovered.
4. When I am ready to configure MAGDA access, offer to open a new visible,
interactive terminal window and start `mgd profile create default`. Ask
before opening it. Launch only that command: do not automate keystrokes,
capture the terminal contents, or inspect credential entry. If you cannot
open a suitable terminal, give me the exact command to run myself.
5. Never ask me to paste an API key into chat and never put credentials in
command arguments, source files, or shell history. I will enter the site URL,
API-key ID, and masked API key directly in the interactive terminal. Empty
key fields should configure anonymous access.
6. After I confirm that profile setup finished, run `mgd auth status` and report
whether installation and connection succeeded. If any step fails, stop and
explain the failed command and the next action; do not claim completion.For skill installation locations, project-scoped installs, refreshes and removal, see Installing the skill.
Install manually
mgd requires Node.js ≥ 22. Install it globally so the mgd command is on
your PATH:
npm install -g @magda/mgd
mgd --versionOr run it without installing:
npx @magda/mgd <command>Global installation is recommended when using mgd with a coding agent because
the installed skill expects the command to remain available on your PATH.
The package is self-contained (one runtime dependency) and ships the assistant
skill files under skills/.
Quick start
# 1. Create a profile (prompts for site URL, API key ID and key; or pass them as flags)
mgd profile create default
# 2. Confirm who you are and where
mgd auth status
# 3. Search
mgd search datasets "climate temperature"
mgd search semantic "rainfall impact on crop yield" # if semantic search is available
# 4. Inspect and download
mgd dataset get magda-ds-<uuid>
mgd dataset distributions magda-ds-<uuid> --jsonl
mgd dist download magda-dist-<uuid> -o data.csvAuthentication and profiles
Setting up a profile
Create an API key in the MAGDA web UI first (Settings → API Keys), then create a profile:
mgd profile create default
# or non-interactively:
mgd profile create default --url https://dev.magda.io --key-id <id> --key <key>- The profile
<name>is required — there is no implicit target, so you can't accidentally set up the wrong profile. --urlaccepts either a site URL (https://dev.magda.io) or a full API base URL —mgdnormalises it by probing<url>first, then<url>/api, so non-standard gateway mounts work.- Any of
--url/--key-id/--keyyou don't pass is prompted for (the key is entered hidden). Credentials are verified against the API before saving. - Leave the API key ID empty to configure anonymous access (MAGDA supports anonymous read access to public data).
createrefuses to overwrite an existing profile — useprofile updateto change one, orprofile removeto delete it first.
Managing profiles
Credentials are stored per named profile in ~/.config/mgd/config.json
(respecting $XDG_CONFIG_HOME), with restrictive permissions (0600 file,
0700 directory). The default profile is named default. Every profile
subcommand except list takes a required positional <name>.
mgd profile create prod --url https://prod.example.com # create a new profile
mgd profile update prod --key-id <id> --key <key> # patch only the given fields
mgd profile list # list profiles
mgd profile use prod # switch the active profile
mgd profile remove prod # delete a profile
mgd auth status # who am I, whereprofile update <name>changes only the fields you pass (--url/--key-id/--key); it does not prompt, errors if you pass no field, and errors if the profile doesn't exist. Handy for rotating a key.profile remove <name>deletes the profile; if it was the active one, the active profile resets todefault.mgd auth loginis a deprecated alias ofprofile create(it keeps its--profile <name>flag, defaulting todefault) and prints a notice.mgd auth logouthas been removed — usemgd profile remove <name>.
mgd auth status reports the active profile, base URL and the identity returned
by the server; it shows anonymous when no credentials are configured.
The active profile is whichever mgd profile use <name> last selected (saved
as activeProfile in the config). For any command, the profile is resolved in
this order of precedence:
MGD_PROFILE(environment) — use this named profile for a single invocation, overriding the active profile;- the active profile set by
mgd profile use; - the profile literally named
default.
Independently of which profile is selected, MGD_BASE_URL / MGD_API_KEY_ID /
MGD_API_KEY override the corresponding fields of the resolved profile — and when
all three are set, no config file is read or written (see below).
Environment variables (CI / agents)
Environment variables override the stored profile and are ideal for CI or
ephemeral containers. When all three of MGD_BASE_URL, MGD_API_KEY_ID and
MGD_API_KEY are set, no config file is read or written:
| Variable | Purpose |
| ---------------- | ------------------------------------------- |
| MGD_BASE_URL | Site or API base URL |
| MGD_API_KEY_ID | API key ID |
| MGD_API_KEY | API key value |
| MGD_PROFILE | Select a stored profile by name |
| NO_COLOR | Disable ANSI colour (also via --no-color) |
export MGD_BASE_URL="https://dev.magda.io/api"
export MGD_API_KEY_ID="…"
export MGD_API_KEY="…"
mgd auth statusOutput modes, scripting and exit codes
- stdout carries the result; stderr carries progress, warnings and error
details. Suppress diagnostics with
2>/dev/null. --jsonprints one pretty JSON document.--jsonlprints one JSON object per line for list results — ideal for streaming intojq,grep,xargs, etc.- Read and list commands emit their data as JSON. The aspect/record mutations
(
aspect create,dataset aspect set/delete,dataset/dist update) print a compact{…, "ok": true}result in--jsonmode, whileaspect get/dataset aspect getemit JSON already (the flag is optional there). Downloads write the file itself and take no--json. - In
--json/--jsonlmode, errors are emitted to stderr as a structured object{"error":{"code","message","status","hint"}}so agents can parse failures.
# titles of the first 20 datasets matching a query
mgd search datasets "water quality" --limit 20 --jsonl | jq -r '.title'Exit codes
| Code | Meaning |
| ---- | -------------------------------------------------- |
| 0 | Success |
| 1 | General runtime / API error |
| 2 | Usage error (bad argument, flag or env var) |
| 3 | Authentication error (missing/invalid credentials) |
| 4 | Not found (record or object does not exist) |
Searching the catalog
MAGDA offers two complementary search modes:
# Keyword / faceted search — best for known terms, filters, pagination
mgd search datasets "rainfall" --limit 10 --offset 0
# Semantic (embedding) search — best for conceptual, natural-language queries
mgd search semantic "impact of drought on crop yield"Use keyword search when you know the vocabulary; use semantic search when the
wording differs from the dataset's metadata. Semantic search requires a
deployment with the semantic-search components installed — on sites without it,
mgd search semantic returns a clear semantic-search-unavailable error (a
non-zero exit) rather than failing silently, so you can fall back to keyword
search. A useful recipe is to run both with --jsonl and merge/de-duplicate by
dataset ID.
Inspecting datasets and distributions
mgd dataset get magda-ds-<uuid> # dataset record
mgd dataset get magda-ds-<uuid> --json # full record as JSON
mgd dataset get magda-ds-<uuid> --aspect <aspectId> # just one aspect
mgd dataset distributions magda-ds-<uuid> --jsonl # its distributions
mgd dist get magda-dist-<uuid> --json # a single distribution--json returns the complete record with every aspect attached to it,
including any custom aspects you have set (mgd aspect create + --aspect /
mgd dataset aspect set). Human-mode output shows the curated highlights only;
pass --json (and pipe through jq) to see everything.
Distribution records carry metadata under aspects such as
dcat-distribution-strings (title, format, downloadURL/accessURL). Use
--jsonl + jq to pull out just the fields you need.
Downloading data
mgd dist download magda-dist-<uuid> -o data.csv # to a file
mgd dist download magda-dist-<uuid> -o - | head # stream to stdout
mgd file download magda-datasets/<datasetId>/<distId>/<fileName> -o data.csv # by bucket/keymgd dist download resolves the distribution's downloadURL: MAGDA-hosted
magda://storage-api/… URLs are fetched through the gateway (with your
credentials); plain http(s):// URLs are fetched directly (no MAGDA auth headers
sent to third-party hosts). For large files, downloads use HTTP Range
requests and support --resume to continue an interrupted transfer:
mgd dist download magda-dist-<uuid> -o big.tif --resumeCreating and editing datasets
mgd reproduces the same end-state record operations as the web "Add Dataset"
wizard, so datasets you create or edit with the CLI remain fully editable in the
web UI. New datasets are created as drafts unless you pass --publish.
# 1. Create a draft dataset
mgd dataset create \
--title "River gauge readings 2025" \
--desc "Daily river height and flow measurements." \
--json # -> magda-ds-<uuid>
# 2. Upload a file and attach it as a distribution
mgd dataset add-file magda-ds-<uuid> ./readings.csv --title "2025 readings" --format csv
# 3. Register a link-only distribution (no upload)
mgd dataset add-file magda-ds-<uuid> --access-url https://example.org/data.csv --title "Source data"
# 4. Update metadata
mgd dataset update magda-ds-<uuid> --title "River gauge readings (2025, v2)"
# 5. Replace the file behind a distribution (adds a new version entry)
mgd dist replace-file magda-dist-<uuid> ./readings-corrected.csv
# 6. Publish — cascades to the dataset's distributions (--without-distributions to opt out)
mgd dataset publish magda-ds-<uuid> # unpublish the same way
mgd dist publish magda-dist-<uuid> # or flip a single distributionDatasets created by the CLI are stamped with a source aspect
({"id":"magda","name":"Magda CLI (mgd)","type":"internal","url":<site>}) — the
same id the web client uses, so they show up as editable records in the web
metadata tools, with a distinct name marking their CLI provenance.
Publishing is a deliberate step. Create as a draft, review, then publish explicitly.
add-file/replace-fileare multi-step operations; if a step fails after the upload,mgdrolls back the uploaded object where it can and prints the exact commands to finish or undo — it never leaves silent partial state.
Versioning. The CLI maintains the application-managed
versionaspect automatically:dataset create/add-fileseed and tag the initial version, anddataset update,dist update,dist replace-file,add-fileanddist removebump the changed record's version (tagged with the registry event id, so past versions work with the registry's time-travel API).publish/unpublishdon't bump versions (lifecycle state, not content). Don't hand-edit theversionaspect;dataset aspect set/aspect patch/aspect deleteare the manual escape hatch and never auto-bump.
Common aspects
Records carry data in named aspects. The ones the CLI reads or writes most:
| Aspect | Record | Holds / notes |
| --- | --- | --- |
| dcat-dataset-strings | dataset | title, description, keywords, themes, languages, issued/modified |
| dcat-distribution-strings | distribution | per-file title, format, downloadURL, accessURL, byteSize |
| publishing | dataset & distribution | { "state": "draft" \| "published" } — use publish/unpublish, don't hand-edit |
| dataset-distributions | dataset | { "distributions": [<distId>, …] } — CLI-managed by add-file/dist remove; don't hand-edit |
| version | dataset & distribution | CLI-managed history — don't hand-edit |
| access-control | dataset | ownerId, orgUnitId, constraintExemption |
| source | dataset | provenance (id: "magda", name: "Magda CLI (mgd)", type, url) |
| dataset-publisher | dataset | { "publisher": "<organisation record id>" } — publishing org shown in the web UI; the CLI does not set it yet (#3715) |
| temporal-coverage | dataset | optional — { "intervals": [{ "start", "end" }] }; set manually when the data spans a time range |
| spatial-coverage | dataset | optional — bounding box / named region; set manually when the data has a spatial extent |
Custom aspects
The registry supports user-defined aspects with JSON-schema validation. mgd
exposes them (the web UI does not):
# aspect definitions
mgd aspect list
mgd aspect get <aspectId>
mgd aspect create <aspectId> --name "My Aspect" --schema @schema.json
mgd aspect delete <aspectId> # only if no record data references it
# aspect data on any record (dataset or distribution)
mgd dataset aspect get magda-ds-<uuid> <aspectId>
mgd dataset aspect set magda-ds-<uuid> <aspectId> @data.json # full replace: inline JSON, @file, or - for stdin
mgd dataset aspect patch magda-ds-<uuid> <aspectId> '{"keywords":["a","b"]}' # partial: merge these fields, keep the rest
mgd dataset aspect delete magda-ds-<uuid> <aspectId>Use set to replace the whole aspect and patch to change only the
supplied fields (a server-side deep merge via the registry's ?merge=true, so
untouched fields survive). For advanced RFC 6902 operations (remove/test/move),
call the raw endpoint: mgd api request PATCH /v0/registry/records/<id>/aspects/<aspectId> --body @patch.json.
Every mutation command also accepts repeatable --aspect <id>=<json|@file|-> to
attach custom aspect data at create/update time.
Uploading files directly to storage
mgd file upload ./big.tif --bucket magda-datasets --key path/to/big.tif --record-id magda-ds-<uuid>Files at or above 16 MB automatically use resumable multipart upload (one
bounded part at a time, constant memory); smaller files use a single request.
Multipart needs a deployment that exposes the multipart storage endpoints
(/v0/storage/multipart/{initiate,part,complete,abort}); older deployments fall
back to single-shot upload automatically (--single-shot forces it). Pass
--record-id <datasetId> so record-linked storage authorization applies.
Raw API access
For endpoints without a curated command, call the REST API directly — with your credentials and structured error handling:
mgd api request GET /v0/registry/records/<id> --query aspect=dcat-dataset-strings --json
mgd api request POST /v0/registry/records --body-file record.json
echo '{"active":true}' | mgd api request PUT /v0/some/endpoint --body -Command reference
| Command | Description |
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| profile create <name> / update <name> / remove <name> | Create / patch / delete a site profile |
| profile list / profile use <name> | List / switch profiles |
| auth status | Show the active profile and authenticated user |
| auth login | Deprecated alias of profile create (--profile) |
| search datasets <query> | Keyword dataset search |
| search semantic <query> | Semantic (embedding) search |
| dataset get <id> | Fetch a dataset record |
| dataset distributions <id> | List a dataset's distributions |
| dataset create | Create a dataset (draft by default) |
| dataset update <id> | Update dataset metadata |
| dataset add-file <id> [file] | Upload/attach a distribution (or --access-url) |
| dataset aspect get/set/patch/delete <recordId> <aspectId> | Read/write custom aspect data on any record (set=replace, patch=merge) |
| dist get <id> / dist update <id> | Inspect / edit a distribution |
| dist download <id> | Download a distribution's file (--resume) |
| dist replace-file <id> <file> | Replace a distribution's file, bump version |
| dist remove <id> | Remove a distribution from its dataset (deletes record + stored files; --keep-files to keep them) |
| file upload <file> / file download <bucket/key> | Direct storage transfer |
| aspect list / get <id> / create <id> / delete <id> | Manage aspect definitions |
| api request <method> <path> | Call any MAGDA API endpoint |
| skills install [--agent <name>] | Install the agent skill (all agents, global by default) |
| skills uninstall [--agent <name>] | Remove the agent skill |
Run mgd --help or mgd <command> --help for full option documentation.
Using mgd with a coding agent
mgd ships with a tool-agnostic assistant skill so an LLM coding agent can
drive the catalog safely. The skills/ folder contains:
mgd-workflows.md— command usage, output modes, search strategy, recipes, the create → enrich → add-file → publish workflow, a common-aspects reference, safety rules and error triage;dataset-elicitation.md— "metadata consultant" behaviour for guided dataset creation (infer before asking, quick vs guided path, confirm-then-write);SKILL.md— the standard Agent-Skill wrapper (name+descriptionfrontmatter pointing at the two files above); auto-discovered by Claude Code, Codex and opencode.
Installing the skill
Install it with the built-in installer. By default it installs for all three agents, globally, so the skill is available from any working directory:
mgd skills installThis drops the skill into each agent's native skills directory:
| Agent | Global (default) | With --project [dir] |
| ----------- | -------------------------------------- | ----------------------------------- |
| Claude Code | ~/.claude/skills/magda-mgd/ | <dir>/.claude/skills/magda-mgd/ |
| Codex | ~/.codex/skills/magda-mgd/ | <dir>/.agents/skills/magda-mgd/ |
| opencode | ~/.config/opencode/skills/magda-mgd/ | <dir>/.opencode/skills/magda-mgd/ |
All three natively auto-discover SKILL.md skills and load the instructions on
demand when a task matches the skill's description.
mgd skills install --agent codex # just one agent
mgd skills install --project # into ./ (current repo) instead of globally
mgd skills install --project ../app # into a specific project directory
mgd skills uninstall # remove (all agents, global) — --agent/--project mirror installInstalls are idempotent — re-running refreshes the managed magda-mgd/ folder.
Make sure mgd is on the agent's PATH and that credentials are configured (a
saved profile, or the MGD_* environment variables; check with mgd auth status) before starting the agent.
Troubleshooting
Authentication error(exit 3) — no or invalid credentials. Recreate the profile withmgd profile create <name>(or update it withmgd profile update <name> --key-id … --key …), or checkMGD_API_KEY_ID/MGD_API_KEY.semantic-search-unavailable(exit 1) — the target site doesn't have the semantic-search components installed; usemgd search datasetsinstead.Not found(exit 4) — the record or storage object doesn't exist (or your key can't see it).- A large upload is rejected — the deployment may lack the multipart storage endpoints and hit an ingress body-size limit on single-shot upload; upload against a deployment with multipart support.
- Parsing output in scripts — always pass
--json/--jsonland rely on the documented exit codes rather than scraping human-readable text.
Development
This package lives in the packages/mgd/ subdirectory of the Magda monorepo.
yarn build # TypeScript → ESM via esbuild, output to bin/
yarn test # unit tests (Mocha + tsx)
yarn typecheck # type-check without emittingNode.js ≥ 22 is required (set in engines in package.json).
License
Apache 2.0
