Skip to main content
Glama
GTalksTech
by GTalksTech

Auvik MCP Server

A Model Context Protocol server for Auvik, the cloud network monitoring platform. It gives any MCP-capable AI agent — Claude Code, Claude Desktop, Copilot Studio, or your own — fifteen tools for answering real network-operations questions about an Auvik tenant: alerts (raw and rolled up for triage), device and interface inventory, the subnets and VLANs the collectors actually observe, hardware component health, which device configurations changed this week, who opened a remote session to a switch, vendor end-of-support status, and interface/device time-series. Fourteen of the fifteen are reads. The fifteenth — dismissing an alert — is the only write this server can perform, and it is gated behind a required reason, a read-before-dismiss rule, a duplicate-write guard, and an audit record written in two places.

"What fired overnight, grouped so the collector flapping doesn't bury the real fault?" · "Is core-sw-01 online, and when was it last seen?" · "Which ports on that switch are down?" · "Did any device configuration change this week?" · "Who was on that router before it broke?" · "What are we running that is past end-of-support?"

Two ideas run through the whole thing.

"We found nothing" and "we couldn't reach Auvik" are never the same answer. Every tool returns data_status: "ok" | "unavailable" | "refused". ok means the answer is real, and a zero is a real zero. unavailable means the upstream call failed — and the response then carries no data keys at all, so there is no empty list for a model to read as "all clear". refused means the server declined to make the call: a guardrail, or input it cannot accept.

It is built from what the API does, not from what its documentation says. Ten things worth knowing before you build against Auvik — five of them places where the live API flatly contradicts Auvik's own published OpenAPI file — are documented in docs/API-DEVIATIONS.md, each with what the document claims, what the API actually returns, and what this server does about it. Several of them silently produce a wrong answer if you take the document at its word.

Tools

Triage

Tool

What it answers

auvik_alert_summary

Start here. One row per (alert, entity, site) with active/resolved counts, worst severity seen, and a flapping flag — a raw list is often dominated by repeated collector reconnects

auvik_list_alerts

The alerts themselves, filtered by severity, status, device, site, name/description substring, and time window

auvik_get_alert

One alert in full — and the read that unlocks dismissing it

auvik_dismiss_alert

The only write. Dismiss one alert, with a required reason, recorded to an audit trail

Inventory

Tool

What it answers

auvik_find_device

Find devices by name, IP, type, vendor, online status or site

auvik_get_device

One device in full: management/discovery state, topology neighbours, interface and component counts, newest config backup

auvik_list_interfaces

A device's interfaces: type, MAC, negotiated speed, duplex, IPs, operational and admin state

auvik_list_networks

Subnets, VLANs and wifi networks the collectors observe — with scan_status, which separates "networks here" from "networks visible from here"

Health

Tool

What it answers

auvik_component_health

Fans, PSUs, disks, memory, CPUs — defaults to "is anything degraded or failed?" across the estate

auvik_config_changes

Which devices had a configuration change in the window, from backup metadata

auvik_remote_access_audit

Who opened terminal, tunnel or remote-browser sessions to which device, newest first

auvik_device_lifecycle

End-of-sale / end-of-support / warranty per device, problems only by default

Statistics

Tool

What it answers

auvik_interface_stats

Interface time-series: bandwidth, utilization, packet loss, packet discard

auvik_device_stats

Device time-series: CPU, memory, storage, bandwidth

auvik_device_availability

Uptime and outage series per device

All three statistics tools take summary_only=true for a compact {samples, min, max, avg, last} per column instead of every row, and report series_total / series_with_data / series_empty so an empty series is visibly a real zero rather than a missing one.

Related MCP server: Autotask MCP Server

Design principles

These are enforced across every tool, because they are what make an agent's answers trustworthy.

  • A zero is never confused with an outage. Covered above: ok / unavailable / refused, and unavailable responses carry no data keys. The HTTP client's contract is that every failure raises; it never returns an empty structure that could be mistaken for an answer.

  • refused is its own status, and it is not a failure. A refusal is the server declining on purpose: dismissing an alert nobody read, retrying a dismissal inside the propagation window, or asking for a fleet-wide list of every healthy CPU core. Collapsing that into unavailable would tell an agent to retry something it should not.

  • One write, one call site. client.post( appears exactly once in the shipped server — in tools/alerts.py — and a test enforces that, scanning server.py, tools/ and scripts/ (the scripts too, because those get run by hand against production). There is no passthrough or "raw request" tool, so no new write can appear by accident.

  • Responses stay under 500 KB. Every list tool has limit (max 200) and reports returned_count and truncated, plus a full-match count — total_count on most, and group_count / alert_count on auvik_alert_summary, which counts groups and alerts rather than rows. The statistics tools scale their per-series row budget down as the number of series goes up, and say so in detail, so a truncation is always visible.

  • Chat-client-safe schemas. Primitive parameter types only, a default on every optional parameter, no Optional/unions, no $ref, no exclusiveMinimum — the rules Copilot Studio's connector needs. A violation makes some clients drop the tool silently at import, so tests/test_schema_rules.py checks the generated schemas rather than trusting review.

  • Vendor text is passed through, never invented. Where Auvik returns something ugly — a raw alert template, a renamed statistic, a Title-Case enum — this server reports it exactly as sent and gives you the context to interpret it. It never synthesises prettier text and presents it as vendor data.

Install

Requires Python 3.12 and an Auvik account with an API key (Admin → Integrations → Auvik API).

git clone https://github.com/GTalksTech/auvik-mcp.git
cd auvik-mcp
py -3.12 -m venv .venv
.venv\Scripts\python.exe -m pip install -r requirements.txt -c constraints.txt

On macOS/Linux, python3.12 -m venv .venv and .venv/bin/python. Versions are pinned and resolved together with constraints.txt — install with -c or you will get a different, untested set.

For development (adds pytest):

.venv\Scripts\python.exe -m pip install -r requirements-dev.txt -c constraints.txt

Credentials

Two ways, checked in this order:

# A: environment variables (the only option inside a container)
$env:AUVIK_USERNAME = "you@example.com"     # your Auvik login email
$env:AUVIK_API_KEY  = "<your Auvik API key>"
$env:AUVIK_REGION   = "us1"                 # optional; see below

# B: the OS keyring (Windows Credential Manager / macOS Keychain / libsecret)
py -3.12 -c "import keyring,getpass; keyring.set_password('auvik-api-key', input('auvik login email: '), getpass.getpass('api key: '))"

AUVIK_REGION is the subdomain of your Auvik URL. If you sign in at https://yourcompany.us2.my.auvik.com, it is us2, and the API host becomes https://auvikapi.us2.my.auvik.com. The default is us1, which is the region Auvik's own documentation uses — set it explicitly if that is not yours. This is worth getting right first: a wrong region with a perfectly good key fails in a way that looks like a bad key, not a bad hostname. A stdio launch passes no environment at all, so set it in the keyring path's environment or accept the default deliberately.

Scope the key deliberately too. Reads need Device, Interface, Network, Component, Configuration, Statistics, Lifecycle and Tenants access. auvik_dismiss_alert additionally needs Alerts: Edit — issue a key without it if you never want the write to be possible, and the vendor will refuse the POST regardless of what any code asks for.

The key is never logged. Startup emits an 8-character SHA-256 fingerprint of it and nothing else; the audit records carry a caller label, never a credential.

Run it

Claude Code

claude mcp add auvik -- "$PWD\.venv\Scripts\python.exe" "$PWD\server.py" --stdio
claude mcp list      # auvik should appear and report connected

Then ask, in a session: "What Auvik alerts fired in the last 24 hours?"

Tool discovery and /health never depend on Auvik being reachable. With no credential configured — or with Auvik down — the server still starts, still lists its fifteen tools, and every call answers data_status: "unavailable" with the reason in detail. That is correct behaviour, not a bug.

What the process does do at startup is kick off a background warm thread that pre-fetches the device inventory, because alert projections resolve device IDs to names through it and paying that on the first question is slow enough to be noticeable. The warm is best-effort by contract: it runs off the request path, and if it fails — no credential, Auvik unreachable — it leaves the server exactly as cold as it was, so the first real tool call still tries afresh.

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "auvik": {
      "command": "/absolute/path/to/auvik-mcp/.venv/bin/python",
      "args": ["/absolute/path/to/auvik-mcp/server.py", "--stdio"],
      "env": {
        "AUVIK_USERNAME": "you@example.com",
        "AUVIK_API_KEY": "<your Auvik API key>",
        "AUVIK_REGION": "us1"
      }
    }
  }
}

On Windows, use \\-escaped paths and .venv\\Scripts\\python.exe.

HTTP mode

$env:MCP_INBOUND_API_KEY = "<a long random string>"
.venv\Scripts\python.exe server.py          # streamable HTTP on $PORT (default 8080)

Clients send that key in an X-API-Key header; GET /health is exempt and needs neither the key nor Auvik. Unset MCP_INBOUND_API_KEY means every request except /health is rejected — fail-closed, deliberately.

A Dockerfile is included. Read docs/HOSTING.md before deploying it: there is one load-bearing constraint, run exactly one replica, and it is explained there.

What Auvik's API actually does

Full catalogue in docs/API-DEVIATIONS.md. The three you are most likely to hit:

1 · filter[thruTime] is required on the /v1/stat/* calls — the OpenAPI file marks it optional and says it "defaults to current time". It does not. Omitting it is an HTTP 400 on every statistics endpoint this server uses (deviation D-4, which also says exactly which ones were tested). This server always sends it, so you never have to; but if you are writing your own client, this is the one that takes out all your statistics calls on day one.

2 · Statistics data is a list, and timestamps are unix_mins. The OpenAPI file types data as an object; the live API sends a list of series (D-1). And column 0 of every statistics row is labelled Recorded At with unit unix_minsMINUTES since the epoch. Read it as seconds and every point lands in January 1970; as milliseconds, in the 1970s. Neither looks obviously wrong on a chart. Read the units array: it names the scale of every column, and the row shape is not fixed across statistics.

3 · Read an alert's description, not its name. A large fraction of real alert names come back as Auvik's raw, un-interpolated template — literally $deviceInterfaces.name Down on $system.name (D-6). That is Auvik's own stored data. The description field is fully interpolated and is the one to put in front of a human. Both are in every projection, and auvik_list_alerts matches its name= filter against the description as well.

Two more worth knowing. Auvik renames the statistic you asked for in its reply (utilizationinterfaceUtilization), so each series reports both stat (Auvik's) and requested_stat (yours), and the mismatch is normal (D-8). And a site is the Auvik tenant's domainPrefix, not a place name — so the Boston office might be acme-bos, and searching for "boston" finds nothing. Every site-aware tool lists the valid values under sites, and an unmatched site comes back as a real zero whose detail names the ones that exist.

Two of those sites lists differ, deliberately. auvik_find_device reports the sites that actually have devices, because that is what it searches. Everything else reports every tenant from /v1/tenants. A tenant can hold alerts and no devices, so asking the alert tools to reuse the device-derived list would make them call a real site unknown — a fabricated zero produced by answering with a list built for a different question.

The write path

auvik_dismiss_alert is the only non-GET this server can make. In order, first failure refuses and no POST is issued:

  1. a reason is required;

  2. the alert must have been returned by a read in this process — a summary does not count, since the model has seen a group, not an alert;

  3. it must not already have been dismissed by this process. This is the rule that actually prevents a duplicate write, because the re-read below is eventually consistent (D-7) and would say "not dismissed" for a couple of minutes. It is checked before the re-read so that a retry is refused locally and deterministically — including while Auvik is unreachable, where the re-read would instead fail and report unavailable, the one status that invites another retry;

  4. re-read from Auvik; already dismissed → refused;

  5. POST;

  6. audit.

Every successful dismissal is recorded twice: a line in audit/dismissals.jsonl and a dismissal_audit line on the process log stream (stderr in stdio mode, stdout in HTTP mode). Both carry the same record — timestamp, alert id, name, severity, entity, device name, the caller's reason, and the caller key label; never the key itself.

audit/dismissals.jsonl is gitignored — it holds real operational evidence — and it is append-only; nothing here rewrites or prunes it. The log copy exists because the file cannot be trusted to survive: on container-local disk it is ephemeral, and a read-only root filesystem stops the append entirely. The file append is best effort. If it fails, the dismissal still returns ok — the write really happened, and reporting unavailable would invite a retry that dismisses twice — with a warning in detail telling you to capture the record from the process log.

Auvik's dismissal is eventually consistent: a re-read within a couple of minutes can still report dismissed: false. That is propagation delay, not failure. The tool says so in detail, and a retry inside that window is refused rather than issuing a second write.

Testing

.venv\Scripts\python.exe -m pytest -q

372 tests, entirely offline. The HTTP client is replaced by a fake serving JSON:API fixtures, and an autouse fixture makes both a real credential load and a real socket raise — so the suite cannot reach the network even by accident, and cannot be made flaky by a workstation that happens to have a credential in its keyring.

The committed fixtures under tests/fixtures/synthetic/ are entirely invented and are regenerated by tests/build_synthetic_fixtures.py. They deliberately carry production's spelling — Title-Case lifecycle enums, prefixed statType values, an alert with no entity, an interface stats response where 13 of 17 series are empty — so the offline tier cannot quietly disagree with the live API on the things D-5, D-8 and D-9 are about.

There is an optional second tier: tests/record_fixtures.py records real responses to tests/fixtures/recorded/ (gitignored), and AUVIK_FIXTURES=recorded pytest runs the same suite against them. A set of tier-invariant tests runs on both and skips rather than passes vacuously when a tier has no records for an endpoint — because a test that cannot fail is not a test.

scripts/live_smoke.py and scripts/live_read_all.py exercise the read tools against a real tenant. Both are GET-only and issue no writes of any kind.

Security notes

  • One write, and it is scoped. client.post( exists at a single call site, enforced by a test. Issue an API key without Alerts: Edit and the write becomes impossible at the vendor as well.

  • Read before dismiss. Nothing can be dismissed that this process has not read, so an agent cannot act on an alert id it invented or carried over from somewhere else.

  • Auditable by construction. Two copies of every dismissal record, one of which survives the container, both including the caller-supplied reason.

  • The credential never lands in a log, a fixture or a response. Startup logs an 8-character fingerprint. AuvikCredentials.__repr__ is overridden so the key cannot leak through an exception traceback or a debugger.

  • Inbound auth fails closed in HTTP mode, with a constant-time comparison, and /health is the only exempt path.

See also

A sibling server built on the same envelope contract and skeleton: GTalksTech/sonicwall-mcp.

License

MIT — see LICENSE.

One exception. reference/auvik-openapi-v1.json is Auvik's published OpenAPI document, reproduced here unmodified so the deviations above can be checked against their source. It is Auvik Inc.'s copyright, not mine, and the MIT grant above does not extend to it. If you are redistributing this repo, check Auvik's terms for that file, or drop it — nothing in the code or the test suite depends on its presence.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides AI assistants with direct access to multi-vendor network devices for tasks like configuration management, health checks, and topology discovery through 35 specialized tools. It enables natural language control over platforms including Cisco, Juniper, and Nokia using SSH, NETCONF, and SNMP protocols.
    11
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Provides AI assistants with direct access to Autotask PSA for MSP operations. Enables natural language interaction for ticket management, time logging, company lookups, project tracking, and billing review through 39 comprehensive tools.
    100
    45
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to interact with NinjaOne, providing tools for managing devices, organizations, alerts, and tickets. Uses a decision tree architecture to load domain-specific tools on demand.
    Apache 2.0

Latest Blog Posts

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/GTalksTech/auvik-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server