ninjaone-mcp
MCP server for NinjaOne RMM that exposes tools for managing organizations, devices, alerts, ticketing, and automation/scripting via NinjaOne's Public API v2.
README
ninjaone-mcp
NinjaOne RMM MCP server — exposes NinjaOne's Public API v2 (Organizations, Devices, Alerts, Ticketing, Automation/Scripting, Jobs) as MCP tools.
What is NinjaOne / when would an agent use this
NinjaOne is an RMM (remote monitoring and management) platform MSPs use to manage clients' IT fleets. An agent should reach for this MCP for requests like:
- "How many devices does this customer have, and which are offline?" →
ninjaone_get_organization_devices/ninjaone_get_devices - "Any active alerts for this device/org?" →
ninjaone_get_device_alerts/ninjaone_get_alerts - "What tickets are open on the support board?" →
ninjaone_get_ticket_boardsthenninjaone_get_tickets - "Run disk cleanup on this device and tell me when it's done" →
ninjaone_get_device_scripting_optionsto confirm what's runnable,ninjaone_run_script_on_device, thenninjaone_get_device_active_jobsto watch it finish - "What automation scripts do we have available?" →
ninjaone_get_automation_scripts
Overview
This server implements the Model Context Protocol (Streamable HTTP transport) with 23 tools across 5 groups, following the MSPbots Vendor MCP Service SOP: stateless, no stored credentials, per-request header authentication.
This was built starting from the community wyre-technology/ninjaone-mcp project's tool surface (organizations/devices/alerts/tickets, reimplemented here directly against NinjaOne's REST API rather than its Node SDK) and extends it with 5 automation/scripting/jobs tools pulled from NinjaOne's own OpenAPI 3.0.1 spec — every endpoint below was checked against a real NinjaOne API spec, not guessed or copied from a secondary source.
The gateway does the OAuth2 exchange, not this server. NinjaOne authenticates via OAuth2 (client_credentials for a machine identity, refresh_token for a user identity — see below); this server takes only the already-exchanged bearer access token via header and calls NinjaOne's REST API directly with it. It never sees a client_id/client_secret/refresh_token, never talks to /oauth/token, and never caches anything — whoever operates the gateway is responsible for minting and refreshing tokens before they expire (NinjaOne's access tokens last 1 hour).
One tool needs a second identity. ninjaone_run_script_on_device is believed to be rejected by NinjaOne when called with a machine (API Services app) token regardless of scope, because NinjaOne ties script execution to a real user for its audit trail — this is the working hypothesis behind the design below, not yet independently confirmed against a real device/script by this repo. That tool alone takes a second, optional bearer token — one the gateway exchanged via the refresh_token grant against a NinjaOne "Web Application" app (which requires a one-time human browser authorization to obtain the refresh token in the first place). The other 22 tools are unaffected either way.
Quick Start
Docker (recommended)
docker compose up --build
The server starts on http://localhost:8080.
Local (uv)
uv sync
python -m ninjaone_mcp
Health Check
curl http://localhost:8080/health
# {"status": "ok"}
No credentials are required for the health endpoint.
授权参数说明 (Authentication)
Every request to /mcp must include the following HTTP headers:
| Header | 类型 | 是否必填 | 默认值 | 枚举值 | 字段描述 | Example |
|---|---|---|---|---|---|---|
X-Ninja-Token |
string | 必填 | 无 | 无(自由文本) | 已经换好的 NinjaOne OAuth2 bearer access token(网关侧用 client_credentials grant 换出来的,机器身份)。本服务直接拿它打 NinjaOne API,不做任何换取/刷新——网关要负责在 token 过期前(1小时有效期)刷新好。 |
X-Ninja-Token: <access_token> |
X-Ninja-Region |
string | 可选 | us |
us, eu, oc, ca, us2, fed |
NinjaOne 部署区域,决定实际请求的 base URL。 | X-Ninja-Region: eu |
X-Ninja-User-Token |
string | 可选(仅 ninjaone_run_script_on_device 需要) |
无 | 无(自由文本) | 已经换好的 NinjaOne OAuth2 bearer access token,但是网关用 refresh_token grant 针对一个 "Web Application" 类型 App 换出来的(用户身份,不是机器身份)。那个 refresh token 本身需要真人走一次浏览器授权(grant_type=authorization_code)才能拿到,是一次性的人工步骤,不在本服务运行时发生。 |
X-Ninja-User-Token: <access_token> |
Missing X-Ninja-Token returns 401 Unauthorized. Missing the optional X-Ninja-User-Token only affects ninjaone_run_script_on_device (returns a not_configured error) — every other tool works fine without it.
Environment Variables
| Variable | Default | Description |
|---|---|---|
MCP_HTTP_PORT |
8080 |
Listening port |
MCP_HTTP_HOST |
0.0.0.0 |
Listening host |
There is no base-URL env var — the base URL is derived per-request from the X-Ninja-Region header (see config.py's region table).
MCP Endpoint
POST http://localhost:8080/mcp
Connect your MCP client with:
- Transport:
http(Streamable HTTP) - Headers:
X-Ninja-Token(required, an already-exchanged bearer access token),X-Ninja-Region(optional),X-Ninja-User-Token(optional, only forninjaone_run_script_on_device)
Tool List
| Tool | 功能 | 参数 |
|---|---|---|
ninjaone_get_organizations |
列出所有客户组织 | limit?, after? |
ninjaone_get_organization |
按 ID 查单个组织详情 | organization_id(必填) |
ninjaone_create_organization |
创建新组织 | name(必填), description?, node_approval_mode?, tags?, template_organization_id? |
ninjaone_get_organization_locations |
列出组织下的站点(location) | organization_id(必填) |
ninjaone_get_organization_devices |
列出组织下的设备 | organization_id(必填), limit?, after? |
ninjaone_get_devices |
全局列出设备,支持 df 过滤表达式 |
df?, limit?, after? |
ninjaone_get_device |
按 ID 查单个设备详情 | device_id(必填) |
ninjaone_get_device_alerts |
查单个设备的活跃告警 | device_id(必填) |
ninjaone_get_device_activities |
查设备活动日志 | device_id(必填), activity_type?, status?, older_than?, newer_than?, limit? |
ninjaone_get_device_services |
查设备的 Windows 服务列表 | device_id(必填), name?, state? |
ninjaone_reboot_device |
重启设备(破坏性操作) | device_id(必填), mode?("NORMAL"/"FORCED",默认 NORMAL), reason? |
ninjaone_get_alerts |
全局列出活跃告警 | source_type?, df? |
ninjaone_reset_alert |
重置/关闭一条告警(破坏性操作) | alert_uid(必填), activity_note? |
ninjaone_get_ticket_boards |
列出所有工单看板 | 无 |
ninjaone_get_tickets |
按看板列出工单,支持状态/组织/设备过滤 | board_id(必填), status?, organization_id?, device_id?, limit?, cursor? |
ninjaone_create_ticket |
创建新工单 | summary(必填), organization_id(必填), description?, device_id?, location_id?, ticket_form_id?, status?, priority?, severity?, type? |
ninjaone_update_ticket |
更新工单字段和/或添加评论 | ticket_id(必填), summary?, status?, priority?, assignee_id?, comment?, comment_public? |
ninjaone_get_ticket_log_entries |
查工单日志(描述/评论/变更历史) | ticket_id(必填), entry_type? |
ninjaone_get_automation_scripts |
列出可用的自动化脚本 | 无 |
ninjaone_get_device_scripting_options |
查设备上可运行的脚本/内置动作/凭据选项 | device_id(必填) |
ninjaone_run_script_on_device |
在设备上运行脚本或内置动作(破坏性操作,需要 X-Ninja-User-Token) |
device_id(必填), type(必填,"SCRIPT"/"ACTION"), script_id?, action_uid?, parameters?, run_as? |
ninjaone_get_active_jobs |
全局列出正在运行/排队的任务 | job_type?, df? |
ninjaone_get_device_active_jobs |
查单个设备正在运行/排队的任务 | device_id(必填) |
测试示例 (Test Example)
List ticket boards:
{
"method": "tools/call",
"params": { "name": "ninjaone_get_ticket_boards", "arguments": {} }
}
Equivalent curl against the running server (streamable HTTP MCP endpoint):
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "X-Ninja-Token: <access_token>" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "ninjaone_get_ticket_boards", "arguments": {} }
}'
Running a script on a device:
{
"method": "tools/call",
"params": {
"name": "ninjaone_run_script_on_device",
"arguments": { "device_id": 123, "type": "SCRIPT", "script_id": 456 }
}
}
API Reference
- Documentation:
https://app.ninjarmm.com/apidocs-beta/core-resources(per-region equivalents foreu/oc/ca/us2/fed) - Auth: OAuth2 (the gateway's job, not this server's) —
client_credentialsgrant for the machine identity,refresh_tokengrant for the user identity, both atPOST /oauth/token; scopes for the machine identity:monitoring,management,control
Known Gaps / Implementation Notes
- Endpoint provenance: 4 of the 5 automation/scripting/jobs endpoints (
requestScriptingOptions,runScriptOnDevice,getActiveJobs,getDeviceActiveJobs) were cross-checked against an independently obtained copy of NinjaOne's OpenAPI spec.getAutomationScriptswasn't present in that copy (it's newer than that spec revision) — its exact/apipath placement is inferred from the other 4's confirmed pattern, not independently verified. See the comment at the top oftools/automation.py. ninjaone_get_ticketsfilters client-side: NinjaOne's board-run endpoint's request schema definesfilters/searchCriteriaparams, but the community wyre-technology project reports these 400 in practice — this tool always requests an unfiltered page and filtersstatus/organization_id/device_idclient-side instead.- No single-ticket-get or standalone add-comment endpoint: NinjaOne's ticketing API doesn't expose a
GET /ticketing/ticket/{id}— to look up one ticket, page throughninjaone_get_ticketson its board. Adding a comment isn't a separate endpoint either — it's folded intoninjaone_update_ticket'scomment/comment_publicparams, alongside aPUTon the ticket itself. ninjaone_get_devices'sdffilter can be silently dropped by NinjaOne when scoping by organization (a known issue in the community project) — preferninjaone_get_organization_devicesfor an org-scoped device list.- Architecture history: this server originally did its own OAuth2 exchange (took
client_id/client_secretand called/oauth/tokenitself, per-request, never caching the result). That's since moved to the gateway — this server now only ever takes an already-exchanged bearer token (X-Ninja-Token) — matching the pattern MSPbots' gateway already uses forms-graph-mcp/connectwise-asio-mcp. The gateway is responsible for the OAuth2 exchange and for refreshing tokens before their 1-hour expiry; if it doesn't,X-Ninja-Token/X-Ninja-User-Tokenrequests will 401 against real NinjaOne endpoints (mapped tounauthorizedhere), not against this server's own logic. ninjaone_run_script_on_deviceuses a second, user-context token (X-Ninja-User-Token, gateway-exchanged via the refresh_token grant against a Web Application app) instead of the machine token every other tool uses — see the Overview section above for why. This is unverified against a real device/script so far; only the plumbing (missing-token error path, and a live call with a real machine-identity token reaching NinjaOne's real API and getting real data) has been checked.- Verified against a live NinjaOne account:
tools/listreturns all 23 tools with clean schemas,pytest(17 tests) passes, and a realninjaone_get_organizationscall using a real, already-exchanged bearer token (viaX-Ninja-Token) returned real organization data.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。