Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 30 Jul 09:35
· 440 commits to main since this release

Added

  • Every epics-doctor line that reports a problem now names its remedy. Three statuses already
    did (ca_error named the CA bundle variable, api_error the mgmt/retrieval mix-up, config_error
    the variable to set) and four did not: unreachable printed only the transport error,
    backend_down named the consequence rather than the fix, and disconnected and
    identity_probe_failed said nothing about what to do. The remedies now live in one table keyed by
    status, appended to the observation and never replacing it, so --json readers get them in the same
    detail field. An unreachable plane also names the variable it reads its URL from, which is the
    one case where the reader cannot tell from the message which variable to look at. unverified
    and no_ingest deliberately stay without one: the first already carries the specific clue it
    measured, and the second is a fault inside the appliance, not in this configuration.

  • --version on all five console commands (epics-mcp had it; epics-doctor,
    epics-diagnose, epics-coverage and epics-crossplane did not), from one shared helper, so the
    version source and the command-name prefix cannot drift apart. Each prints its own name and the
    package version. Note: on the two display-aware commands the engine check still runs before the
    arguments are parsed, so on a core-only install those two report the missing engine instead; that
    limit is pinned by a test rather than left to be rediscovered.

  • epics-doctor notices an Archiver appliance that is not ingesting. getApplianceInfo proves
    that an appliance is answering and nothing more: one whose engine has never spoken to its IOCs
    answers it exactly like a healthy one, so the doctor printed ok for a deployment that was
    archiving nothing. That is a wiring fault, the class this tool exists to catch. The identified
    appliance is now also asked getApplianceMetrics, and two things make it a finding: channels held
    with none connected, or the appliance's own status reporting a stopped webapp. The second is
    the worse fault and is invisible in the counts, because when the engine webapp does not reply,
    mgmt cannot merge its numbers and they vanish from the row while pvCount survives, with the
    payload still served as HTTP 200.

  • New plane status no_ingest (~), exit 0. Deliberately not a failure: a freshly
    commissioned or fully paused appliance is legitimately in this state, and failing the run would
    make epics-doctor cry wolf in every CI job that calls it. It is not silent either, and that is
    what the next entry is for.

  • New --json field degraded_planes. The planes that proved their identity and are measurably
    not doing their job. It exists because every other signal stays clean for such a plane: ok and
    verification_complete remain true, unverified_planes and inconclusive_identity_planes
    remain empty, and identified_planes even lists it, since its identity IS proven. A script
    written against the documented field list would otherwise read a non-archiving archiver as
    positively confirmed. Purely additive; no existing field changes meaning and no exit code moves.

Changed

  • The archiver plane now issues three requests on a healthy run rather than two, and the ingest
    probe uses a single attempt with a 15 s floor instead of the shared retrying session. urllib3
    applies the timeout per attempt, and this route fans out to three internal requests per cluster
    member: measured against a 16-member cluster it answers in 7.3 s but takes 23.3 s to fail under
    the default 3-retry policy, which would have made the check blind there while slowing every run.
  • The archiver identity probe goes through the same shared beacon fetcher as every other plane. It
    was the one plane building its request inline, which already made that helper's "the one place
    every identity probe issues its request" contract untrue.
  • epics-doctor's config_error line now names the variable to set at its START, and the
    remedy points at that position.
    Measured against the last published release: 0.3.0 ended the
    observation with "Set EPICS_MCP_ARCHIVER_URL (the MGMT webapp URL)", the remedy table then took
    that instruction over, and it now reads "Set the variable named at the start of this finding",
    the construction unreachable already used. This matters beyond wording because detail is the
    field --json readers are told to use, so a consumer matching the older text will not find it.
    The position is guarded; the wording deliberately is not.
  • BREAKING: the import package is now epics_mcp (was epics_pv_mcp). import epics_pv_mcp
    stops working; import epics_mcp replaces it, one for one, with no other change to the API. This
    completes the rename begun in 0.3.0, where the distribution, the repository, the server command
    and the server's MCP identity already became epics-mcp and only the import package did not. It
    is done now rather than later because every release under the old import name grows the set of
    installations a rename breaks.
  • BREAKING: the epics-pv-mcp console command is removed. Use epics-mcp, which has been the
    primary command since 0.3.0. The alias was added in 0.3.0 for anyone following pre-rename docs,
    and 0.3.0 shipped it: every installation of that release carries epics-pv-mcp, so upgrading to
    0.4.0 removes a command that works today. Check your wrapper scripts and MCP client configs for
    it. Nothing older is affected, because 0.3.0 was this project's first published release of any
    kind. The four diagnostic commands (epics-doctor, epics-diagnose, epics-crossplane,
    epics-coverage) are unaffected.
  • BREAKING: the all extra is removed. pip install epics-mcp[all] no longer resolves; use
    epics-mcp[dev], which is what all contained. It was exactly epics-mcp[dev], so it promised a
    reader everything the package can do and delivered the developer toolchain, and the display-aware
    tools it seemed to imply are not an extra at all but a local dependency group. dev is now the
    only extra. Nothing else changes: no dependency is added or dropped by this.
  • The product is called EPICS MCP. The prose name was still "EPICS PV MCP Server" in 19 places,
    including the README H1 (which is the project page title on the index), the CITATION.cff title
    that published work cites, and the first line of the operator guide that ships in the wheel. The
    rename to epics-mcp in 0.3.0 was made because the PV plane is one of six, so a title saying PV
    contradicted its own reason. Nothing user-facing behaves differently; the distribution, the
    commands, the import package and the MCP identity are unchanged.
  • epics-doctor exits 1, not 2, on an internal error. 2 is the usage-error code across these
    commands and in argparse, so the old value told a wrapper the caller had passed something wrong
    when the command itself had failed. A genuine usage error (an unknown flag) still exits 2.
    Note the cost: exit 1 is also "a configured plane hard failed", so the exit code alone no longer
    separates the two. They differ on the streams: an internal error writes a doctor: line to
    stderr and no report to stdout.

Fixed

  • The display-aware CLIs tell a broken engine apart from a missing one. epics-crossplane and
    epics-coverage probed for the opi_navigation engine with a name lookup only, so an engine
    that was installed but did not import (typically a missing transitive dependency) passed the
    check and the command then died with a bare traceback. It now reports the failure with the
    underlying exception named and exits 1, while a genuinely absent engine keeps its explanation and
    exit 2. The advice differs too: installing the engine will not fix a broken one.
  • The core server no longer dies over an OPTIONAL capability. The display-capability probe called
    importlib.util.find_spec bare, and it runs at module level, so an import hook that RAISES for
    opi_navigation took the whole epics_mcp.server import down: exit 1 with a traceback, on a
    server whose PV tools never needed the display engine at all. The probe now answers instead of
    propagating. A module a finder reports as genuinely absent is the supported core-only state and
    stays silent, as before; a finder that could not answer is a different claim and is logged loud
    rather than wearing the "not installed" message. The sibling CLIs already behaved this way, so one
    package was answering the same question two ways.
  • A long live value in the find_device report is capped at 80 characters, not 82, as its own
    documentation promised.
  • The ready-to-paste write-enabled MCP client block now starts. As shipped it omitted
    EPICS_MCP_AUDIT_LOG_FILE and the loopback reach settings, both of which a write-enabled server
    refuses to start without, so a reader who pasted it saw only "server not connected".
  • The setup instructions no longer describe a .env file. Four places told adopters to copy
    .env.example to .env, or to run epics-doctor to confirm one. Nothing in the server ever
    loads a dotenv file; configuration is read from the process environment, and a variable that does
    not reach the process is dropped silently. .env.example is a reference to copy lines OUT of.
  • EPICS_MCP_ARCHIVER_RETRIEVAL_URL is spelled with its prefix in the README. The unprefixed
    form does not bind, so a split-appliance operator who followed it got history requests sent to
    the mgmt port with no error to explain it.
  • .env.example names all four tools the Olog write gate covers, not two. It was the only
    place that understated the gate's reach.
  • Every link on the PyPI project page now goes somewhere. The README is the long_description,
    and on that page there is no repository around it, so its relative targets resolved against
    pypi.org and landed nowhere; measured on the rendered page, all of them. They are absolute GitHub
    URLs now, pinned to main so the page always points at current documentation. Anchors were
    already fine there (the index rewrites them) and are untouched. The link guard follows the new
    spelling rather than skipping it as a URL, so a renamed page still fails the build.
  • The Naming-Service modules describe their own origin correctly, and no longer ship a developer
    path.
    Both called themselves "vendored" from pvValidator, which is GPL-3.0-only while this
    package is MIT, so the wording asserted a licence problem that does not exist: measured with
    difflib over the whole files, the longest identical run of lines is four (two of them non-blank,
    both imports), and the shared remainder is signatures the REST endpoint dictates. They now say
    they follow that client's API shape and carry none of its code, the measurement is recorded in
    the known limits, and attribution sits in the README credits. One of the two also carried an
    absolute path from the author's machine into the published wheel; that string is gone, and the
    repo-wide facility guard now rejects a local drive path so it cannot come back.
  • The operator guide no longer promises an op= correlation the refusals cannot honour, and its
    Olog separator table is right about +.
    The write-posture section said every stage of a
    set_pv_value write is op-correlated; the id is issued when a write is dispatched, so the two
    pre-dispatch refusals (DENY, BOUNDS_DENY) carry none, and a reader correlating an audit trail
    by op would have looked for a token that is never written. The filter table listed a
    separators-only value as dropped for level as well, while the same page explains ten lines lower
    that + splits only title and is an ordinary level. The guide ships in the wheel and is served
    as the epics-pv://guide resource, so both were wrong at the point of use.
  • The start conditions of the PV write gate are now listed completely wherever they are listed at
    all.
    The tool description, the server instructions, the configuration reference and the
    deployment guide each enumerated what a write-enabled server needs and left out the loopback-only
    search reach, while other places state it. An enumeration that stops one item short reads as
    complete, so an operator learned that the network posture and the write gate were independent
    settings, when in fact writes on plus a reach beyond loopback is a start-time refusal. The
    configuration reference also gained the EPICS_CA_NAME_SERVERS row: it takes part in that check
    but was missing from the EPICS network table.
  • The bug report template no longer calls this an unqualified read-only server. It can write,
    behind gates; the reason not to paste credentials is that each REST plane takes an
    EPICS_MCP_*_AUTH value and an error string can carry the request URL.
  • epics-mcp --help prints help, and --version prints the version. epics-mcp accepted no
    options at all, so --help reached the stdio transport instead of a parser: the command appeared
    to succeed, printed nothing and exited 0, because a server started on a closed stdin ends at once.
    An unknown option is now a usage error (exit 2). --version answers the question the bug report
    template asks, which no command line could answer before. The usage line of all five commands is
    also pinned to the command's own name; on Python 3.14 argparse derived it from the interpreter and
    printed the absolute path of the installed script instead.

Internal

  • The two prose guards no longer leak their exemptions: the language guard's self-exemption is keyed
    on file paths rather than bare file names, and both guards anchor an exception's path key on a
    path-component boundary, so a key written for scripts/x.py no longer also frees
    myscripts/x.py.
  • The GitHub Actions workflows moved off the deprecated Node 20 runtime (setup-uv,
    upload-artifact, download-artifact).