lumics-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@lumics-mcplist all devices"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
lumics-mcp
A Model Context Protocol (MCP) server that lets an AI assistant read and operate the Lumics network monitoring platform.
Not affiliated with Lumics or NetCuras. This is an independent, community-maintained open-source project. It is not affiliated with, sponsored by, endorsed by, or supported by Lumics or NetCuras. No claim is made to their trademarks or other marks; "Lumics" and "NetCuras" are used here only to identify the platform this software talks to. For support with the Lumics platform itself, contact Lumics. For support with this server, open an issue here.
Contents
Related MCP server: domotz-mcp
What it does
The server exposes the documented Lumics REST API v1.0 surface as MCP tools, so an assistant can answer questions about your monitored estate and — when you allow it — change it. Tools cover these resource areas:
Devices — list, read, create, update, delete; update a module's last-discovery time; bulk update (gated, off by default).
Collectors — list, read, create, update, delete.
Components and component types — list and read components of a given type, update a component, enumerate component types and device definitions.
IPAM subnets — list, read, create, update, delete.
IPAM addresses — list and read addresses within a subnet, create, update, delete.
IPAM groups — list, read, create, update, delete, including parent/child grouping.
Metrics — per-component metrics for a company or a device, single-item metrics, summarized (averaged or summed) metrics, and cross-device metric summaries.
Identity — read the current user and company; revoke tokens (gated, off by default).
The underlying contract is captured verbatim in docs/reference/lumics-api-v1.md,
which ships with the repository so you can audit what the code targets and see when the vendor
API drifts from it.
Prerequisites
You need two things before installing:
A Lumics account with access to the data you want the assistant to see. The server acts as your user; it cannot see anything you cannot see.
A JWT API token. Lumics issues JWT bearer tokens only — there is no OAuth.
You also want your company id, a 24-character hex value — but you do not need it in hand before you start, because the server will help you find it. See First run: finding your company id.
Minting a token
The simplest route is to log into the Lumics web UI and visit /api/v1/me/token. You can also
POST your credentials:
curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' \
--data 'username=you@example.com&password=YOUR_PASSWORD' \
'https://app.lumics.io/api/v1/me/token?expiresIn=86400'Replace you@example.com and YOUR_PASSWORD with your own credentials. The response is
{ "token": "...", "expiresIn": 86400 }.
expiresIn is in seconds and defaults to 86400 (one day). The Lumics API accepts larger values
and documents no maximum, so it is entirely possible to mint a token that effectively never
expires. Do not. Use the shortest lifetime that is workable and rotate. See Security.
First run: finding your company id
LUMICS_COMPANY_ID is optional. Without it the server still starts, so you can use it to
discover the id:
Configure the server with
LUMICS_TOKENonly, leavingLUMICS_COMPANY_IDunset, and connect it from your client.Ask the assistant to call
lumics_get_me. It returns the user this token authenticates as and the company that user belongs to. The company id is the 24-character hex string.Set
LUMICS_COMPANY_IDto that value and restart the server (and your client, if it caches the tool list).
Until the variable is set, only the tools that need no company are registered — lumics_get_me and
lumics_get_device_definition_components, which is two tools (three, if you have enabled
lumics_revoke_tokens). Everything else is company-scoped and is withheld from tools/list rather
than left to fail on every call, so a partly-configured server looks small rather than broken. The
server logs a warning on stderr saying exactly this.
lumics_get_device_metrics and lumics_get_device_item_metrics are in that withheld set even
though their Lumics paths carry no company segment: they enforce the company pin by reading the
device inside LUMICS_COMPANY_ID first, so without a configured company there is nothing for them
to check against. See Cross-company access is off by default.
If you already know the id you can skip all of that and set it up front; the examples below do. A value that is supplied is validated at startup and must be 24 hex characters, so a typo fails immediately instead of turning into a 404 mid-conversation.
You can also read the id out of the Lumics web UI URL after /companies/.
Install
Node.js 20 or newer is required. Every command below runs the published package with npx, so
there is nothing to clone or build.
Claude Code
claude mcp add lumics \
--env LUMICS_TOKEN=your-jwt-token \
--env LUMICS_COMPANY_ID=your-company-id \
--env LUMICS_READ_ONLY=1 \
-- npx -y @zenixsolutions/lumics-mcpTwo details matter here:
The bare
--separatesclaude mcp add's own flags from the command it should run. Without it,npxand its arguments are parsed as flags toclaudeand the command fails.By default the server is registered for the current project only. Add
-s userto make it available in every project:
claude mcp add lumics -s user \
--env LUMICS_TOKEN=your-jwt-token \
--env LUMICS_COMPANY_ID=your-company-id \
--env LUMICS_READ_ONLY=1 \
-- npx -y @zenixsolutions/lumics-mcpClaude Desktop
Edit claude_desktop_config.json — Settings → Developer → Edit Config will open it, or find it at
~/Library/Application Support/Claude/claude_desktop_config.json on macOS and
%APPDATA%\Claude\claude_desktop_config.json on Windows. Add:
{
"mcpServers": {
"lumics": {
"command": "npx",
"args": ["-y", "@zenixsolutions/lumics-mcp"],
"env": {
"LUMICS_TOKEN": "your-jwt-token",
"LUMICS_COMPANY_ID": "your-company-id",
"LUMICS_READ_ONLY": "1"
}
}
}
}Restart Claude Desktop afterwards. Note that this file stores your token in plaintext; treat it as a credential file.
VS Code
Add to .vscode/mcp.json in your workspace, or to your user settings.json under "mcp":
{
"servers": {
"lumics": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@zenixsolutions/lumics-mcp"],
"env": {
"LUMICS_TOKEN": "your-jwt-token",
"LUMICS_COMPANY_ID": "your-company-id",
"LUMICS_READ_ONLY": "1"
}
}
}
}Cursor
Add to ~/.cursor/mcp.json for all projects, or .cursor/mcp.json inside a project:
{
"mcpServers": {
"lumics": {
"command": "npx",
"args": ["-y", "@zenixsolutions/lumics-mcp"],
"env": {
"LUMICS_TOKEN": "your-jwt-token",
"LUMICS_COMPANY_ID": "your-company-id",
"LUMICS_READ_ONLY": "1"
}
}
}
}Codex
Add to ~/.codex/config.toml:
[mcp_servers.lumics]
command = "npx"
args = ["-y", "@zenixsolutions/lumics-mcp"]
[mcp_servers.lumics.env]
LUMICS_TOKEN = "your-jwt-token"
LUMICS_COMPANY_ID = "your-company-id"
LUMICS_READ_ONLY = "1"ChatGPT and Grok — not yet supported in v0.1
Do not expect these to work today. This is a capability gap in those clients, not an oversight here, and no packaging trick works around it:
ChatGPT developer-mode connectors and Grok custom connectors both connect to MCP servers over a publicly reachable HTTPS URL. Neither client can launch or talk to a local process over stdio, which is the only transport v0.1 ships.
Grok additionally rejects
localhostand private/internal addresses outright, so tunnelling to a machine on your own network is not a workaround either.The
mcp-remotebridge does not help. It is itself a local process; it does not make your server reachable from ChatGPT's or Grok's servers.
Support for both arrives with the v0.2 self-hosted HTTP deployment, where you run the server behind your own TLS-terminated HTTPS endpoint and point the client at your own URL. That deployment is not released yet. Until it is, this section describes work that has not been done.
Client support matrix
Client | v0.1 (stdio via | Notes |
Claude Code | Supported |
|
Claude Desktop | Supported |
|
VS Code | Supported |
|
Cursor | Supported |
|
Codex | Supported |
|
ChatGPT | Not supported | Needs public HTTPS endpoint — planned v0.2 |
Grok | Not supported | Needs public HTTPS endpoint — planned v0.2 |
Claude.ai web | Not supported | Needs public HTTPS endpoint — planned v0.2 |
Configuration
All configuration is by environment variable. The canonical, commented list is
.env.example; this table mirrors it.
Variable | Required | Default | Description |
| Yes | — | Lumics API token (JWT bearer). Sent as |
| No | unset | Your Lumics company id (24-character hex), used as the |
| No |
| API base URL. Override only for a self-hosted or non-production Lumics. Must use |
| No |
| Per-request timeout in milliseconds. |
| No |
| Maximum characters of tool output — disclosure notes and JSON payload together, not the payload alone. Truncation is always disclosed in the response. |
| No |
| Diagnostic verbosity on stderr: |
| No | unset (off) | Set to |
| No | unset (off) | Set to |
| No | unset (off) | Set to |
| No | unset (off) | Set to |
| No |
|
|
| No (v0.2 only) |
| HTTP listener port. Inert in 0.1.0; documented for forward reference. |
| No (v0.2 only) |
| HTTP bind address. Loopback by default. Change only behind TLS and authentication. Inert in 0.1.0. |
| Yes when | — | Shared secret clients must present as |
| No (v0.2 only) |
| Comma-separated |
| No (v0.2 only) | empty (no origin allowed) | Comma-separated |
| No | unset (off) | Maintainers only. Set to |
The five LUMICS_HTTP_* variables are listed because they are validated at startup when you ask for
the HTTP transport — but 0.1.0 then refuses the transport itself, so setting any of them changes
nothing about how this release runs. They are documented so a v0.2 deployment can be prepared and
reviewed before it exists.
The server does not read a .env file
Configuration comes from the environment the process is given, and from nowhere else. Nothing here
discovers a .env. An earlier build did, using a relative path, which for a published server meant
whichever directory the MCP client happened to launch it from — so a file planted there could
redirect LUMICS_BASE_URL and send the token elsewhere, or reopen every safety gate the operator had
left closed. See Security.
Two supported ways to use a .env, both of which are the operator choosing a file rather than the
server finding one:
Your MCP client's
envblock. Every install above already does this; nothing extra is needed.Node's own flag, for local development:
cp .env.example .env # then edit it node --env-file=.env dist/index.js--env-file=.envrequires the file to exist;--env-file-if-exists=.envtolerates its absence.
.env is gitignored and must never be committed.
Security
Read this section before you give an assistant a write-capable token.
Start read-only
LUMICS_READ_ONLY=1This is the recommended default posture, and it is a registration-time control rather than a runtime check: with it set, create, update, delete, and admin tools are never advertised to the model at all. A model cannot misuse a tool it cannot see. Turn it off only when you specifically intend the assistant to modify your Lumics data, and prefer running two separately configured server entries — one read-only for everyday use, one write-capable for deliberate change work — over leaving writes permanently enabled.
Two operations are gated off by default
Both stay unavailable unless you opt in with an environment variable. A model cannot enable them.
LUMICS_ENABLE_BATCH_UPDATE— bulk device update. A single call applies the same field changes to a comma-separated list of device ids. The Lumics API documents no cap on how many. The blast radius of one malformed call is your whole device inventory.LUMICS_ENABLE_TOKEN_REVOCATION— token revocation. The Lumics API has no per-token revoke.POST /me/token/revokerevokes every JWT ever issued to your user, including the token this server is currently using, and including tokens used by any other integration, script, or session you own. It is self-inflicted denial of service plus collateral damage, it cannot be undone, and everything affected must be re-issued by hand. The tool also requires an explicit confirmation argument, but treat that as a speed bump: an agent can supply it. The environment variable is the real gate.
Cross-company access is off by default
Every tool is covered by the company pin. Most take an optional companyId argument, and by
default a value that differs from LUMICS_COMPANY_ID is refused with a not_permitted error. A
Lumics token issued to an MSP user reaches every company that user administers, so without this gate
a model that had picked up another tenant's id — from a conversation, a document, a previous answer —
could read or write there, while the tool description said the call applied to the configured
company.
Two tools have no companyId argument to check, because their Lumics paths carry no company segment:
lumics_get_device_metrics and lumics_get_device_item_metrics are addressed by device id alone.
They enforce the same pin a different way — a device-ownership read first: the device is fetched
inside LUMICS_COMPANY_ID, and the metric read happens only if that confirms the device belongs
there. A device in another tenant is refused with not_permitted and no metric request is made. That
costs one extra round trip per call, which is the price of a control with no exception in it. Both
tools are therefore company-scoped, and are withheld entirely when LUMICS_COMPANY_ID is unset.
Set LUMICS_ALLOW_CROSS_COMPANY=1 only if you deliberately operate several tenants through one
server, and understand that it widens the blast radius of every write from one company to every
company the token can reach. It is an operator setting; nothing in a conversation can turn it on.
See SECURITY.md.
Token handling
Mint tokens with the shortest workable
expiresIn. The default is one day; the API documents no maximum, so nothing stops you creating a token that outlives its usefulness by years.Rotate regularly, and rotate immediately if a token has been pasted anywhere it might persist — chat transcripts, shell history, CI logs, an issue comment.
Client configuration files (
claude_desktop_config.json,.cursor/mcp.json,~/.codex/config.toml) store tokens in plaintext. Protect them like private keys.The server redacts credential material at the error boundary, so tokens do not appear in tool output or diagnostics. That is verified by test, not assumed.
Never commit
.env. Secret scanning runs in CI.
Reporting a vulnerability
See SECURITY.md. Do not open a public issue for a security problem.
Limitations
These are real constraints of the current release and the underlying API. They are listed here rather than buried because knowing them changes how you should read the server's answers.
The Lumics API has no pagination
This is the most important limitation. Across all 41 documented endpoints the only result-control
parameter is limit. There is no offset, page, skip, cursor, after, sort, or order,
list responses are bare JSON arrays with no total count, and no response carries a next-page link.
Consequences:
Large result sets can be truncated with no way to fetch the rest. If you have more devices than the effective limit, you cannot page through them. Narrowing the query is the only option.
The server therefore emits no
offset,has_more, ornext_offsetfields. Inventing them would be lying: ahas_more: falseon a truncated list would make an agent report a partial inventory as complete.When a result count reaches the requested limit, the server says explicitly that results may be truncated and that the API provides no pagination mechanism. Read that disclosure as "this may not be everything."
Some list endpoints (
component,componenttypes,deviceDefinitions/components, andmetrics/summaries) do not document even alimit.
Metric tools require a properties argument, and a wrong one is not an error
properties is required on lumics_get_company_metrics, lumics_summarize_company_metrics,
lumics_get_device_metrics and lumics_get_device_item_metrics. The vendor documentation calls it
optional and is wrong: without it the API answers
400 "Must supply required component metrics as properties parameter", so those four tools cannot
make a single successful call. It is required rather than defaulted because no metric name is
correct for every module — a default would answer a question nobody asked. It stays optional on
lumics_get_metric_summary, where it means something different (see below).
The syntax is <TypeGroup>.<metric>, comma-separated, no spaces — Calculated.cpu, or
Calculated.cpu,Calculated.mem. Type groups seen on live tenants are Calculated, Rate and
TimeTicks; there is no Counter or Gauge group despite those being plausible SNMP type names.
Property names are tenant-specific, so treat the examples as shapes rather than as a vocabulary.
To find legal names, call lumics_get_metric_summary with no properties and read
data.<class>[].stats: the outer keys are type groups and the inner keys are metric names, so
joining them with a dot gives a value the other four tools accept. Two limits on that, both real:
it enumerates device-scoped names only, so a component-level name — an interface counter, say — is discoverable from no endpoint at all and has to come from the Lumics UI or from someone who already knows it;
its response key depends on the module (
devicesforsnmp,http_endpointsforhttp), so read whichever key is present rather than assumingdevices.
On lumics_get_metric_summary itself, properties filters rather than projects: supplying it
drops items that do not carry the property instead of narrowing the items that do, and it has been
observed emptying a response that was otherwise full. Leave it unset there unless you have a reason.
A name the API does not recognise is not rejected. It returns HTTP 200 with the full row count
and empty stats on every row, which reads exactly like "this metric has no data". This server
rejects the worst form of it locally — a value in which no entry carries a Group. prefix — and
detects the rest after the fact, disclosing in the response that the names may be wrong rather than
letting an empty answer stand. See
A metric call returned rows but no values.
One more thing about component selection: itemType takes the singular component id
(snmp_common_cpu), which is not what lumics_list_component_types returns — that endpoint
returns plural aliases and most of them are rejected with 400 Unknown component. Build the id from
lumics_get_device_definition_components instead, or copy the type field off a row a metric call
already returned. Lumics validates itemType before properties, so a wrong itemType hides a
properties problem entirely: fix itemType first.
The company-scoped metric endpoint is unreliable; prefer the device-scoped tools
lumics_get_company_metrics and lumics_summarize_company_metrics read
/api/v1/metrics/companies/..., and that route did not answer dependably when it was measured
against a production tenant on 2026-07-30 (spec §12.5 M12, §14 defect 25). What was observed:
lumics_get_company_metricsreturned HTTP 500 on ordinary queries that carried a validpropertiesvalue —{"error":"Sorry, an error occurred. Please try again.","code":500}. It failed withlastMetric,isMonitored,minIntervalsandlimit, and withinterval=minuteandinterval=fiveMin. It succeeded withinterval=hour,interval=day,aggregateandalignTimeRange, and a minimal query returned 200 earlier the same day.lumics_summarize_company_metricsnever returned at all — over 90 seconds, with and withoutitemTypenarrowing. That is the same finding as the slowness below, seen from the other side.The device-scoped tools worked throughout, returning populated data in one to two seconds.
Browsing the vendor's own web application, a company dashboard load issued 57 API calls, including its "Top devices by CPU" and "Top devices by memory" widgets, and never once called
/api/v1/metrics/companies/. The vendor's own product does not use this endpoint for company-wide metrics.
This is intermittent and query-dependent, not a dead endpoint, and no cause has been established. It is a correlation measured on one tenant on one day. Both tools are still registered and still work for some queries — the decision was to document this, not to withhold the capability.
What to do with it:
Prefer the device-scoped route for anything estate-wide. List the devices you care about with
lumics_list_devices, then calllumics_get_device_metricsper device, orlumics_get_device_item_metricsfor one component. More calls, and it is the path that answered.If you do use the company-scoped tools, drop the parameters above and prefer
interval=hourorinterval=day.A 500 or a timeout there is not an absence of data. The tool descriptions say so, and a 500 from these two endpoints carries endpoint-specific guidance rather than the generic "this is not a problem with your arguments" — because on this route the arguments do correlate with the failure.
The server does not retry a 500 here. It never has (500 is not in its retryable status set), and the error now says so instead of implying a retry already happened.
lumics_summarize_company_metrics is slow, and a timeout is not an empty result
metrics/.../summarize aggregates every matching component in the company before it answers. It has
been measured taking over 90 seconds without returning, where the other metric endpoints answer
the same module and window in one to two seconds. Nothing in the vendor documentation mentions this.
This server gives that one endpoint a three-minute deadline of its own rather than the shared
LUMICS_TIMEOUT_MS, and attempts it exactly once — three attempts at three minutes is nine
minutes of silence, which from a client is indistinguishable from a hung server, and a retry cannot
help an endpoint that is slow because of how much work it is doing. On a large tenant it can still
time out. A timeout there is a timeout, not evidence that there is no data, and the error says
so. Narrow it with itemType, a tighter properties or a shorter window, or use
lumics_get_company_metrics, which reads the same module without the cross-component aggregation.
Everything the vendor documents about this endpoint's response — its envelope, the sum semantics,
the bucket shape — is unverified against live behaviour, because it never returned during the
contract run.
Metric endpoints require a resolution parameter
All four metric-data endpoints require either dataPoints or width (width wins if both are
set). The server supplies a sensible default so calls do not fail on a parameter a model has no
way to reason about, and the effective value is disclosed in the output. You can override it. Time
windows are accepted as ISO-8601 timestamps or a relative lookback and converted to the API's
epoch-millisecond fromMs/toMs internally.
The server sends no limit on a metric call unless you supply one. A metric result carries one
row per component per time bucket, and Lumics documents no ordering for those rows, so a limit
applied on your behalf would cut across time as well as across components and leave a series with
holes that look like real gaps in the monitoring data. Where a metric response is too large, the
output budget shortens it instead — from the end of the order Lumics returned, disclosed as such —
so what you lose is the tail of the series rather than scattered points.
metrics/summaries — the endpoint whose own vendor description advertises "top X lists of
devices" — accepts no limit, sort, or top parameter at all. Any ranking you see was
performed client-side by this server after retrieving the full set, and the output says so.
Timestamps must carry a timezone
from and to on every metric tool, and date on lumics_update_device_last_discovery, accept
two forms and only two:
a bare date,
2026-07-29, which means 00:00:00Z — UTC midnight;a timestamp with an explicit zone,
2026-07-29T14:00:00Zor2026-07-29T14:00:00+02:00.
A timestamp carrying a time but no zone — 2026-07-29T14:00:00 — is rejected, with a message
naming the fix. It is not guessed at, because Date.parse reads that form in the server's local
timezone while reading a bare date as UTC: the window moves by up to fourteen hours, and the
response notes report the moved window, so the wrong answer looks internally consistent. Epoch
milliseconds are also accepted, and a relative lookback such as 6h or 7d needs no timezone at
all.
to may not be in the future either, beyond 24 hours of clock skew. Monitoring data only exists
for the past, so a future end date does not widen the result — it only makes the reported window
wrong.
lumics_list_devices returns a compact projection by default
A full device record is around 1.9 kB, mostly its modules polling map, so the default
limit of 100 and the 25,000-character output budget cannot both be satisfied — a default call
would ask for a hundred devices and be able to show about thirteen. So lumics_list_devices applies
a default field projection: id, name, ipAddress, deviceType, collector, enabled,
maintenanceMode. Nothing is dropped from the device list by this; only fields are, and the
response says so.
Pass fields with the names you want to override it, pass fields: [] to get whole records, or
call lumics_get_device for one device in full. No other list tool projects by default.
A write is never retried after a transport failure
If the connection drops, times out, or delivers an incomplete body on a POST, PATCH, or DELETE,
the server makes one attempt and no more. The request may have been applied before the connection
died, and the transport cannot tell that apart from a request Lumics never saw, so a replay would
duplicate a record, double-apply a change, or hit a record the first attempt had already deleted. The
error text says the write may already have landed and instructs a verifying read rather than a retry;
read that as "go and look", not "it failed".
DELETE is in that set even though HTTP calls it idempotent. Idempotence guarantees the same state,
not the same answer: a replayed delete 404s, and not_found — "Lumics has no such resource" — is what
would surface, so a completed deletion would be reported as never having happened.
Retries on a status code are unchanged and still apply to every verb, 429 included: a status
proves the server answered, which removes the ambiguity.
The HTTP transport is not in 0.1.0
LUMICS_TRANSPORT=http is refused at startup in this release. 0.1.0 ships stdio only
(ADR-001 decision 3) and opens no network
listener at all; Streamable HTTP is decision 4 of the same ADR and is scheduled for v0.2. The
listener code is in the tree so that v0.2 is additive, but it is not reachable from configuration
here, and the five LUMICS_HTTP_* variables are documented for forward reference only.
Unset LUMICS_TRANSPORT, or set it to stdio, and connect over stdio from your MCP client.
Raw Mongo query passthrough is deliberately not exposed
The Lumics metric endpoints accept componentQuery (a raw Mongo query expression) and filters
(a filter object convertible to one). Neither is exposed as a tool argument in v0.1. Handing a
language model a raw database query language is a NoSQL injection and unbounded-query surface. The
typed itemType, isMonitored, and properties arguments cover the practical use cases. This is
a deliberate reduction of capability the vendor API offers, chosen on security grounds. If raw
passthrough is ever added it will go behind an explicit opt-in flag.
v0.1 supports the companies context only
Several Lumics endpoints take a :context path segment whose legal values vary per endpoint —
companies, admingroups, and in two read cases system. v0.1 supports companies only.
admingroups and system are not offered. Note also that the vendor documentation never
specifies what context id to supply for system, so it is documented-but-unspecified and not safe
to rely on.
Rate limits are undocumented
The API documents a 429 Too Many Requests response but publishes no limits, no windows, and no
headers. The server is conservative about concurrency and honours Retry-After when present, but
we cannot tell you where the ceiling is because Lumics does not say.
Other API-level caveats
Documented status codes include
304 Not Modifiedand423 Locked, but no endpoint documents its own caching or locking semantics.Identifier field names are inconsistent upstream: some responses return
id, others_id, and component objects leak Mongoose internals (__t,__v).No enumerations are published for
deviceType,role,moduleType, or componentitemType.lumics_list_component_typesis the lookup for thecomponentTypekey the component tools take, but not for the metric tools'itemType— it returns plural aliases the metrics API rejects. Build anitemTypefromlumics_get_device_definition_components, or copy thetypefield off a row a metric call returned. Metric property names are enumerable from one endpoint only, and only for device-scoped metrics — see Metric tools require apropertiesargument.Every IPAM address route uses the singular
/ipsubnet/segment — all five of them. The vendor documents the plural/ipsubnets/for POST, PATCH and DELETE, and told implementers not to "fix" it. That is wrong: measured against the live API on 2026-07-31, the plural is not routed for any verb. This shipped as a defect in0.1.0, wherelumics_create_ipaddress,lumics_update_ipaddressandlumics_delete_ipaddressaddressed a path that does not exist and could never have succeeded. Fixed in 0.1.1; the two address reads were always correct. Detail indocs/reference/lumics-api-v1.md§0.5 and §14 defect 26.The vendor documentation does drift from live behaviour, and has been measured doing so. The first contract run against a live tenant, on 2026-07-30, contradicted it in seven places, four of them in the metric layer. Those measurements are recorded, dated and marked in
docs/reference/lumics-api-v1.md— §0 indexes them, §14 defects 17–23 carry the detail — with the vendor's original text left in place beside them, and the decisions taken about them are in ADR-003. Contract tests run against a live tenant before each release; see docs/RELEASE.md.
Release maturity
This is a 0.x release. The tool surface may change before 1.0, including breaking renames
and argument changes, which pre-1.0 semver permits with a minor bump and a changelog entry. Pin an
exact version if you depend on stability. The HTTP transport, Docker deployment, and .mcpb
bundle are not in v0.1.
Troubleshooting
It worked yesterday and now every tool fails
Your token expired. Lumics tokens default to expiresIn=86400 — one day — so this is the most
common failure by a wide margin, and it presents as an authentication error (unauthorized, HTTP 401) on every call rather than as anything that looks like expiry.
Mint a fresh token, update it wherever your client stores it, and restart the server. The token
is read once at startup; editing a config file is not enough on its own. If you find yourself doing
this daily, mint with a longer expiresIn deliberately rather than by accident, and read
Security first — there is no documented maximum, and a token that never expires is a
credential you will forget you issued.
The server started but almost no tools are there
LUMICS_COMPANY_ID is not set. Company-scoped tools are withheld from tools/list when there is
no company to scope them to, which leaves two tools in a default deployment — lumics_get_me and
lumics_get_device_definition_components. The server logs a warning on stderr saying so. Follow
First run: finding your company id.
Tools work but every list comes back empty
Check that LUMICS_COMPANY_ID is the company your token belongs to. A syntactically valid id
for a company your user cannot see returns empty results rather than an error, which reads as "you
have no devices". lumics_get_me reports the user the token authenticates as and the company it
belongs to; compare the two.
A call was refused with not_permitted and a companyId in the message
The call named a companyId other than LUMICS_COMPANY_ID, and LUMICS_ALLOW_CROSS_COMPANY is
off. Omit companyId to use the configured company. If you genuinely need another tenant, see
Cross-company access is off by default — it is an
operator setting and cannot be changed from a conversation.
A device list is truncated, or says it may be incomplete
Do not raise the limit. The Lumics API has no pagination, and a larger limit fetches records
the output budget then drops — you get fewer, not more. Pass a fields projection instead so each
record is small enough that more of them fit, narrow the query, or ask for a higher
LUMICS_MAX_OUTPUT_CHARS. See
lumics_list_devices returns a compact projection by default.
The same applies to a metric response: shrink it with properties, itemType, lastMetric or a
shorter window rather than with limit.
A metric call returned rows but no values
The property names are probably wrong. This is the most likely explanation, and it is far more likely than the metric genuinely having no data.
Lumics does not reject an unrecognised properties value. It answers HTTP 200 with the full row
count and an empty stats object on every row — a successful, complete-looking, empty answer that
is indistinguishable from a real absence of data except by inspection. This server inspects it: when
the paths you asked for resolve on none of the returned rows, the response says the names may be
malformed and does not present the result as a negative finding about your estate. Read that
note as "nothing was measured", not as "nothing is being collected".
What to check, in order:
Syntax. Each entry must be
<TypeGroup>.<metric>with no spaces —Calculated.cpu, notcpu. A value with no dotted entry at all is refused locally before any request is made.The type group. If the note says the group never appeared in any row's
stats, the group half is wrong.Calculated,RateandTimeTicksare what live tenants have shown;CounterandGaugedo not exist.The metric name. If the note says the group came back but held no such metric, the name half is wrong. Enumerate the real names with
lumics_get_metric_summarycalled with noproperties, and readdata.<class>[].stats.itemType, if you passed one. Lumics validates it beforeproperties, and a wrong value fails with400 Unknown component— which hides the properties problem rather than showing it.
If the names are right and the rows are still empty, the metric may genuinely not be collected on
those components. Confirm it in the Lumics UI before reporting it: the API gives no signal that
distinguishes the two. See
Metric tools require a properties argument.
A company metric call failed with a 500
Expected, unfortunately. /api/v1/metrics/companies/... returned 500 on ordinary queries when it was
measured, and the failures tracked specific parameters: drop lastMetric, isMonitored,
minIntervals and limit, and use interval=hour or interval=day rather than minute or
fiveMin. If it still fails, go device-scoped — lumics_list_devices, then
lumics_get_device_metrics per device — which is the route that answered in every run. The failure is
not evidence that the company has no data for that module. See
The company-scoped metric endpoint is unreliable.
A timestamp was rejected
Add an explicit zone: 2026-07-29T14:00:00Z, or an offset such as 2026-07-29T14:00:00+02:00. A
bare 2026-07-29 is fine and means UTC midnight. A relative lookback like 6h avoids the
question entirely. See Timestamps must carry a timezone.
If the rejection says the value is in the future, that is to — it may not be later than now, give
or take 24 hours of clock skew.
A metric call was refused and named a device in another company
lumics_get_device_metrics and lumics_get_device_item_metrics take no companyId, so they check
the pin by reading the device inside LUMICS_COMPANY_ID first. A not_permitted refusal there means
the device is in a different Lumics company, or does not exist at all; no metric request was made.
Pick a device with lumics_list_devices, which lists only the configured company. See
Cross-company access is off by default.
The server refuses to start and says LUMICS_BASE_URL must use https:
Your LUMICS_BASE_URL is a plain http: URL for a host that is not loopback. The Lumics token is a
bearer credential sent on every request, so over plaintext to a remote host it crosses the network in
the clear and is readable by anything on the path. Switch the URL to https:.
Plain http: is accepted only for 127.0.0.1, localhost, or [::1], which covers a local
development proxy. The comparison is exact, so a host like localhost.example.invalid gets no
exemption. No environment flag widens this — if a deployment genuinely needs plaintext to a remote
host, terminate TLS in front of Lumics or tunnel to loopback.
The server refuses to start with LUMICS_TRANSPORT=http
That is deliberate in 0.1.0, not a bug. See The HTTP transport is not in 0.1.0.
The server exits immediately with a configuration error
Configuration is validated at startup and the message names the variable and what to do about it. Read it on stderr — most clients hide server stderr behind a "server disconnected" message, so check your client's MCP log rather than concluding the server is broken. Nothing is requested and no credential is read when validation fails.
I need to see what the server is actually doing
Turn the verbosity up with LUMICS_LOG_LEVEL=debug and restart. Diagnostics go to stderr —
stdout is the MCP protocol channel and carries nothing else — so look in your client's MCP log.
debug adds a record per tool call: which tool, how long it took, how many characters it returned,
whether a list reached the limit it asked for, and how many items the output budget dropped. That is
what to reach for when a tool returns less than you expected. The default is info; warn and
error are quieter, and silent turns the stream off entirely for a supervisor that treats any
stderr output as a fault.
Tool reference
Tools are named lumics_<action>_<resource> and grouped by resource below. Every tool carries an
operation classification — Read, Create, Update, Admin, or Destructive — as
required by the Engineering OS security standard, mapped to MCP tool annotations so clients can
present the right confirmation prompts.
39 tools exist; 37 are registered in a default deployment. The two missing are
lumics_batch_update_devices and lumics_revoke_tokens, each behind its own environment flag. With
LUMICS_READ_ONLY=1 only the Read rows are registered, which is 20 tools. Without
LUMICS_COMPANY_ID only the two tools that need no company are registered — see
First run.
Devices
Tool | Class | Gate |
| Read | — |
| Read | — |
| Create | — |
| Update | — |
| Update | — |
| Admin |
|
| Destructive | — |
Bulk device update is classified Admin, not Update: one call rewrites arbitrary fields across
many devices and the Lumics API documents no cap on how many, so it carries destructiveHint and
requires a confirm argument alongside its environment flag. See
ADR-002.
Collectors
Tool | Class | Gate |
| Read | — |
| Read | — |
| Create | — |
| Update | — |
| Destructive | — |
Components
Tool | Class | Gate |
| Read | — |
| Read | — |
| Read | — |
| Read | — |
| Update | — |
IPAM — subnets
Tool | Class | Gate |
| Read | — |
| Read | — |
| Create | — |
| Update | — |
| Destructive | — |
IPAM — addresses
Tool | Class | Gate |
| Read | — |
| Read | — |
| Create | — |
| Update | — |
| Destructive | — |
IPAM — groups
Tool | Class | Gate |
| Read | — |
| Read | — |
| Create | — |
| Update | — |
| Destructive | — |
Metrics
Tool | Class | Gate |
| Read | — |
| Read | — |
| Read | — |
| Read | — |
| Read | — |
Identity
Tool | Class | Gate |
| Read | — |
| Admin |
|
Per-tool argument detail lives in docs/TOOLS.md, which ships alongside
the implementation — arguments, types, defaults, enums, output shape, and the endpoint each tool
maps to. The table above is structural only;
treat docs/TOOLS.md and the server's own tools/list response as authoritative for signatures,
and remember that names may change before 1.0.
Development
Requires Node.js 20 or newer.
git clone https://github.com/ZenixSolutions/lumics-mcp.git
cd lumics-mcp
npm ci
cp .env.example .env # then set LUMICS_TOKEN, and LUMICS_COMPANY_ID once you have it
npm run build
node --env-file=.env dist/index.jsThe --env-file flag is how the .env gets read: the server does not look for one itself. See
The server does not read a .env file.
Script | What it does |
| Compile TypeScript to |
| Compile in watch mode |
| Run the built server ( |
|
|
| ESLint |
| ESLint with |
| Prettier write |
| Prettier check — what CI runs |
| Vitest, single run |
| Vitest in watch mode |
| Vitest with V8 coverage |
| Contract tests against a live tenant (needs a real token) |
| typecheck + lint + format:check + test — run before opening a PR |
npm run validate is the gate CI enforces. Run it locally first.
Contract tests are opt-in because they make real API calls. They are read-only, but they consume quota against a live tenant. See docs/RELEASE.md for when they are required.
Contributing
Contributions are welcome. This repository is governed by the Engineering OS framework, which means meaningful changes go through issue, discovery, approval, independent review, and validation before merge — and that no author may be the sole reviewer of their own work. Read CONTRIBUTING.md before you start writing code; it will save you a rejected pull request.
CONTRIBUTING.md — contribution flow, branch and commit conventions
CODE_OF_CONDUCT.md — Contributor Covenant 2.1
SECURITY.md — vulnerability reporting and hardening guidance
CHANGELOG.md — release history
docs/DEPENDENCY_POLICY.md — dependency rules
docs/RELEASE.md — release and tagging policy
docs/DECISION_LOG.md — approved decisions and recorded exceptions
docs/rfc/ and docs/adr/ — approved designs and architectural decisions
docs/reference/lumics-api-v1.md — the captured Lumics API contract this server targets
License
MIT © 2026 Zenix Solutions
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseBqualityDmaintenanceThis project provides a Model Context Protocol (MCP) server that allows language models and other MCP clients to interact with a SuzieQ network observability instance via its REST API.Last updated24MIT
- AlicenseAqualityAmaintenanceMCP server for Domotz network monitoring and management. Enables AI assistants to query network devices, alerts, and topology via the Domotz API.Last updated254Apache 2.0

oitvoip-mcp-serverofficial
FlicenseBqualityCmaintenanceA Model Context Protocol (MCP) server that provides AI agents with seamless access to the NetSapiens VoIP platform for user management, call analytics, and system administration.Last updated234
Pingera MCP Serverofficial
Alicense-qualityDmaintenanceA Model Context Protocol (MCP) server for the Pingera monitoring service, providing seamless integration between AI models and monitoring data.Last updated1MIT
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Self-hosted MCP gateway: turn any API, database or MCP server into AI connectors — no code.
MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ZenixSolutions/lumics-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server