N-central MCP Server
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., "@N-central MCP Serverlist all devices in the Production org"
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.
N-central MCP Server
A Model Context Protocol server for N-able N-central — exposing the N-central REST API as MCP tools, resources, and prompts for use with any MCP-compatible client.
Features at a Glance
87 tools covering devices, device notes, organizations, users, custom properties, scheduled tasks, PSA integrations, maintenance windows, and reporting — full coverage of the N-central REST API
Three write modes:
read-only,write(default),full— controls which tools are exposedAuto-paginated bulk reports in CSV or JSON; per-endpoint concurrency caps tuned to N-central's documented limits
MCP Resources for live org-hierarchy context (
ncentral://org-tree) and per-entity lookups via templated URIsMCP Prompts for common audit and reporting workflows
Two transports: stdio (for Claude Desktop / local clients) and Streamable HTTP (for remote clients, MCP Inspector, etc.)
Single- or multi-tenant: self-host against one N-central (env credentials), or run one hosted server many users point at — each targeting their own N-central via per-request headers (
NC_MULTI_TENANT=1), with strict per-request credential isolation. See the Setup & Client GuideProduction-grade auth: JWT exchange with auto-refresh, hash-based bearer-token auth for the HTTP endpoint, CORS allow-list, rate limiting, audit log
Operability:
/healthzand/metrics(Prometheus text format) endpoints, structured audit logging, configurable retry/timeout/session caps
Related MCP server: appd-mcp
Quick Start
Prerequisites
Node.js ≥ 22.9 (uses the built-in
--env-file-if-existsflag andfetch)An N-central instance you can reach over HTTPS
A User-API JWT token generated in the N-central UI
1. Install dependencies
npm install2. Get your N-central JWT token
In the N-central UI: Administration → User Management → Users → [user] → API Access → Generate JSON Web Token
Best practice: Use a dedicated API-only user with least-privilege roles. The API user password rotates every 90 days — regenerate the JWT proactively to avoid 500 errors.
3. Configure your environment
cp .env.example .env
# Edit .env — set NC_SERVER_URL and NC_JWT_TOKEN at minimum.The most common variables (full list in .env.example):
Variable | Required when | Description |
| single-tenant | Your N-central URL, e.g. |
| single-tenant | User-API JWT from the N-central UI. Not needed in multi-tenant mode |
| hosted mode | Set to |
| multi-tenant | Comma-separated host suffixes a client may target — SSRF guard (exact or DNS-suffix match) |
| optional |
|
| HTTP mode only | Setting this enables HTTP mode (omit for stdio) |
| HTTP mode | Bearer token clients must present. Generate with |
| optional | Interface to bind. |
| browser clients | Comma-separated allow-list of origins |
Connecting a client? See the Setup & Client Guide for copy-paste config for Claude Code, VS Code, Claude Desktop, and Cursor — in both single- and multi-tenant modes.
Write modes
Mode | Tool count | Includes |
| 56 | GET endpoints only |
| 82 | Read tools + create/update tools (POST/PUT/PATCH) |
| 87 | Everything, including destructive tools: |
All write/destructive tools are audit-logged. Start in read-only, move to write once the integration is trusted, and reserve full for vetted automation.
4. Start the server
The server runs in stdio mode by default and switches to HTTP mode when MCP_PORT is set.
Option A — stdio (Claude Desktop / local clients)
Use the npm script (loads .env if present):
npm startOr wire it into Claude Desktop directly. Add to claude_desktop_config.json:
{
"mcpServers": {
"ncentral": {
"command": "node",
"args": ["--env-file-if-exists=/absolute/path/to/n-central-mcp/.env", "/absolute/path/to/n-central-mcp/index.js"],
"env": {
"NC_WRITE_MODE": "read-only"
}
}
}
}Option B — Streamable HTTP (remote clients, MCP Inspector)
Set MCP_PORT in .env (default 3100) and an MCP_API_KEY, then:
npm run start:http
# Listening at http://127.0.0.1:3100/mcp
# Health probe: http://127.0.0.1:3100/healthz
# Metrics: http://127.0.0.1:3100/metricsClients send Authorization: Bearer <MCP_API_KEY> on every request.
Option C — Docker
cp .env.example .env
# Edit .env: NC_SERVER_URL, NC_JWT_TOKEN, MCP_API_KEY are required for the HTTP listener.
docker compose up -dCompose maps 127.0.0.1:3100:3100 by default. To expose on the LAN, edit docker-compose.yml and ensure MCP_API_KEY is set.
5. Verify
# stdio mode — should print "Authenticated with N-central..." on first tool call.
# HTTP mode — should respond:
curl -s http://127.0.0.1:3100/healthz
# {"status":"ok","sessions":0}Multi-Tenant (Hosted) Mode
By default the server is single-tenant: it reads one NC_SERVER_URL + NC_JWT_TOKEN from
its environment. That's the right model for an MSP self-hosting it against a single N-central.
Set NC_MULTI_TENANT=1 to host one server that many users point at, each targeting a
different N-central server with their own JWT — supplied per request via headers in their
own MCP client config. (Intended for a centrally-hosted demo/eval box, not for routing third
parties' production credentials through infrastructure you don't control.)
# Hosted server (HTTP only — stdio cannot carry per-request headers):
NC_MULTI_TENANT=1 \
MCP_PORT=3100 \
MCP_API_KEY="$(openssl rand -hex 32)" \
NC_FQDN_ALLOWLIST=ncentral.com,n-able.com \
node index.jsEach user's MCP client config sends, per request:
{
"mcpServers": {
"ncentral": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer <MCP_API_KEY>", // gates access to THIS server
"X-NC-FQDN": "https://their-ncentral.example.com",
"X-NC-JWT": "<their N-central User-API JWT>"
}
}
}
}Isolation guarantees
One session = one tenant. Credentials are validated at session init (before the session exists); an invalid/missing header pair is rejected with
400. The tenant is then bound to the session for its lifetime — later header changes on the same session are ignored.No shared credential state. Tokens are keyed per tenant and resolved per request via
AsyncLocalStorage, so concurrent requests for different servers can never read each other's URL or token. The resource cache is tenant-scoped for the same reason.Memory only, auto-evicted. Tokens and cache entries live in memory and are dropped when the last session for a tenant closes. Nothing is persisted; JWTs are never logged.
SSRF guard.
NC_FQDN_ALLOWLISTrestricts which N-central hosts a client may target (exact or DNS-suffix match). Leave it unset only behind trusted network boundaries — the server warns at startup if it's empty.
Scaling. The event loop handles many concurrent users on one process (the work is I/O-bound —
waiting on N-central). If you outgrow one process, run replicas behind a load balancer with
sticky routing by mcp-session-id — StreamableHTTP sessions live in process memory, so a
session must return to the replica that created it.
Tip — multiple servers without hosting: a self-hoster who just needs to target several N-central servers from their own editor can skip multi-tenant mode entirely and define one stdio entry per server, each with its own
env: { NC_SERVER_URL, NC_JWT_TOKEN }.
Tools
Each tool is tagged with its required write mode:
🟢 read — available in every mode
🟡 write — requires
NC_WRITE_MODE=writeorfull🔴 destructive — requires
NC_WRITE_MODE=full
Pagination
Every list_* tool returns a single page by default (with pagination metadata: pageNumber, pageSize, totalItems, totalPages, _links). To retrieve every result across all pages in one call, pass all: true — the server will auto-paginate at 200 items per page (up to 40,000 items). For CSV/JSON exports over large datasets, use the matching report_* tool instead.
Devices (11)
Tool | Mode | Description |
| 🟢 | List all devices with pagination, sorting, and filter support |
| 🟢 | List devices under a specific org unit |
| 🟢 | Get a device by ID |
| 🟢 | Get service monitoring status for a device |
| 🟢 | Get hardware/software asset info for a device |
| 🟢 | Get warranty/lifecycle info for a device |
| 🟢 | Get appliance task info by task ID |
| 🟡 | Add a new device (customerId, networkAddress, longName, supportedOs, deviceClass required) |
| 🟡 | PUT — replace asset lifecycle/warranty info (all fields required) |
| 🟡 | PATCH — partially update asset lifecycle info |
| 🔴 | Delete a device by ID (optional |
Organizations (14)
Tool | Mode | Description |
| 🟢 | List all service organizations |
| 🟢 | Get a specific service org by ID |
| 🟢 | List customers (all or filtered by SO) |
| 🟢 | Get a specific customer by ID |
| 🟢 | List sites (all or filtered by customer) |
| 🟢 | Get a specific site by ID |
| 🟢 | List all organization units |
| 🟢 | Get a specific org unit by ID |
| 🟢 | Get licensing/usage limits for an org unit |
| 🟢 | List child org units for a parent |
| 🟡 | Create a new service organization |
| 🟡 | Create a new customer under a service org |
| 🟡 | Create a new site under a customer (PREVIEW) |
| 🟡 | Update licensing/usage limits for an org unit (PATCH) |
Scheduled Tasks (5)
Tool | Mode | Description |
| 🟢 | List all scheduled tasks across the environment |
| 🟢 | Get general info for a scheduled task |
| 🟢 | Get aggregated or per-device task status |
| 🟢 | List all scheduled tasks for a device |
| 🔴 | Run an Automation Policy / Script / MacScript on a device (direct support task) |
Custom Properties (9)
Tool | Mode | Description |
| 🟢 | List all custom properties for a device |
| 🟢 | Get a specific device custom property |
| 🟢 | Get default custom property for an org unit |
| 🟢 | List custom properties for an org unit |
| 🟢 | Get a specific org unit custom property |
| 🟢 | Get default value for an org unit custom property |
| 🟡 | Update a custom property value on a device |
| 🟡 | Update a custom property value on an org unit |
| 🟡 | Update the default value of an org-unit custom property (with propagation) |
Users & Access (10)
Tool | Mode | Description |
| 🟢 | List all users in N-central (global, not scoped by org unit) |
| 🟢 | Get details for the currently authenticated user |
| 🟢 | List users for an org unit |
| 🟢 | List user roles for an org unit |
| 🟢 | Get a specific user role |
| 🟢 | List access groups for an org unit |
| 🟢 | Get a specific access group by ID |
| 🟡 | Create a new user role for an org unit (PREVIEW) |
| 🟡 | Create a new org-unit-type access group |
| 🟡 | Create a new device-type access group |
Server Info & Discovery (6)
Tool | Mode | Description |
| 🟢 | Server/API version info, health, or extended system details |
| 🟢 | Current server time (useful for clock drift detection) |
| 🟢 | List all device filters |
| 🟢 | Retrieve an N-central report by ID |
| 🟡 | Extra server version info using supplied credentials |
| 🟡 | Invalidate the current N-central API session |
Registration & Software (4)
Tool | Mode | Description |
| 🟢 | Agent registration token for a site / customer / org unit |
| 🟢 | Generate an activation key for a device |
| 🟢 | List agent installer download URLs for a customer |
| 🟡 | Generate a software download link for a customer |
Maintenance Windows (4)
Tool | Mode | Description |
| 🟢 | List all maintenance windows for a device |
| 🟡 | Add a set of patch maintenance windows to a list of devices |
| 🟡 | Modify existing maintenance windows by ScheduleId |
| 🔴 | Delete maintenance windows by ScheduleIds |
PSA (10)
Tool | Mode | Description |
| 🟢 | Customer-mapping record by customer ID |
| 🟢 | All PSA mappings for a customer |
| 🟢 | Standard PSA companies for a customer |
| 🟢 | Contacts in a Standard PSA company |
| 🟢 | Sites in a Standard PSA company |
| 🟢 | List Custom PSA tickets |
| 🟡 | Validate Standard PSA credentials (TigerPaw 3.0 only) |
| 🟡 | Retrieve a Custom PSA ticket (POST — requires creds) |
| 🟡 | Create a new Custom PSA ticket |
| 🟡 | Update PSA mappings for a customer |
Device Notes (6)
Tool | Mode | Description |
| 🟢 | List all notes attached to a device |
| 🟡 | Add a note to a device |
| 🟡 | Add the same note to a list of devices |
| 🟡 | Update an existing note on a device |
| 🔴 | Delete a specific note on a device |
| 🔴 | Delete ALL notes on a device |
Reports (8)
The cross-entity and bulk aggregate reports. For simple lists, use the matching list_* tool with all: true and format: "csv" — those auto-paginate and CSV-export too. Bulk reports use per-endpoint safe concurrency (3-5); override with concurrency.
Tool | Mode | Description |
| 🟢 | Fan out a per-device call across an org unit — |
| 🟢 | Deduplicated users across an SO and all its customers. CSV default. |
| 🟢 | All devices under a service org (filters across all devices). CSV default. |
| 🟢 | Customers with sites and device counts (per-site and customer totals). CSV default. |
| 🟢 | Full SO → Customer → Site hierarchy flat table. CSV default. |
| 🟢 | All active issues for an org unit. CSV/JSON. |
| 🟢 | All job statuses for an org unit. CSV/JSON. |
| 🟡 | Submit a patch comparison report job (returns report ID) |
Resources
Resources provide live context to the client without requiring explicit tool calls. Hierarchical resources are cached for 60s by default — set NC_RESOURCE_CACHE_TTL_MS=0 to disable.
URI | Description |
| Full SO → Customer → Site hierarchy with IDs and names |
| Server health + version snapshot |
| Templated — full device record by ID |
| Templated — customer details by ID |
| Templated — org unit details by ID |
Prompts
Name | Description |
| Comprehensive customer/site report with org custom properties |
| Active issues and monitoring status across the environment |
| Find sites with missing or low device counts |
| Audit custom property consistency across all customers |
Resilience
Concern | Behavior |
Rate limits (429) | Auto-retry with exponential backoff on all methods (up to 3 attempts) |
Unauthorized (401) | Auto re-authenticates from JWT and replays the request on all methods |
Token expiry | Access tokens (1hr) and refresh tokens (25hr) auto-refreshed; concurrent refreshes coalesced |
Server errors (500/503) | Retried on GET/PUT/DELETE (idempotent). POST/PATCH fail fast to avoid duplicate writes |
Request timeouts | 30s on API calls, 15s on auth calls. Retried on idempotent methods only |
Stale HTTP sessions | Cleaned up after 30 minutes of inactivity |
Known API Quirks
Probe assets: Return 404 — probes don't have asset records (expected behavior, skipped in bulk reports)
Active issues:
deviceClassValueanddeviceClassLabelare alwaysnull(known N-central API bug)get_deviceby ID:lastLoggedInUserandstillLoggedInmay returnnull— uselist_devicesinstead for these fields. (lastApplianceCheckinTimewas also missing pre-v2025.3.1.9 — now fixed.)Active issues at SO level: The
/active-issuesendpoint only supports customer/site org unit types, not service orgScheduled task
/details: does NOT accept DEVICE-level task IDs — only SYSTEM and CUSTOMER. Navigate viaparentIdif you have a device task ID.create_direct_scheduled_task: Scripts must have Repository ID ≥ 2000 and "Enable API" toggled ON in the N-central UI. There's no API to enumerate scripts — find IDs in the Script/Software Repository UI. Extensive use accumulates DB rows that slow the UI's Task Execution page.validate_psa_credential: only works with TigerPaw 3.0 — calls for other PSAs will fail.Per-endpoint concurrency limits: N-central enforces concurrency per-endpoint (range 1-50).
/api/devicesallows 5 concurrent;/api/devices/{id}/assets/lifecycle-infoonly 1. Bulk reports default to safe values; tune via theconcurrencyparameter.PREVIEW endpoints:
create_siteandcreate_user_roleare flagged PREVIEW by N-central — the request/response shape may change between versionsCredentialed POST endpoints:
validate_psa_credential,get_custom_psa_ticket_detail, andget_server_info_authenticatedtransmit plaintext credentials in request bodies — only use over HTTPS and be mindful of audit-log contentsselectis a filter, not a projection: despite the name, theselectquery parameter on list endpoints is a FIQL/RSQL predicate that filters rows. It does NOT pick which fields come back. Valid:select=soId==50(returns only that SO). Invalid:select=soId,soName(parse error). Not all fields are queryable — unsupported ones error withField not found: X. Some operators (e.g.=gt=) throw NPEs on the server.
Troubleshooting
Symptom | Likely cause | Fix |
500 errors on every API call | N-central API user password expired (rotates every 90 days) | Reset the password in N-central UI; regenerate the JWT; set a reminder for ~80 days |
Repeated | N-central instance was rebooted (in-memory token state lost) | First 401 triggers re-auth; subsequent calls recover automatically. Noisy on restart but transient. |
JWT works now but fails 5 minutes later | Token revocation propagation | After regenerating a JWT in N-central UI, allow up to 5 minutes for the old token's revocation to propagate |
Server restart loses authentication state | Tokens are stored in-memory only | First API call after restart triggers fresh JWT exchange — no action needed |
Can't reach the API on a custom port | N-central only serves the API on port 443 | Use a reverse proxy or accept port 443 |
| Repository ID < 2000 (bundled default) or "Enable API" toggle is OFF | Use a custom-uploaded script; toggle "Enable API" in the UI |
Reaching |
| Use a tighter filter via the |
HTTP mode exits with "FATAL: MCP_PORT is set but MCP_API_KEY is not" | Safety check — HTTP mode requires an API key | Set |
| Server bound or published to localhost only | Set |
Client connects but queries the wrong N-central / | Server not started with | In single-tenant mode the headers are ignored and env |
| Missing/invalid | Send both; FQDN must be |
"FATAL: NC_MULTI_TENANT=1 requires HTTP mode" | Multi-tenant needs per-request headers, which stdio can't carry | Set |
For client-side setup issues, see the Setup & Client Guide.
Project Structure
├── index.js # Entry point — transport selection (stdio / HTTP)
├── src/
│ ├── auth.js # Per-tenant JWT → Access Token auth, auto-refresh logic
│ ├── client.js # HTTP client with retry, timeout, and rate-limit handling
│ ├── context.js # Per-request tenant context (AsyncLocalStorage) — credential isolation
│ ├── logging.js # Structured logger + audit log
│ ├── metrics.js # Prometheus counters / gauges
│ ├── paginator.js # Auto-pagination, bounded concurrency, CSV helpers
│ ├── prompts.js # MCP Prompts definitions
│ ├── resources.js # MCP Resources definitions
│ ├── server-utils.js # JSON-schema → Zod, header parsing, safeCompare
│ ├── shared.js # Shared pagination/format schema helpers
│ ├── tool-registry.js # Write-mode gating + MCP tool annotations
│ └── tools/
│ ├── custom-properties.js
│ ├── devices.js
│ ├── maintenance-windows.js
│ ├── notes.js
│ ├── organizations.js
│ ├── psa.js
│ ├── registration.js
│ ├── reports.js
│ ├── scheduled-tasks.js
│ ├── server-info.js
│ └── users.js
├── test/
│ ├── auth-isolation.test.js # Per-tenant token/credential isolation (forced interleave, 401 path)
│ ├── isolation.test.js # End-to-end session isolation, cache, boot matrix, SSRF guard
│ ├── mock-fetch.js # Shared test helpers (not a test suite)
│ ├── helpers.test.js
│ ├── server-utils.test.js
│ └── utils.test.js
├── docs/
│ └── SETUP-GUIDE.md # Client setup how-to (Claude Code, VS Code, Claude Desktop, Cursor)
├── .env.example
├── Dockerfile
└── docker-compose.ymlLicense
Released under the MIT License — see the LICENSE file for the full text.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/theonlytruebigmac/n-central-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server