action1-mcp-server 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.pyc
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ .venv/
7
+
8
+ # Segredos: Client ID/Secret do Action1 só devem existir no claude_desktop_config.json (fora do projeto)
9
+ .env
10
+ .env.*
11
+ *.bak
12
+ claude_desktop_config*.json
13
+
14
+ # Dados exportados do Action1 (arquivo_saida, relatórios CSV/HTML, planilhas)
15
+ saidas/
16
+ *.json
17
+ *.csv
18
+ *.html
19
+ *.xlsx
20
+ *.xls
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 João Pedro Rodrigues
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,332 @@
1
+ Metadata-Version: 2.5
2
+ Name: action1-mcp-server
3
+ Version: 1.0.0
4
+ Summary: MCP server somente leitura para a API do Action1 (patches, vulnerabilidades e inventário)
5
+ Author-email: João Pedro Rodrigues <jpedrocrc@hotmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: action1,claude,mcp,patch-management,rmm,vulnerability
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: httpx>=0.27
13
+ Requires-Dist: mcp[cli]<2,>=1.2.0
14
+ Description-Content-Type: text/markdown
15
+
16
+ <div align="center">
17
+
18
+ # Action1 MCP Server
19
+
20
+ **Give Claude, ChatGPT or Codex read-only access to everything in your [Action1](https://www.action1.com) console.**
21
+
22
+ Managed endpoints, missing patches, vulnerabilities, software inventory, automations, reports and audit trail, through the
23
+ [Model Context Protocol](https://modelcontextprotocol.io).
24
+
25
+ [![PyPI](https://img.shields.io/pypi/v/action1-mcp-server?color=2563eb&label=PyPI)](https://pypi.org/project/action1-mcp-server/)
26
+ [![Python](https://img.shields.io/badge/python-3.10%2B-2563eb)](https://www.python.org/)
27
+ [![License: MIT](https://img.shields.io/badge/license-MIT-16a34a)](LICENSE)
28
+ [![Read-only](https://img.shields.io/badge/access-read--only-16a34a)](#-security)
29
+ [![MCP](https://img.shields.io/badge/MCP-stdio-7c3aed)](https://modelcontextprotocol.io)
30
+
31
+ [Quick start](#-quick-start) · [What you can ask](#-what-you-can-ask) · [Available data](#-available-data) · [Tools](#-tools) · [Development](#-development)
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## 🚀 Quick start
38
+
39
+ **1. Install**
40
+
41
+ ```bash
42
+ pip install action1-mcp-server
43
+ ```
44
+
45
+ **2. Get your API credentials**
46
+
47
+ In Action1, go to **Settings → API Credentials → Add API Credentials**, assign the **Viewer** role, and copy the **Client ID** (an `api-key-...@action1.com` address) and the **Client Secret**. The secret is shown only once.
48
+
49
+ **3. Connect your AI client**
50
+
51
+ <details open>
52
+ <summary><b>Claude Desktop</b></summary>
53
+
54
+ Edit `claude_desktop_config.json`. On Windows it is in `%APPDATA%\Claude\`; on macOS, in `~/Library/Application Support/Claude/`.
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "action1": {
60
+ "command": "action1-mcp-server",
61
+ "env": {
62
+ "ACTION1_CLIENT_ID": "api-key-...@action1.com",
63
+ "ACTION1_CLIENT_SECRET": "your-client-secret"
64
+ }
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ Then quit Claude Desktop completely (on Windows: tray icon → **Quit**) and open it again.
71
+
72
+ </details>
73
+
74
+ <details>
75
+ <summary><b>Claude Code</b></summary>
76
+
77
+ ```bash
78
+ claude mcp add action1 -e ACTION1_CLIENT_ID=api-key-...@action1.com -e ACTION1_CLIENT_SECRET=your-client-secret -- action1-mcp-server
79
+ ```
80
+
81
+ </details>
82
+
83
+ <details>
84
+ <summary><b>Codex CLI, Codex IDE extension and ChatGPT desktop app</b></summary>
85
+
86
+ All three share the same configuration file, so you only need to set it up once.
87
+
88
+ Using the Codex CLI:
89
+
90
+ ```bash
91
+ codex mcp add action1 --env ACTION1_CLIENT_ID=api-key-...@action1.com --env ACTION1_CLIENT_SECRET=your-client-secret -- action1-mcp-server
92
+ ```
93
+
94
+ Or edit `~/.codex/config.toml` (on Windows: `%USERPROFILE%\.codex\config.toml`):
95
+
96
+ ```toml
97
+ [mcp_servers.action1]
98
+ command = "action1-mcp-server"
99
+
100
+ [mcp_servers.action1.env]
101
+ ACTION1_CLIENT_ID = "api-key-...@action1.com"
102
+ ACTION1_CLIENT_SECRET = "your-client-secret"
103
+ ```
104
+
105
+ In the ChatGPT desktop app you can also add it from **Settings → MCP servers → Add server → STDIO**, then select **Restart**.
106
+
107
+ > [!NOTE]
108
+ > ChatGPT on the web can't use this server: it doesn't read local configuration and only connects to remote MCP servers. Use the ChatGPT desktop app or Codex instead.
109
+
110
+ </details>
111
+
112
+ The data center region (North America, Europe, UK, Australia) is detected automatically on the first login. To pin it, set `ACTION1_REGION`.
113
+
114
+ **Updating**
115
+
116
+ ```bash
117
+ pip install --upgrade action1-mcp-server
118
+ ```
119
+
120
+ Restart your AI client afterwards. Your configuration stays the same.
121
+
122
+ ## 💬 What you can ask
123
+
124
+ Ask in plain language, in any language:
125
+
126
+ - *"List the Action1 routes and what each one returns"*
127
+ - *"Give me an overview of the environment: endpoints, missing patches and vulnerabilities"*
128
+ - *"Which endpoints need a reboot or haven't checked in for more than 30 days?"*
129
+ - *"Show endpoint FINANCE-LAPTOP01 with its missing updates and CVEs"*
130
+ - *"List the critical CVEs in the CISA KEV catalog and which endpoints have them"*
131
+ - *"Which critical updates are past their SLA?"*
132
+ - *"How did last Friday's patch automation go on each endpoint?"*
133
+ - *"Who logged in to the console this week?"*
134
+
135
+ ## 📦 Available data
136
+
137
+ All routes below were tested against a live Action1 account with a **Viewer** credential.
138
+
139
+ | Route | What it returns |
140
+ |---|---|
141
+ | `organizations`, `enterprise` | Organizations and enterprise details |
142
+ | `me`, `users` | The credential's user and the console users |
143
+ | `endpoints/managed/{orgId}` | Endpoints: status, last seen, IP, MAC, OS, hardware, logged-on user, agent version, groups, pending reboot, missing patch and CVE counts |
144
+ | `endpoints/managed/{orgId}/{id}/missing-updates` | Updates missing on one endpoint |
145
+ | `endpoints/groups/{orgId}` | Endpoint groups and their members |
146
+ | `vulnerabilities/{orgId}` | CVEs found on endpoints: CVSS, CISA KEV, affected software and versions, fixing update, remediation deadline and status |
147
+ | `vulnerabilities/{orgId}/{cveId}`, `CVE-descriptions/{cveId}` | CVE details, affected endpoints and documented compensating controls |
148
+ | `updates/{orgId}` | Missing OS and third-party patches: severity, approval, SLA, KB |
149
+ | `installed-software/{orgId}/data` | Software inventory, per organization or per endpoint |
150
+ | `software-repository/{orgId}` | Software repository packages and versions |
151
+ | `automations/schedules/{orgId}`, `automations/instances/{orgId}` | Scheduled automations, runs and per-endpoint results |
152
+ | `reports/all`, `reportdata/{orgId}/{reportId}/data` | Report catalog (~76 built-in reports) and report rows (CSV/HTML export) |
153
+ | `scripts/all`, `settings/all`, `setting-templates/all` | Script library and advanced settings |
154
+ | `audit/events` | Audit trail: logins, remote sessions, configuration changes, API calls |
155
+ | `subscription/*`, `roles`, `logs/{orgId}`, `endpoints/deployers/{orgId}`, `data-sources/all` | License, roles, diagnostic logs, Deployers and data sources (need a role above Viewer) |
156
+
157
+ Parameters, fields, required permissions and quirks for each route are documented in [`ENDPOINTS.md`](src/action1_mcp/ENDPOINTS.md) (in Portuguese). The map was built from Action1's official OpenAPI 3.1 specification, published at [app.action1.com/apidocs](https://app.action1.com/apidocs).
158
+
159
+ ## 🧰 Tools
160
+
161
+ Tool names are in Portuguese; your AI assistant picks the right one from your request. In Action1, an *endpoint* is a managed computer, so the tools call computers *máquinas* and API paths *rotas*.
162
+
163
+ | Tool | Description |
164
+ |---|---|
165
+ | `action1_get` | Calls any `GET` route and passes every parameter through unchanged. `{orgId}` in the path is replaced with the default organization. With `paginar=True` it paginates automatically up to `max_registros`. `arquivo_saida` saves the full result to disk. |
166
+ | `listar_rotas` | Returns the API map (routes, parameters, fields, permissions, limits), so the assistant knows what it can request |
167
+ | `listar_organizacoes` | Lists the account's organizations (their IDs are the `orgId` used by the routes) |
168
+ | `listar_maquinas` | Lists endpoints with search and filters for status, pending reboot, patch and vulnerability status, OS and group |
169
+ | `buscar_maquina` | Fetches one endpoint by ID or name, with its missing updates, CVEs and, optionally, installed software |
170
+ | `listar_vulnerabilidades` | Lists CVEs by severity, remediation status, endpoint, publication date, CVE list or CISA KEV |
171
+ | `buscar_cve` | CVE details, affected endpoints, documented controls, and whether the CVE is present in the organization |
172
+ | `listar_atualizacoes` | Lists missing patches by severity, approval status and text |
173
+ | `listar_softwares` | Software inventory for the organization or for one endpoint |
174
+ | `listar_automacoes` | Scheduled automations, automation runs, or the per-endpoint result of one run |
175
+ | `consultar_relatorio` | Reads a report by name or ID; without a name, lists the report catalog |
176
+ | `listar_auditoria` | Audit trail for a date range, by event type or text |
177
+ | `resumo_ambiente` | Environment overview: endpoints by status, connection, OS, agent version and group; stale and pending-reboot endpoints; endpoints with the most missing patches and CVEs; patches by severity, approval and SLA; CVEs by severity, remediation status, KEV and product |
178
+
179
+ The resource `action1://rotas` exposes the full API map as Markdown.
180
+
181
+ <details>
182
+ <summary><b>Example of a generic call</b></summary>
183
+
184
+ ```json
185
+ {
186
+ "endpoint": "vulnerabilities/{orgId}",
187
+ "params": {
188
+ "score": "Critical",
189
+ "remediation_status": "Overdue",
190
+ "filter": "Chrome",
191
+ "sortby": "-cvss_score"
192
+ },
193
+ "paginar": true,
194
+ "max_registros": 500
195
+ }
196
+ ```
197
+
198
+ </details>
199
+
200
+ ## 🚦 Limits and behavior
201
+
202
+ | Topic | Behavior |
203
+ |---|---|
204
+ | **Authentication** | OAuth2 with Client ID and Client Secret. The access token lasts 1 hour and is renewed automatically; if the API rejects it, the server logs in again once. |
205
+ | **Rate limit** | Action1 doesn't publish a number. On `429` the server waits for the `Retry-After` delay and retries. Local throttling can be turned on with `ACTION1_RATE_LIMIT`. |
206
+ | **Permissions** | Each route needs a permission from the credential's role. Without it, the response is `{"erro": "sem_permissao", ...}` with the name of the missing permission. |
207
+ | **Organizations** | A single-organization account is used automatically. With several, endpoints, CVEs, patches and software queries cover all of them (`orgId=all`); the other tools ask for `org_id` (or `ACTION1_ORG_ID`). |
208
+ | **Pagination** | `from` + `limit`, with `next_page` or `total_items` (sometimes an estimate such as `"10+"`). The server never requests 1-item pages, because `limit=1` misbehaves in the API. |
209
+ | **Dates** | The API answers in UTC, formatted `YYYY-MM-DD_HH-mm-ss`. In the tools, `YYYY-MM-DD` dates are read as days in Brasília time. |
210
+ | **Errors** | `401`, `403`, `404`, `400` (with the API's message) and timeouts come back as JSON: `{"erro": ..., "mensagem": ...}`. Timeouts and `5xx` errors are retried with backoff. |
211
+ | **Large responses** | Responses longer than `ACTION1_MAX_CHARS` are truncated (long strings such as base64 images and scripts first, then lists), with a hint to narrow the query. Use `arquivo_saida` to save the complete result. |
212
+
213
+ <details>
214
+ <summary><b>Optional environment variables</b></summary>
215
+
216
+ | Variable | Default | Purpose |
217
+ |---|---|---|
218
+ | `ACTION1_REGION` | auto-detected | `na`, `na-2`, `eu`, `uk` or `au` |
219
+ | `ACTION1_BASE_URL` | | Explicit base URL (overrides the region) |
220
+ | `ACTION1_ORG_ID` | the only organization | Organization used in place of `{orgId}` |
221
+ | `ACTION1_RATE_LIMIT` | `0` | Requests per minute (`0` disables local throttling) |
222
+ | `ACTION1_PAGE_SIZE` | `100` | Page size for automatic pagination |
223
+ | `ACTION1_MAX_RETRIES` | `3` | Retries on `429`, `5xx` and timeouts |
224
+ | `ACTION1_MAX_ESPERA` | `120` | Longest wait (seconds) accepted for a `429` retry |
225
+ | `ACTION1_TIMEOUT` | `60` | Per-request timeout (seconds) |
226
+ | `ACTION1_MAX_CHARS` | `60000` | Maximum response size before truncation |
227
+ | `ACTION1_LOG_LEVEL` | `WARNING` | Log level (always written to stderr) |
228
+
229
+ </details>
230
+
231
+ ## 🔒 Security
232
+
233
+ - **Read-only.** The server only sends `GET` requests; the one exception is the internal `POST /oauth2/token` login. Three `GET` routes are also blocked, even with `permitir_nao_listados=True`: the agent installer link, the Deployer installer link and remote sessions.
234
+ - **Use a Viewer credential.** The server's blocklist is a second layer; the role on the credential is the first. A Viewer key can't change anything even if a request gets through.
235
+ - **Secret handling.** The Client ID and Secret are read only from the environment, never written to logs, and the secret and tokens are removed from every response and error message.
236
+ - **Audited.** Every API call, including `GET`s, shows up in Action1's Audit Trail under the credential's user.
237
+ - **Real company data.** The credential sees your whole fleet, so conversations may contain hostnames, IP and MAC addresses, logged-on user names and vulnerability details. Request only what you need and follow your company's data protection policy.
238
+ - **One credential per person.** Never share the secret in chat, e-mail or GitHub issues. If it leaks, revoke it in **Settings → API Credentials** right away.
239
+
240
+ ## 📥 Other installation methods
241
+
242
+ Requires Python 3.10 or newer.
243
+
244
+ | Method | Command |
245
+ |---|---|
246
+ | PyPI | `pip install action1-mcp-server` |
247
+ | uv, without installing | `uvx action1-mcp-server` (in `claude_desktop_config.json`: `"command": "uvx", "args": ["action1-mcp-server"]`) |
248
+ | GitHub | `pip install git+https://github.com/jpedrocrc/Action1-MCP-Server` |
249
+
250
+ <details>
251
+ <summary><b>Windows: "command not found"</b></summary>
252
+
253
+ `pip` installs the executable in `...\Python3xx\Scripts`. If that folder is not on your `PATH`, your AI client cannot find `action1-mcp-server`. Use the full path to the `.exe` in `"command"`, or set `"command": "python"` and `"args": ["-m", "action1_mcp"]`.
254
+
255
+ </details>
256
+
257
+ ## 🛠 Development
258
+
259
+ <details>
260
+ <summary><b>Project structure</b></summary>
261
+
262
+ | File | Contents |
263
+ |---|---|
264
+ | `pyproject.toml` | Package metadata, dependencies and the `action1-mcp-server` command |
265
+ | `src/action1_mcp/server.py` | MCP server and tools |
266
+ | `src/action1_mcp/ENDPOINTS.md` | API map. The server reads the JSON block at the end of this file, so supporting a new route only takes adding it there. |
267
+ | `test_server.py` | Offline tests and coverage tests against the real API |
268
+
269
+ </details>
270
+
271
+ **Local setup**
272
+
273
+ ```bash
274
+ git clone https://github.com/jpedrocrc/Action1-MCP-Server
275
+ cd Action1-MCP-Server
276
+ pip install -e .
277
+ ```
278
+
279
+ With `-e`, code changes take effect without reinstalling; just restart your AI client. To switch back to the published version, run `pip uninstall -y action1-mcp-server`, then `pip install action1-mcp-server`.
280
+
281
+ > [!NOTE]
282
+ > The package requires `mcp<2`. Version 2.x of the `mcp` SDK renamed `FastMCP`, and the server has not been migrated yet.
283
+
284
+ **Tests**
285
+
286
+ ```bash
287
+ python test_server.py --offline
288
+ ```
289
+
290
+ Runs without network access. It checks the API map, tool registration, pagination (against simulated responses) and the safeguards (blocked routes, `GET`-only code, secret and token masking).
291
+
292
+ ```bash
293
+ python test_server.py
294
+ ```
295
+
296
+ Needs `ACTION1_CLIENT_ID` and `ACTION1_CLIENT_SECRET`. It calls every mapped route with `limit=2`, reuses the IDs it finds to test routes that need one, then calls every tool. Routes the credential's role can't reach are reported as `SEM PERMISSÃO`, not as failures. It takes about 1 minute.
297
+
298
+ To try the server in the MCP Inspector (requires `uv` and `npx`), run the command below and set `ACTION1_CLIENT_ID` and `ACTION1_CLIENT_SECRET` under *Environment Variables* before connecting:
299
+
300
+ ```bash
301
+ mcp dev src/action1_mcp/server.py
302
+ ```
303
+
304
+ <details>
305
+ <summary><b>Releasing a new version</b></summary>
306
+
307
+ 1. Bump the version in `pyproject.toml` (`version`) and `src/action1_mcp/__init__.py` (`__version__`). PyPI never accepts the same version number twice.
308
+ 2. Run both test modes.
309
+ 3. Build:
310
+ ```powershell
311
+ Remove-Item -Recurse -Force dist -ErrorAction SilentlyContinue; uv build
312
+ ```
313
+ 4. Publish with a PyPI token scoped to the `action1-mcp-server` project:
314
+ ```powershell
315
+ $env:UV_PUBLISH_TOKEN = "pypi-..."; uv publish
316
+ ```
317
+ 5. Check in a clean environment: `pip install --upgrade action1-mcp-server`, then `pip show action1-mcp-server`.
318
+ 6. Commit and push to GitHub.
319
+
320
+ If a published version is broken, **yank** it on PyPI (project → Releases → version → *Yank*) and publish the fix as a new version. Don't delete files: deletion is permanent and the file name can never be reused.
321
+
322
+ </details>
323
+
324
+ ## 📋 Changelog
325
+
326
+ | Version | Changes |
327
+ |---|---|
328
+ | **1.0.0** | First release. |
329
+
330
+ ## 📄 License
331
+
332
+ [MIT](LICENSE) © João Pedro Rodrigues