Repository navigation
Breaking
-
One command tree, no aliases (clean break). Nothing of the pre-0.3 surface was on PyPI, so
these are removed rather than deprecated:Removed Use instead mdf setup,mdf manifest init,mdf config manifest initmdf init [PATH]mdf validate,mdf publish --preflight-onlymdf checkmdf status --authmdf whoamimdf config doctormdf doctormdf dataset cite,mdf dataset versionsmdf cite,mdf versionsmdf dataset preview [--sample]mdf show ID --files/--samplemdf config manifest discover|inspectmdf manifest discover|inspectmdf list --latest-onlythe default now; mdf list --allshows everythingmdf search --type(datasets only) hidden top-level aliases: init,validate,whoami,watch,doctor,cite,open,preview,versions,diff,edit,withdraw,resubmit,pending,approve,reject,delete,stats,updatemdf <command>/mdf dataset …/mdf admin …/mdf publish --update/mdf status --watchhidden mdf backend …andmdf stream …groups— -
SDK removals:
mdf.skill(handlers,mdf_tools,skill.md; the read-only MCP server is
the agent interface),MDFAgent.stream_*, and theBackendClient.stream_*methods (including
stream_clone, which sent the API token to any*materialsdatafacility.orghost). -
mdf search --json --briefreturns{"success": true, "total", "offset", "results": [...]}with
source_idandversionper hit, instead of a bare list. -
mdf listshows only the latest version of each dataset and hides deleted/withdrawn records
unless--all(or--status) is given. -
mdf doctorexits non-zero when a check fails (5service,4login,2manifest). -
MCP tool errors now carry
"success": falsebesideerror, the same envelope as the CLI; the
error vocabulary moved tomdf.core.errors(still re-exported bymdf.cli.formatting). -
Exit codes: every failure now exits non-zero, in both
--jsonand human mode:
1unknown,2usage/validation,3not found,4auth/forbidden,5network/5xx/429.
Scripts that only checked for0/1should switch on these values. -
--jsonfailures print one envelope:{"success": false, "error": {"kind", "code", "message", "hint"}}.
Success payloads keep their shapes and now always include"success": true. -
mdf admin approve|reject|delete|rebuild-embeddingsunder--jsonrequire--yes(no prompts on stdout). -
Stream commands and the five
mdf_stream_*agent tools are removed from this release (the backend
router is unmounted); the handlers return afeature_unavailableenvelope. -
dataset_doiinmdf.yamlis no longer sent asexternal_doi. Declareexternal_sourcefor
mirrored datasets; userelated_worksfor a DOI of a related record. -
No login prompts outside a terminal. A command that needs a login now exits
4with the
authenvelope (hintmdf login) instead of prompting when--jsonis set or stdin/stdout is
not a TTY. Login prompts and the Globus URL go to stderr, never stdout. Under--json, bad
flags or values (click usage errors) also print the envelope (exit2). -
mdf show --jsonemits the API response flat —{"success", "card", "permissions"[, "citation"]}
— instead of nesting it under a secondcardkey. -
mdf publish --updatesends the dataset identity as the top-levelsource_idfield, not
extensions.mdf_source_id(deprecated server-side), and starts from the dataset's full current
metadata. A plainmdf publish --submiton anmdf.yamlthat already records asource_idis
refused (exit 2) instead of creating a duplicate dataset. An update uploads only data paths
given on the command line (data_sourcesinmdf.yamlare ignored for updates). -
mdf cite -frejects unknown formats (exit 2) instead of falling back to APA. -
mdf initnever asks for or storesdefaults.service;mdf mcp installaddsMDF_SERVICE
to the snippet only with--service. Without a terminal,mdf initfails fast (exit 2) when
--title/--authorare missing instead of waiting on a prompt. -
mdf clone --planreads the dataset card only; listing every file needs--files. Failed or
timed-out file listings are errors (mdf.core.agent.CloneListingError), not warnings, and a
directory listing without a Globus Transfer login raisesAuthRequired. -
SDK: core modules no longer
print().mdf.auth.globus.logout()returns quietly; clone/upload
warnings go throughlogging(loggermdf.core.agent).
Changed
mdf.cli.mainis now only the assembly point: the app and its login policy live in
mdf.cli.app, commands inmdf.cli.commands.{auth,status,publish,clone,search,list,show,import_}
(themdfentry point is unchanged).mdf.core.agent._resolve_globus_files(data_sources, transfer_token)keeps its signature but gains
keyword-onlyon_progress=andtimeout=(default 300 s), and raises instead of warning when a
listing fails, times out, or has no Transfer token.
Security
- A cached Globus login (with its data, transfer and groups tokens) is sent only to the known MDF
services and loopback hosts. For any other--api-url/MDF_API_URLhost, pass--token,
save a token for that URL, or setMDF_TRUST_API_URL=1; otherwise the command stops with the
autherror (exit 4). Public reads fall back to anonymous. - Saved tokens are keyed by normalized API URL (scheme/host case-insensitive, default ports and a
trailing slash ignored, path case kept). A symlinked token file is never read or written;
mdf logoutremoves the link. - The Globus data token goes only to the MDF HTTPS host on its default port.
mdf whoamireports the credential from the same resolution the requests use (env token →
client credentials → dev user → cached Globus login → saved token).mdf clonesends the Globus data token only to the MDF HTTPS host; non-httpsdata sources are
rejected instead of downloaded. Redirects are followed with credentials stripped across hosts.
Added
mdf search --offset --sort --year --organization --author --keyword --domain; the query is optional
(browse mode). Facets are printed in human mode.mdf listpages server-side (--limit,--status), reportsnext_key, and never skips records.mdf login --no-browserandMDF_NO_BROWSER=1; the login URL is always printed before any browser opens.mdf dataset resubmit --yes;mdf resubmit --yes.- SDK:
mdf.auth.globus.login_required_for(scopes);MDFAgent.clonereturns all transfertask_ids
andpartial; anonymous-first clone for public datasets. mdf publish --update [ID]: the ID defaults tosource_idinmdf.yaml, then the last publish;
the new version keeps the previous description, keywords, license, funding and related works
unless you change them; without data paths it is a metadata-only update. The first successful
publish writessource_idintomdf.yaml.mdf publish --submitagainst staging notes on stderr that staging mints DataCite test DOIs
(prefix10.23677) that do not resolve on doi.org.mdf import --submitagainst staging prints the same test-DOI notice.mdf loginandmdf whoamishow who you are (name and email) and the service;
whoami --jsongainsnameandemail, and reports rejected credentials (401/403) as
"credentials rejected"with exit 4.mdf login --token T --savekeeps a validated token per API URL (owner-only
~/.config/mdf/access_tokens.json, written atomically, never through a symlink). It is sent
only to the service it was saved for, and a usable cached Globus login takes precedence;
mdf logoutremoves it.mdf cite -o FILE; piped or redirected,citeprints the citation text only.mdf check [--update [ID]]: every checkpublish --submitruns, without uploading.mdf whoami,mdf doctor,mdf init,mdf cite,mdf versionsas top-level commands;
mdf show --files/--sample;mdf clone --plan --fileswith a live file count and
--timeoutfor the listing.mdf list: relative dates, one-line titles, a "Next: mdf status " hint.- Shared CLI options and client lifecycle (
mdf.cli.options:ServiceOpt,JsonOpt, …,
open_client());--dev-useris hidden from help. - SDK:
interactive=onBackendClient.authenticated,get_authorizerand
get_authorizer_for_scopes, themdf.auth.globus.interactive_login(allowed)context manager, and
mdf.core.exceptions.AuthRequired. The SDK default is unchanged (it may prompt).
Fixed
mdf publish --update: explicit empty values inmdf.yaml(subjects: [],description:)
clear inherited values;custommerges per key with inherited extensions; unknown author keys
(e.g. identifiers) are kept; missing or unreadable previous metadata stops the update (exit 5)
instead of silently dropping it;source_idwrite-back replaces an emptysource_id:line and
writes atomically; a failing history write no longer fails a successful publish.- Uploads for an update go to a fresh upload location, never into an existing
/mdf_open/{source_id}/directory (the legacyextensions.mdf_source_idno longer steers them). mdf importof a missing Zenodo record or unregistered DOI exits3(not found) without raw
HTTP-library text.- Search/list/related tables show
—for missing values and titles on one line. mdf versionsno longer suggests diffing a single version against itself.mdf showcrashed on every real dataset (author objects); authors now render everywhere
through one helper.- Tables show full dataset IDs in the first column (
search,list,related;searchdrops its
constant Type/Status columns to make room), and output that is not a terminal has no box drawing. mdf logoutprinted its message twice.- Multi-source
mdf clone --transfertransferred only the first data source. publishsilently submitted local paths when no data token was available; it now fails with a clear message.http(s)://data sources were dropped by clone; same-name files from different hosts overwrote each other.mdf validate --jsonprinted Rich chrome on stdout;whoami --jsonalways reported success.--service prodretry storm on connection timeout.