nexthink-mcp-server
v3.0.0
Published
Enterprise-grade Model Context Protocol (MCP) server exposing Nexthink DEX telemetry (saved NQL API queries), Remote Actions, and engagement workflows to LLM agents. Targets MCP spec 2025-11-25 with pluggable auth for every Nexthink credential type.
Maintainers
Readme
Nexthink MCP Server
A Model Context Protocol (MCP) server that
exposes Nexthink Digital Employee Experience (DEX) telemetry and automation to
LLM agents. Built against the MCP 2025-11-25 stable spec (structured tool
output, tool annotations, resources) on @modelcontextprotocol/sdk v1.30.
+-------------------+ MCP (JSON-RPC / stdio) +-------------------------+ OAuth2 / Bearer / Basic +-----------------------------+
| LLM / Agent | <------------------------> | Nexthink MCP Server | <-------------------------> | Nexthink Infinity |
| (Claude / custom) | structured tool output | auth -> retry -> domain| HTTPS, region-partitioned | (NQL, Act, Workflows APIs) |
+-------------------+ +-------------------------+ +-----------------------------+What it does
- Authentication — all four Nexthink credential forms, selected by one env var: OAuth2 client credentials (Basic-header and form-body), pre-issued bearer, and legacy HTTP Basic. See Authentication.
- Token handling — in-memory OAuth token cache with proactive refresh and a single-flight guard, so concurrent tool calls don't stampede the token endpoint.
- Retries —
429/5xx/network errors retry with full-jitter exponential backoff, honoringRetry-After, alongside per-request timeouts and a one-shot 401 → token-refresh → retry.4xx(e.g. an unknown query id) is not retried; the error body is surfaced back to the model for self-correction. - Structured output — every tool declares a Zod
outputSchemaand returns validatedstructuredContent, with a JSON text fallback. - Guardrails —
destructiveHintannotations for human-in-the-loop gating, an optional read-only mode, and a Remote Action allow-list. - Logging — single-line JSON to stderr (never stdout), with secret redaction.
- Region handling — derives the
*.api.<region>.nexthink.cloudbase and<instance>-login.<region>...token endpoint from instance + region.
Documentation
Everything referenced here either ships inside this package or is a public URL.
Shipped in the package, alongside dist/:
| File | Contents |
| --- | --- |
| docs/CAPABILITIES.md | Full capability matrix: every tool's inputs, structured output, MCP annotations, and the exact Nexthink endpoint it calls; auth matrix; guardrails; what's deliberately not implemented. |
| docs/RESEARCH.md | The sourced API findings behind the implementation — real request/response contracts with citations, and an explicit statement of what remains unverified. |
| CHANGELOG.md | Version history, including the 3.0.0 breaking changes. |
After installing, read them from the package directory:
npm view nexthink-mcp-server # registry metadata
npm pack nexthink-mcp-server # fetch the tarball, then extract
tar -xzf nexthink-mcp-server-*.tgz && ls package/docsOr, if it's already installed as a dependency:
cat node_modules/nexthink-mcp-server/docs/CAPABILITIES.mdExternal references:
- Model Context Protocol — https://modelcontextprotocol.io
- MCP
2025-11-25specification — https://modelcontextprotocol.io/specification/2025-11-25/changelog - Nexthink NQL API — https://docs.nexthink.com/api/nql
- Authoring NQL API queries (where Query IDs come from) — https://docs.nexthink.com/platform/user-guide/administration/content-management/nql-api-queries
- Nexthink API credentials & auth token — https://docs.nexthink.com/API/getting-authentication-token
Important: NQL queries run by ID, not by text
Nexthink's public API cannot execute ad-hoc NQL. Both /api/v2/nql/execute
and /api/v1/nql/export accept only {queryId, parameters}, where queryId
identifies a query an administrator saved in the Nexthink web interface under
Administration → Content management → NQL API queries. Saving assigns an
immutable Query ID matching ^#[a-z0-9_]{2,255}$; the API then replays that
query, substituting only the where-clause parameters it declares.
Practically, this server lets an agent run a curated catalog of queries rather than compose new ones:
- An administrator authors the query in the web UI and notes its Query ID.
- The agent calls
execute_nql(query_id="#…", parameters={…}).
There is no API to list saved queries, so supply the ids your agent may use in its prompt or configuration. New questions need a new saved query. Row limits, time windows and projections are baked into the saved query and cannot be overridden at call time.
Upgrading from 2.x — breaking. Versions before 3.0.0 exposed a
queryinput and POSTed{query: "<NQL text>"}. The API never accepted that, so NQL calls could not succeed against a real tenant. In 3.0.0:execute_nql/export_nql_asynctakequery_id+parametersinstead ofquery;execute_nql'slimitbecamemax_rows(client-side trim only);run_remote_actionreturnsrequest_idinstead ofexecution_id/status; andtrigger_workflowreturnsrequest_uuid+execution_uuids[].
Tools
| Tool | Kind | Description |
| --- | --- | --- |
| execute_nql | read-only | Run a saved NQL query by id (with optional parameters); normalized rows (handles v1 tabular and v2 object responses). |
| export_nql_async | read-only | Schedule a bulk async export of a saved NQL query → export id. |
| get_nql_export_status | read-only | Poll an export by id; returns the download URL when COMPLETED. |
| run_remote_action | destructive | Trigger a Remote Action on devices (by Collector id). |
| trigger_workflow | destructive | Trigger an IT workflow / engagement campaign on devices and/or users. |
Destructive tools are hidden entirely when NEXTHINK_READ_ONLY=true.
Example
Given a saved query #high_crash_devices whose where-clause declares
$binary_name:
// execute_nql
{ "query_id": "#high_crash_devices", "parameters": { "binary_name": "chrome.exe" } }max_rows is also accepted, but it only trims rows client-side to protect the
model's context — it does not change the query the API runs.
Resources
| URI | Description |
| --- | --- |
| nexthink://schema/nql-reference | How saved queries and parameters work, plus NQL syntax, time clauses, aggregations, domains, and the device.collector.id tip for remote actions. |
Authentication
Nexthink Infinity's public API uses OAuth 2.0 client-credentials. This server
supports every credential-presentation form via NEXTHINK_AUTH_TYPE:
| NEXTHINK_AUTH_TYPE | Required vars | Notes |
| --- | --- | --- |
| oauth2_basic (default) | NEXTHINK_CLIENT_ID, NEXTHINK_CLIENT_SECRET | Official method: id:secret in the HTTP Basic header, scope=service:integration. |
| oauth2_post | NEXTHINK_CLIENT_ID, NEXTHINK_CLIENT_SECRET | Credentials in the form body (client_secret_post). |
| bearer | NEXTHINK_BEARER_TOKEN | Pre-issued/vaulted token; no refresh. |
| basic | NEXTHINK_USERNAME, NEXTHINK_PASSWORD | Legacy/on-prem classic Web API. |
The OAuth token endpoint is derived from instance + region, or set explicitly
with NEXTHINK_TOKEN_URL.
Configuration
Every variable the server reads:
| Variable | Required | Description |
| --- | --- | --- |
| NEXTHINK_INSTANCE + NEXTHINK_REGION | yes¹ | Instance name + region (us/eu/pac/meta); derives all URLs. |
| NEXTHINK_API_BASE_URL | yes¹ | Explicit API base (overrides derivation; for proxies/on-prem). NEXTHINK_INSTANCE_URL is accepted as an alias. |
| NEXTHINK_AUTH_TYPE + its vars | yes | See Authentication. Default oauth2_basic. |
| NEXTHINK_READ_ONLY | — | true hides the destructive tools. |
| NEXTHINK_ALLOWED_ACTIONS | — | Comma-separated Remote Action allow-list. |
| NEXTHINK_HTTP_TIMEOUT_MS / NEXTHINK_MAX_RETRIES / NEXTHINK_RETRY_BASE_MS / NEXTHINK_RETRY_MAX_MS | — | Reliability tuning. |
| NEXTHINK_LOG_LEVEL | — | debug/info/warn/error. Structured JSON to stderr. |
| NEXTHINK_SCOPE | — | OAuth scope. Default service:integration. |
| NEXTHINK_TOKEN_URL | — | Explicit OAuth token endpoint (overrides derivation). |
| NEXTHINK_NQL_EXECUTE_PATH | — | Pin the execute endpoint. Default /api/v2/nql/execute; set to /api/v1/nql/execute only for pre-v2 compatibility. |
¹ Provide either NEXTHINK_INSTANCE+NEXTHINK_REGION or NEXTHINK_API_BASE_URL.
Defaults: NEXTHINK_HTTP_TIMEOUT_MS=30000, NEXTHINK_MAX_RETRIES=3,
NEXTHINK_RETRY_BASE_MS=500, NEXTHINK_RETRY_MAX_MS=8000,
NEXTHINK_READ_ONLY=false, NEXTHINK_LOG_LEVEL=info. An empty
NEXTHINK_ALLOWED_ACTIONS means any Remote Action id is permitted.
Quick start
Published on npm — no clone or build needed. Register it with an MCP client (Claude Desktop shown; any stdio MCP client works the same way):
{
"mcpServers": {
"nexthink": {
"command": "npx",
"args": ["-y", "nexthink-mcp-server"],
"env": {
"NEXTHINK_INSTANCE": "your-instance",
"NEXTHINK_REGION": "eu",
"NEXTHINK_AUTH_TYPE": "oauth2_basic",
"NEXTHINK_CLIENT_ID": "your-client-id",
"NEXTHINK_CLIENT_SECRET": "your-client-secret"
}
}
}
}Requires Node.js ≥ 22 on the machine running the client. Running from a
checkout instead? Use "command": "node" with
"args": ["/abs/path/to/nexthink-mcp-server/dist/index.js"].
Development
npm install
npm run build
npm start # reads config from the environment
npm run dev # ts, no build stepTest
npm run typecheck
npm test # unit + integration (transform, config, auth, HTTP retry)
npm run test:smoke # builds, then drives the server over a real stdio MCP handshakeArchitecture
Module layout — the compiled equivalent ships as dist/, mirroring this tree:
src/
config.ts # env -> validated config; region-aware URL derivation
logger.ts # structured JSON logs to stderr, secret redaction
errors.ts # ConfigError / AuthError / NexthinkApiError (retryable flag)
auth/ # pluggable auth strategies (oauth basic|post, bearer, basic)
http/client.ts # retry + full-jitter backoff + Retry-After + 401-refresh
transform.ts # NQL v1 (tabular) + v2 (objects) -> normalized records
nexthink/client.ts # typed facade over NQL / Act / Workflows endpoints
server.ts # McpServer: registerTool (Zod in/out) + registerResource
index.ts # stdio entry; assembles the stackThe request path per tool call: auth provider → HTTP client (retry) → domain client → normalized result → structured tool output.
API references
API contracts here are implemented from Nexthink's published API models
(NqlApiExecuteRequest, NqlApiExportRequest, NqlApiStatusResponse, and the
remote-action / workflow ExecutionRequest+ExecutionResponse) and
cross-checked against two independent community SDKs. Primary sources:
- NQL API — https://docs.nexthink.com/api/nql
- Execute an NQL — https://docs.nexthink.com/api/nql/execute-an-nql
- Export an NQL — https://docs.nexthink.com/API/nql/export-an-nql
- Authoring NQL API queries — https://docs.nexthink.com/platform/user-guide/administration/content-management/nql-api-queries
- Remote actions API — https://docs.nexthink.com/API/remote-actions/remote-actions-api
Roll out against a non-production instance first, and start with
NEXTHINK_READ_ONLY=true so the destructive tools stay hidden until the read
path is confirmed against your tenant.
Scope note: this server targets Nexthink Infinity cloud (NQL). The older
on-prem V6 Engine exposes a separate, deprecated NXQL API that does accept
raw query text over a different host, port, and auth model. That API is out of
scope here and is not a workaround for the saved-query requirement above.
Security notes
- Least privilege: scope Nexthink API credentials to read-only NQL unless remote
actions are needed; combine with
NEXTHINK_READ_ONLYand/orNEXTHINK_ALLOWED_ACTIONS. - Human-in-the-loop: destructive tools are annotated so clients gate them behind approval.
- Secrets come from the environment only and are redacted from logs; never commit
.env.
License
MIT. The full text ships in the package as LICENSE.
