action1-mcp-server
A production-grade MCP server that wraps the entire Action1 REST API, enabling Claude or any MCP host to manage endpoints, run scripts, deploy updates, and more via natural language with safety guards.
README
action1-mcp-server
A production-grade Model Context Protocol (MCP) server for the Action1 RMM REST API. Wraps the entire Action1 API surface so that Claude (or any other MCP host) can run PowerShell on Windows, Bash on macOS, deploy updates, reboot endpoints, search audit trails, build CVE remediation plans, and more — all via natural language and with a three-layer destructive guard so the LLM cannot accidentally mutate production state.
Status: v0.5.0 — 100 % coverage of the pinned Action1 OpenAPI 3.1.0 spec, exposed as 166 tools (33 curated incl. 6 workflow wrappers + 133 auto-generated) with pinned SHA, hybrid architecture, three-layer destructive guard, stdio and Streamable HTTP transports, MCPB bundle, CI. Full conformance pass against the Anthropic
mcp-builderskill best practices (server name, tool annotations, response_format, output schemas, Markdown rendering, character limits, evaluation suite).
Why this project
Action1 has a clean REST API but a thick layer of LLM-unfriendly quirks that, if unhandled, silently produce wrong results:
- macOS Bash actions need a ten-field
run_scriptpayload the API returnsnot applicable to Mac platformfor any field that is missing. - Windows PowerShell actions need a non-empty
success_exit_codes. last_seenisYYYY-MM-DD_HH-MM-SS(UTC), not ISO-8601 —Date.parsereturnsNaN(we render it human-readable in Markdown output).- The
selfURL of a list-endpoint includes a/generalsegment that is not a real REST resource. - The script-output stream is interleaved with
Starting/Waiting/Completedlifecycle markers that need to be filtered. OSis upper-case,platformis lower-case, both can carry the platform string; we normalise via a single detector.
This server encodes all of those. See
docs/api-coverage-gaps.md §"Cross-repo
comparison" for what other Action1 MCP projects miss.
Highlights
- 100 % API coverage — every operation in the pinned Action1 OpenAPI
3.1.0 spec is reachable through exactly one MCP tool. Mapping in
docs/api-coverage.md. The pin is enforced by a unit test (tests/unit/specSha.test.ts) so an upstream spec change fails the build instead of silently rotting the surface. - Hybrid architecture — hand-curated tools that encode the quirks above + auto-generated tools from the spec + high-level workflow wrappers (CVE remediation plan, audit log search, software inventory, recurring schedules, report export, group resolver).
- MCP-protocol completeness — beyond tools, the server also ships:
- 7 Resources (
action1://me,://orgs,://templates,://reports,://endpoints/{orgId},://groups/{orgId},://vulnerabilities/{orgId}) for hosts that pre-fetch context. All list-shaped resources are capped to fit the 1 MB host limit and emit a truncation note when the cap fires. - 6 Slash-Prompts (
/patch-windows-fleet,/audit-vulnerabilities,/triage-offline-endpoints,/run-script-fleetwide,/software-inventory-sweep,/check-my-permissions) for one-click workflows. logging/setLevel— clients can dynamically bump or lower verbosity at runtime.- Cancellation propagation — long-running tools (
auto_paginate,wait_for_automation,execute_and_wait) honour the host's AbortSignal. outputSchemadeclared on every list-shaped read tool for client-side validation.
- 7 Resources (
- Skill-conformant tools — every tool emits
response_format(markdowndefault,jsonopt-in), a structured description withArgs / Returns / Examples / Error Handlingsections, all four annotations (readOnlyHint,destructiveHint,idempotentHint,openWorldHint) explicitly set, andtotal/count/next_cursor/has_moreon every paginated response. - Markdown rendering with humanised values — Action1 timestamps
(
YYYY-MM-DD_HH-MM-SS) render asYYYY-MM-DD HH:MM:SS UTC; ID columns lead the table; per-toolCHARACTER_LIMIT = 25_000keeps agent context lean. - Three-layer destructive guard —
ACTION1_ALLOW_DESTRUCTIVEenv switch + per-callconfirm: "YES"+dry_run: true(default). The LLM cannot accidentally mutate production state. OptionalACTION1_DESTRUCTIVE_AUTO_CONFIRMfor isolated single-operator setups (still requires the env switch). - Sound auth — OAuth2 client-credentials with proactive 5-minute refresh, in-flight deduplication of concurrent token requests, and refresh_token-first fallback when re-acquiring.
- Robust transport — retry with exponential backoff for DNS hiccups,
HTTP 5xx, and 429 (honors
Retry-After). Pagination walkslimit/fromtransparently with caller-supplied caps. - Both transports — stdio (default; for local Claude Code / Cursor /
Claude Desktop) + Streamable HTTP, stateless (for hosted Claude.ai
custom connectors / team setups). HTTP transport supports bearer-token
auth via
MCP_HTTP_TOKEN(constant-time compare), a 1 MB body limit, and hard-fails on non-loopback bind without an Origin allowlist. - Distribution — MCPB bundle for Claude Desktop one-click install, npm publish-ready.
- Observability — structured JSON-line logs to stderr with
auto-redaction of
Bearer …,api-key-…, and any property whose key looks sensitive (authorization,*_token,*_secret,password, …). Stdout reserved for MCP JSON-RPC. - Strict TypeScript with Zod input schemas on every tool. 527 tests across 4 categories (unit / integration / security / performance).
Quickstart
git clone https://github.com/mguttmann/action1-mcp.git
cd action1-mcp
npm install
cp .env.example .env # edit with your Action1 credentials
npm run build
npm run inspector # opens MCP Inspector against the built server
Or attach directly to Claude Code:
claude mcp add action1 -- node --env-file=$(pwd)/.env $(pwd)/dist/index.js
For the full installation walkthrough — including Claude Desktop, Claude.ai,
Cursor, Continue.dev, and Cody — see docs/INSTALLATION.md.
Architecture
┌──────────────────────────────────────────────────────────────────────┐
│ Curated tools + Workflow wrappers │
│ • script execution (Win + Mac + OS-aware) │
│ • execute_and_wait (POST + poll + filtered output) │
│ • get_endpoint with /general → canonical path + list-fallback │
│ • CVE remediation plan, audit log search, software inventory, … │
├──────────────────────────────────────────────────────────────────────┤
│ Auto-generated tools │
│ • one tool per remaining spec operation, verb-first snake_case names │
│ • shared runtime: pagination, destructive guard, error mapping │
│ • Action1 OpenAPI 3.1.0 pinned at SHA af75f1cc… │
├──────────────────────────────────────────────────────────────────────┤
│ Shared infrastructure │
│ • Action1Client (auth, retry, pagination, error mapping) │
│ • TokenProvider (refresh_token fallback, 5-min proactive refresh) │
│ • OrgResolver (fuzzy name match, session cache) │
│ • Three-layer destructive guard (env + confirm + dry_run) │
│ • Structured stderr logger with auto-key redaction │
│ • Markdown auto-table renderer (humanises Action1 timestamps) │
│ • Per-tool CHARACTER_LIMIT = 25_000 + WIRE_LIMIT_BYTES = 400_000 │
└──────────────────────────────────────────────────────────────────────┘
When a curated tool and an auto-generated tool address the same path, the
curated one wins by name and the auto-generated one is suppressed via
src/codegen/curated-overrides.ts. See
docs/api-coverage.md for the full mapping.
Configuration
All configuration is via environment variables; copy
.env.example to .env and fill in.
| Variable | Required | Default | Description |
|---|---|---|---|
ACTION1_BASE_URL |
yes | — | Region-specific instance, e.g. https://app.eu.action1.com/api/3.0. |
ACTION1_CLIENT_ID |
yes | — | OAuth2 client id from Action1 → Settings → API Credentials. |
ACTION1_CLIENT_SECRET |
yes | — | OAuth2 client secret. Shown once at credential creation. |
ACTION1_ORG_ID |
recommended | — | Default org UUID. Each tool can override via org_id. |
ACTION1_DEFAULT_TIMEOUT_MINUTES |
no | 10 |
Per-action timeout (PowerShell, etc.). |
ACTION1_DEFAULT_RETRY_MINUTES |
no | 1440 |
Window during which Action1 retries when an endpoint is offline. |
ACTION1_LOG_LEVEL |
no | info |
One of debug, info, notice, warn, warning, error, critical, alert, emergency. |
ACTION1_LAST_SEEN_STALE_MINUTES |
no | 10 |
Minutes since last_seen before an endpoint is treated as offline (range 1–1440). Tune up for fleets that check in infrequently. |
ACTION1_ALLOW_DESTRUCTIVE |
no | false |
Set to true / 1 / yes to allow tools with destructiveHint: true to mutate state. |
ACTION1_DESTRUCTIVE_AUTO_CONFIRM |
no | false |
Single-operator only: waives the per-call confirm: "YES" requirement. dry_run still defaults to true — pass dry_run: false to actually execute. Still requires ACTION1_ALLOW_DESTRUCTIVE=true. |
PORT |
no | 3000 |
HTTP transport port. |
HOST |
no | 127.0.0.1 |
HTTP transport host. |
MCP_HTTP_TOKEN |
no | unset | Required Authorization: Bearer <token> on /mcp when set. Strongly recommended for any non-loopback bind. |
MCP_HTTP_ALLOWED_ORIGINS |
no | unset | Comma-separated allowlist for browser Origin headers. Required when binding to a non-loopback host — the server hard-fails to start otherwise. |
TRUST_PROXY |
no | false |
Reverse-proxy hop trust for correct client IPs in rate-limiting. One of false / true / loopback / a CIDR. Enable only for trusted upstream proxies. |
Connecting to MCP hosts
| Host | Transport | Doc |
|---|---|---|
| Claude Code (CLI) | stdio | INSTALLATION § Claude Code |
| Claude Desktop (macOS / Windows) | stdio | INSTALLATION § Claude Desktop |
| Claude.ai (web) | Streamable HTTP | INSTALLATION § Claude.ai |
| Cursor | stdio | INSTALLATION § Cursor |
| Continue.dev / Cody | stdio | INSTALLATION § generic |
Tools
See docs/USAGE.md for the full tool reference and
docs/EXAMPLES.md for end-to-end recipes.
A summary of the curated layer (the auto-generated layer is the long
tail and is documented operation-by-operation in docs/api-coverage.md):
Discovery (read-only)
action1_list_organizations, action1_list_endpoints,
action1_endpoints_summary (server-side aggregation: ~1 KB regardless
of fleet size), action1_get_endpoint (with /general → canonical
path fallback, plus derived platform and connectivity.online),
action1_search_endpoints (substring filter on hostname / user / OS /
status), action1_list_action_templates, action1_get_action_template,
action1_list_missing_updates, action1_list_vulnerabilities (graceful
403 if scope absent), action1_list_recent_automations,
action1_automations_summary.
Execution (destructive — guard required)
action1_run_powershell (Windows; auto-fills success_exit_codes),
action1_run_bash_macos (macOS; auto-fills the ten run_script fields),
action1_run_script (auto-routes by OS), action1_reboot_endpoint,
action1_deploy_update (spec-conformant {scope, packages, reboot_options}),
action1_deploy_package, action1_uninstall_program,
action1_run_data_collection. All accept target_type: "Endpoint" (default)
or "EndpointGroup" for bulk fan-out.
Polling / results (read-only)
action1_get_automation_status, action1_get_automation_results,
action1_get_automation_output (filters Starting/Waiting/Completed),
action1_wait_for_automation.
Workflows (destructive)
action1_execute_and_wait — start an action, poll until it terminates,
return the filtered output. Auto-routes by OS for mode: "script" or runs
a named template for mode: "template".
High-level wrappers
action1_endpoint_groups_resolve (fuzzy name → UUID),
action1_software_inventory_for_endpoint, action1_recurring_schedules,
action1_audit_log_search, action1_report_export,
action1_cve_remediation_plan.
Auto-generated
Every remaining Action1 API operation as a verb-first snake_case tool.
See docs/api-coverage.md. All input parameter
names are normalised to snake_case for consistency with the curated
layer.
Resources
Seven MCP resources for hosts that pre-fetch context:
| URI | Purpose |
|---|---|
action1://me |
The authenticated identity / role / permissions. Use this as a startup probe to know which tool families will 403. |
action1://orgs |
All visible organizations. |
action1://templates |
Action template catalog. |
action1://reports |
Report catalog. |
action1://endpoints/{orgId} |
Live endpoint snapshot per org. Pass default to use ACTION1_ORG_ID. |
action1://groups/{orgId} |
Endpoint groups. |
action1://vulnerabilities/{orgId} |
Org-wide CVE rollup. May 403 if the role lacks view_vulnerabilities. |
All list-shaped resources are capped at ~400 KB and emit a truncation note when the cap fires; the equivalent paginated tool gives unbounded data via cursor chaining.
Slash-prompts
Six host-surfaced workflows for the most common day-to-day jobs:
| Prompt | Args | What it does |
|---|---|---|
/patch-windows-fleet |
group_query |
Resolves the group, lists missing updates, previews and (after confirmation) deploys. |
/audit-vulnerabilities |
top_n? |
Surveys org-wide vulnerabilities, ranks the worst, presents a remediation plan. |
/triage-offline-endpoints |
stale_minutes? |
Walks the inventory, surfaces endpoints whose status or last_seen are stale. |
/run-script-fleetwide |
group_query, script_text |
Validates the script is read-only, dispatches via OS-aware routing, streams output. |
/software-inventory-sweep |
software_name |
Searches every endpoint's installed software for a substring match. |
/check-my-permissions |
— | Reads action1://me and reports which tool families will 403 with the current role. |
Destructive guard
Every tool flagged destructiveHint: true enforces three independent gates:
ACTION1_ALLOW_DESTRUCTIVE=true ← server env, restart-required
AND
confirm: "YES" ← exact, case-sensitive
AND
dry_run: false ← default is true → preview-only
// Preview only (default)
{ "tool": "action1_run_powershell",
"arguments": { "endpoint_id": "<uuid>", "script_text": "Get-Date" } }
// → returns { "dry_run": true, "would_send": { method, path, body } }
// Actually execute (server env: ACTION1_ALLOW_DESTRUCTIVE=true)
{ "tool": "action1_run_powershell",
"arguments": { "endpoint_id": "<uuid>", "script_text": "Get-Date",
"dry_run": false, "confirm": "YES" } }
Single-operator setups can opt into ACTION1_DESTRUCTIVE_AUTO_CONFIRM=true,
which waives only the per-call confirm: "YES" requirement — dry_run
still defaults to true, so a bare call is still a preview; pass
dry_run: false to execute. The env switch is still required, and the
server logs a loud warning at start-up when this mode is on.
See docs/SECURITY.md for the full threat model.
Examples
Three quick workflows; many more in docs/EXAMPLES.md:
# 1. Fleet health check
> "How many endpoints are online and how many need a reboot?"
agent → action1_endpoints_summary
result → { total: 1247, fresh_last_seen: 1130, reboot_required: 38, ... }
# 2. Run a read-only PowerShell on a specific Win11 host
> "What's the OS build of <hostname>? Run `Get-ComputerInfo OsBuildNumber`."
agent → action1_search_endpoints { query: "<hostname>", fields: ["hostname"] }
agent → action1_run_powershell {
endpoint_id: <uuid>,
script_text: "Get-ComputerInfo OsBuildNumber",
dry_run: false, confirm: "YES" }
agent → action1_wait_for_automation { instance_id: <id>, endpoint_id: <uuid> }
# 3. Build a CVE remediation plan
> "What's needed to remediate CVE-2024-XXXX?"
agent → action1_cve_remediation_plan { cve_id: "CVE-2024-XXXX" }
result → { plan: [{ cve, affected_endpoints, candidate_packages }], ... }
agent → action1_deploy_update { ... } (after operator confirmation)
Rate limits
Action1's REST API does not publish a hard rate-limit ceiling. In practice we observe ~10 requests per second sustained without 429s on the EU instance. If you do hit 429:
- The retry wrapper honours
Retry-Afterautomatically. - Token-bucket-style throttling on
auto_paginatecalls keeps a multi-page list walk under ~5 RPS. - For mass-fan-out actions, prefer
target_type: "EndpointGroup"over individual per-endpoint POSTs — Action1 fans out server-side.
The server does NOT pre-emptively cache list responses; consecutive
calls always hit the API. See src/client/retry.ts
for the retry policy.
Required Action1 permissions
The MCP server's tool surface respects whatever role the API key carries. Common scopes:
| Tool family | Required permission |
|---|---|
| Discovery (orgs, endpoints, templates, automations) | view_endpoints, view_organizations, view_action_templates |
| Software inventory | view_installed_software |
| Missing updates | view_endpoints (often suffices) |
| Vulnerabilities | view_vulnerabilities (commonly missing on automation-only keys) |
| Audit log | view_audit |
| Reports | view_reports |
| Execute scripts / deploy / reboot | manage_actions, manage_endpoints |
| Manage schedules | manage_schedules |
Use action1://me or the /check-my-permissions slash prompt to
confirm what the configured key can do before the agent starts
attempting destructive operations.
Documentation
| File | What it covers |
|---|---|
docs/INSTALLATION.md |
Step-by-step for every supported MCP host, plus install FAQ. |
docs/FAQ.md |
Cross-topic FAQ + how-to recipes. |
docs/USAGE.md |
Tool reference for curated + wrapper layers, plus Resources, Prompts, output-format, cancellation, logging. |
docs/EXAMPLES.md |
End-to-end recipes (patching, triage, CVE remediation, etc.). |
docs/DEPLOYMENT.md |
stdio / HTTP / systemd / MCPB / Cloudflare Workers, plus the production checklist. |
docs/SECURITY.md |
Threat model, destructive guard, credential rotation, redaction, rate limits. |
docs/TROUBLESHOOTING.md |
Common failure modes and fixes. |
docs/CONTRIBUTING.md |
Codegen workflow, test layout, commit conventions. |
docs/api-coverage.md |
The full operation-to-tool mapping (166 tools over the pinned spec). |
docs/api-coverage-gaps.md |
Cross-repo comparison and capability gaps. |
docs/403-investigation.md |
Why /general is not a real path. |
evals/ |
Skill-format evaluation suite (12 multi-hop questions, harness-runnable). |
TESTING.md |
Unit / integration / security / performance tests, smoke tests, live-run results (anonymised). |
Development
npm run dev # tsx watch (stdio)
npm run typecheck
npm run lint
npm test # 527 tests across all categories
npm run test:unit # 37 files
npm run test:integration # MCP-protocol round-trip tests
npm run test:security # destructive-guard truth table, redaction matrix
npm run test:performance # capItemList / markdownTable scaling
npm run codegen # regenerate src/tools/generated/index.ts from the spec
npm run inspector # MCP Inspector against the built stdio server
The macOS payload-builder unit tests are intentionally strict — they protect against silent regressions of the reverse-engineered field set. If you change the payload, update the snapshot.
Roadmap
Tracked in docs/api-coverage-gaps.md §
"Recommended second-wave roadmap":
- Endpoint groups CRUD + targeting (mostly delivered via auto-gen + the
target_typeparameter on curated tools). - Reports surface (delivered via the auto-gen +
action1_report_exportwrapper). - Software inventory per endpoint (delivered via
action1_software_inventory_for_endpoint). - CVE-based remediation builder (delivered via
action1_cve_remediation_plan). - Recurring automations CRUD (delivered via auto-gen).
Open candidates:
- Streamable HTTP "Sampling" / "Elicitation" support if user demand appears.
- Cloudflare Workers reference deployment.
- Bundle the generated
_coverage_table.mdinto the npm package.
Security
See docs/SECURITY.md. Report vulnerabilities via the
private security advisory at
https://github.com/mguttmann/action1-mcp/security/advisories/new.
License
MIT © 2026 mguttmann
Acknowledgements
- The Action1 team for shipping a clean OpenAPI bundle.
- The MCP community at https://modelcontextprotocol.io.
- The Anthropic
mcp-builderskill, whose Quality Checklist this server follows. - The four sibling projects whose code I read while scoping this one:
dimer47/action1-cli,pts211/action1-mcp-server,ghively/Action1MCP,DavidS-GPC/mcp-action1. Cross-repo notes indocs/api-coverage-gaps.md.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.