Releases: ZenixSolutions/lumics-mcp
Release list
v0.1.2
Install: npx -y @zenixsolutions/lumics-mcp — on npm (published with provenance).
23.6% smaller. 92 files and 755.5 kB unpacked → 48 files and 577.2 kB. No behaviour changes; the tool surface is identical to 0.1.1.
Changed
-
Source maps are no longer published.
dist/**/*.mapis excluded from the tarball, cutting it
from 92 files and 748.7 kB unpacked to 48 files and 573.9 kB — about 23% off everynpx
invocation and every install.They were inert. The 44 maps
0.1.0and0.1.1shipped point at../src/*.ts, carry no
sourcesContent, andsrcis not in the package, so nothing existed at the targets on a
consumer's disk. Every consumer paid 181 kB for debugging support that could not work.They are still emitted by
npm run buildand still work locally, where the sources are present.
The exclusion is a negated pattern inpackage.jsonfiles, not a build change, so nothing about
the local development experience changes. Shipping resolvable maps instead would have meant
publishingsrcand growing the package to roughly 1.15 MB; that trade-off is recorded in
#9.
Added
-
The release workflow now verifies the changelog section is finished, not merely present.
scripts/check-changelog.mjsfails the release on a missing or undated heading, a heading still
markedUnreleased, an empty section, a missing comparison link, or scaffolding wording left in
the body.release.ymlruns it beforenpm ci, so a tag that cannot produce a release fails in
seconds rather than after a full install and test run. Available locally as
npm run check:changelog -- X.Y.Z.The previous check only grepped for the heading, which a section full of scaffolding satisfies —
and did:0.1.0published with "Nothing below has shipped yet; this section is the release note
under construction and is finalised at tag time" still in it, andCHANGELOG.mdships inside the
tarball, so the release announced on the registry that it had not happened
(#10).Verified against that exact published text, which the gate rejects on four counts. Be candid
about the limit: the structural checks are exact, but the scaffolding check is a phrase list and
only catches wording someone thought to write down.Quoted text is excluded from the scaffolding scan — inline code, fenced blocks, blockquotes and
double-quoted spans, including spans that wrap across lines. That was not foresight. The gate
failed this very release on its first run, because the entry you are reading quotes the wording
the gate bans. A changelog documenting a scaffolding phrase is not scaffolding, and a gate that
cannot tell the difference would push people to reword accurate notes to appease it. -
tests/installation/package-contents.test.tsasksnpm packwhat it would publish and asserts
the answer. It covers both directions in whichfilesfails silently: too broad, which is how the
maps shipped, and too narrow, which would publish a package that installs and cannot start. It
also asserts no.env, key or.npmrcis packed, and that nothing outsidedist/and the four
named documents appears at all. Verified it fails when the map exclusion is removed.
v0.1.1
Install: npx -y @zenixsolutions/lumics-mcp — on npm (published with provenance).
If you are on 0.1.0, upgrade. lumics_create_ipaddress, lumics_update_ipaddress and lumics_delete_ipaddress addressed a route the Lumics API does not serve and could never have succeeded. The two read tools were unaffected.
Fixes three tools that could never have worked in 0.1.0.
Fixed
-
lumics_create_ipaddress,lumics_update_ipaddressandlumics_delete_ipaddressaddressed a
route that does not exist. They sent the PLURAL/ipsubnets/:id/ipaddressessegment, which the
Lumics API does not route for any verb. All three now send the SINGULAR/ipsubnet/, which is
what every IP address route uses. Verified against a live tenant on 2026-07-31, including a real
create. The two read tools were unaffected and always worked.The captured contract was the source of the error, not a slip in the code.
docs/reference/lumics-api-v1.md
§13 Q1 documented a per-verb split — singular reads, plural writes — asserted it was "confirmed
present in the vendor's own route definitions", and instructed readers not to "fix" it. Measured
against the live API, there is no such split. §13 Q1 is corrected in place with the vendor's
original wording retained alongside, and the measurements are recorded in §0.5 (M13–M16), §14
defects 26–28, anddocs/contract-runs/2026-07-31-run-04.md.Surfaced by a user bug report of a hung
lumics_create_ipaddresscall. The hang came from the
prototype this server replaced; on0.1.0the same call fails in about 150ms with a clean 404.
Added
-
tests/contract/live-write-routes.test.ts— live routing coverage for every write path, not
just IPAM. It mutates nothing: probes address ids no record holds and send empty bodies, so a
routed path can answer without anything being created, changed or deleted.This closes the gap that let the defect ship. The contract gate was read-only by design (D-0006),
so it had never issued a single write request, and0.1.0passed it with three broken tools. The
four top-levelPOSTcreates remain deliberately UNVERIFIED rather than probed, because a
top-level create has no parent id to falsify — recorded as such rather than asserted weakly.
Changed
- Route probing must not use this project's own HTTP client.
LumicsClientsends
Accept: application/json, and the API content-negotiates its router 404 into JSON — which makes
a dead route indistinguishable from a missing record. The first live run of the new suite passed
its positive assertions while establishing nothing for exactly this reason. The probes now issue
raw requests withAccept: */*, where an unrouted path answers an HTML error page, and the
premise is stated in the classifier's own documentation. README.md,CLAUDE.md,docs/TOOLS.mdanddocs/RELEASE.mdno longer state the withdrawn
per-verb rule.CLAUDE.md's "do not fix the API in code" guidance stands; only its example
changed, since the old one would now lead an agent to reintroduce the defect.
v0.1.0
Install: npx -y @zenixsolutions/lumics-mcp — on npm (published with provenance).
See README.md for client setup and the full configuration table.
First release.
Added
- 39 tools, one for each of the 41 documented Lumics REST API v1.0 endpoints except the two token
endpoints withheld on security grounds: collectors (5, spec §5), components and component types
(5, §6), devices (7, §7), IPAM addresses (5, §8), IPAM groups (5, §9), IPAM subnets (5, §10),
identity (2 of the 4 endpoints in §11), and metrics (5, §12). 37 are registered in a default
deployment; 20 underLUMICS_READ_ONLY=1. - stdio transport, distributed as the npm package
@zenixsolutions/lumics-mcpand runnable with
npx.LUMICS_TRANSPORT=httpis refused at startup in this release: ADR-001 decision 3 makes
v0.1 stdio-only and states that it opens no network listener at all, so the configuration path to
the listener is closed rather than left reachable while three documents say it does not exist.
src/transport/http.tsstays in the tree so v0.2 is additive, and the fiveLUMICS_HTTP_*
variables are documented for forward reference only. Streamable HTTP is ADR-001 decision 4,
scheduled for v0.2. LUMICS_COMPANY_IDis optional. Without it the server starts, registers only the two tools that
need no company (lumics_get_meandlumics_get_device_definition_components; three with
LUMICS_ENABLE_TOKEN_REVOCATIONon) and logs a warning. This is what makes the documented first-run
flow possible: the way to discover a company id islumics_get_me, and a server that refused to
start without the id could not run the tool that finds it. Calllumics_get_me, set the variable,
restart. A value that is supplied is still format-checked at startup.- Operation classification on every tool — Read, Create, Update, Admin, or Destructive — with the MCP
annotations (readOnlyHint,destructiveHint,idempotentHint) derived from the classification
rather than written by hand, so an annotation cannot contradict it.openWorldHintistrue
everywhere. - Per-tool reference documentation at
docs/TOOLS.md: every tool's arguments,
types, defaults, constraints, return shape, underlying endpoint, and gating. LUMICS_READ_ONLY=1safety switch, which registers read tools only.- Opt-in gates for the two highest-impact operations:
LUMICS_ENABLE_BATCH_UPDATEfor bulk device
update andLUMICS_ENABLE_TOKEN_REVOCATIONfor token revocation. Both are registration-time
controls: without the flag the tool is absent fromtools/listentirely. - Human-friendly time windows on metric tools: a relative
lookbacksuch as15m,6hor7d, or
ISO-8601from/to, converted internally to the API's epoch-millisecondfromMs/toMs. The
window defaults to the last hour. Nobody has to compute epoch milliseconds. Reversed windows,
epoch-seconds mistakes, windows wider than 366 days, atoin the future, andfromcombined
withlookbackare all rejected locally with an explanation. - Timestamp arguments require an explicit timezone.
from/toon every metric tool anddate
onlumics_update_device_last_discoveryaccept a bareYYYY-MM-DD(meaning UTC midnight), an
ISO-8601 timestamp carryingZor a numeric offset, or epoch milliseconds. A timestamp with a
time component and no zone —2026-07-29T14:00:00— is rejected, with a message naming the
fix.Date.parsereads that form in the server's local timezone while reading a bare date as UTC,
which shifted a window by up to fourteen hours while the response notes reported the shifted
window: a wrong answer that looked internally consistent. On
lumics_update_device_last_discoverythe same input would have persisted a shifted discovery time
to Lumics. - A default
fieldsprojection onlumics_list_devices—id,name,ipAddress,deviceType,
collector,enabled,maintenanceMode— because a full device record is around 1.9 kB and the
defaultlimitof 100 could not fit the 25,000-character output budget: a default call returned
thirteen of the hundred devices it asked for, with two disclosure notes giving opposite advice.
The projection is disclosed in every response, an explicitfieldsargument replaces it, and
fields: []asks for whole records. No other list tool projects by default. LUMICS_ALLOW_CROSS_COMPANY, off by default. Every tool is covered by the company pin: most take an
optionalcompanyIdand, with the flag unset, a value differing fromLUMICS_COMPANY_IDis refused
withnot_permitted. See Security below.LUMICS_LOG_LEVEL, one ofdebug,info(default),warn,errororsilent. Diagnostics have
always gone to stderr — stdout is the MCP protocol channel — but there was no way to turn the
verbosity up or off.debugadds a record per tool call with its duration, output size, whether the
limit was reached and how many items the output budget dropped, which is what Troubleshooting needs
when a tool returns less than expected.silentquiets stderr entirely, for a supervisor that treats
any stderr output as a fault. The level is parsed insrc/config.tsand applied bysrc/index.ts,
so importing this package cannot change a host application's logging.- A default resolution of 60
dataPointson metric calls, since the Lumics API requiresdataPoints
orwidthon every metric-data endpoint and rejects a call with neither. The effective value, and
whether it was defaulted, is disclosed in the output. - Client-side ranking (
topN,sortBy,sortDirection) formetrics/summaries, which accepts no
limit, top-N or sort parameter of any kind. The output states that the ranking was applied by
this server after fetching the full set, and reports how many items had no value at the sort path.
Lumics keys its results by item class, and the trim is applied per class: withtopN: 2over two
classes you can get four rows, and no ranking crosses a class boundary. The response says so whenever
more than one class is present, rather than only when the output budget happened to drop something. - Local input validation ahead of the API: identifiers must be 24-character hex ObjectIds, IP
addresses must parse as addresses, netmasks as dotted quads, MAC addresses as MAC addresses, and a
PATCH with no changed fields is refused rather than reported as a successful no-op. - Output shaping with an optional
fieldsprojection and aLUMICS_MAX_OUTPUT_CHARSbudget
(default 25,000). The budget caps the entire text a tool returns, disclosure notes and JSON
payload together: notes are reserved first and the payload is fitted to what remains. The one
exception is disclosures that exceed the budget by themselves — they are emitted in full and the
payload is reduced to nothing, because a disclosure is never dropped or shortened to save space.
Arrays shed whole items from the end so what remains still parses and the loss is positional;
every drop is disclosed with a count. The completeness note and the truncation note are generated
together, so a response cut by both no longer tells you to raise the limit and to lower it in the
same breath. - No
limitis sent to a metric endpoint unless the caller supplies one — deliberately unlike the
list tools, which default to 100.limitis optional upstream, and injecting a default silently
truncated a multi-thousand-row time series in an order Lumics does not document, cutting across
time as well as across components. An incomplete inventory looks incomplete; a series with holes
looks like data. The output budget sheds from the end instead, and the row-count note on every
metric response states which of the two happened and what it means for reading the series. - The captured Lumics API contract as a committed artifact at
docs/reference/lumics-api-v1.md, so
the contract the code targets is auditable and upstream drift is visible. - Governance and community documentation: README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT,
CLAUDE.md, dependency policy, release policy, issue and pull request templates. - CI on every push to
mainand every pull request: typecheck, lint, format check, tests, build,
secret scan, and a stdio startup smoke test on Node 20 and 22.
Changed
Everything in this section comes from the first contract run against a live Lumics tenant, on
2026-07-30, which contradicted the vendor documentation in the metric layer. The measurements are
recorded in docs/reference/lumics-api-v1.md §0, §12.5 and §14
defects 17–23; the decisions are
ADR-003.
- BREAKING (tool surface):
propertiesis now a required argument on
lumics_get_company_metrics,lumics_summarize_company_metrics,lumics_get_device_metricsand
lumics_get_device_item_metrics. Spec §12.0 marks it optional; the live API answers
400 {"error":"Must supply required component metrics as properties parameter"}without it, so
those four tools could not make a single successful call at all before this change. The break
is therefore real in form and removes a call shape that never worked. It is required rather than
defaulted because no metric name is correct for every module, and a default would turn every
unqualified request into a confident answer to a question nobody asked. The syntax is
<TypeGroup>.<metric>, comma-separated —Calculated.cpu. It stays optional on
lumics_get_metric_summary(spec §12.4), where it is genuinely optional upstream and acts as a
filter rather than a projection: supplying it returnedcount: 0on a live tenant, dropping
items rather than narrowing them. - **L...