@pmoses-s1/sentinelone-mcp 1.0.0 → 1.1.0

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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ ## 1.1.0 — 2026-05-28
4
+
5
+ ### Added
6
+ - **Streamable HTTP transport.** New `--transport http` mode (default stays `stdio`). Single-endpoint POST `/mcp` per the MCP 2024-11-05 spec, plus `/healthz` for load balancer probes. Implementation is pure `node:http`, no new dependencies.
7
+ - **Per-user bearer token auth.** New `MCP_BEARER_TOKENS_FILE` env var pointing at a `{ "<name>": "<token>" }` JSON file gives each team member a stable name in audit logs and supports rotation. SIGHUP reloads tokens without dropping connections. `MCP_BEARER_TOKENS` env var (comma-separated raw tokens) is a fallback for small or quick-test setups.
8
+ - **Audit logging.** Every authenticated HTTP request emits `[audit] <ts> | <name> | <method> | <param-summary> | <status>` to stderr; systemd captures it via journald.
9
+ - **`S1_CREDS_FILE` credential resolver.** Highest-priority explicit path for credentials, useful for VM deployments and secret-store integrations (Vault, Doppler, 1Password Connect, sealed-secrets).
10
+ - **Deploy artifacts** under `deploy/`:
11
+ - `install.sh` — one-shot installer for Mac and Linux. `--user` mode for individuals, `--server` mode for Linux VMs (creates `mcp` system user, generates an initial bearer token, installs systemd unit, starts the service).
12
+ - `systemd/sentinelone-mcp.service` — hardened unit with `NoNewPrivileges`, `ProtectSystem=strict`, `MemoryDenyWriteExecute`, SIGHUP-as-reload.
13
+ - `caddy/Caddyfile.example` — TLS reverse proxy template with bearer header gate and streaming-friendly flush.
14
+ - `README.md` — full topology guide (single-user local, single-user HTTP, team VM-hosted) with day-2 operations.
15
+ - **Test suite.** Three new files under `tests/`, runnable via `npm test`:
16
+ - `smoke.test.mjs` — source-of-truth tool inventory (26 tools by name).
17
+ - `stdio-transport.test.mjs` — JSON-RPC round trip via spawned stdio process.
18
+ - `http-transport.test.mjs` — HTTP transport end-to-end, bearer auth happy/sad paths.
19
+ - **README auto-regenerator** at `scripts/regen-readme-tools-table.mjs`. `npm run regen:readme` keeps the README table in sync with `ALL_TOOLS`. `npm run regen:readme -- --check` fails when stale (suitable for CI).
20
+
21
+ ### Fixed
22
+ - **README tool table.** Previous count was 19; actual is 26. Auto-generated now.
23
+ - **Header comment in `index.js`.** Previously said 21; updated to 26.
24
+ - **`purple_ai_query`** removed from the documentation. The tool itself was removed 2026-05-03 because the underlying API requires a browser-session `teamToken` that service-account API tokens never obtain. The README, `index.js`, and `docs/mcp-tools.md` no longer reference it.
25
+ - **`uam_set_status` documentation.** Doc previously said valid status values include `CLOSED`. The source enum is `NEW`, `IN_PROGRESS`, `RESOLVED`; doc now matches.
26
+
27
+ ### Changed
28
+ - **Refactored** dispatch out of `index.js` into `lib/server-core.js` so both transports use one code path. `lib/stdio-transport.js` is the extracted stdio loop; `lib/http-transport.js` is new.
29
+ - **package.json**:
30
+ - `version` 1.0.0 → 1.1.0
31
+ - new scripts: `start:http`, `test`, `regen:readme`
32
+ - new files included in the npm tarball: `deploy/`, `scripts/`, `CHANGELOG.md`
33
+
34
+ ### Compatibility
35
+ - Default invocation is unchanged: `npx -y @pmoses-s1/sentinelone-mcp` still produces a stdio MCP server with identical behavior to 1.0.0.
36
+ - Existing `claude_desktop_config.json` and `.mcp.json` configs work without modification.
37
+ - The 26 tools, 2 resources, and 2 prompts are unchanged from the late-1.0.0 line; only the documentation now matches reality.
38
+
39
+ ## 1.0.0 — 2026-05-07
40
+
41
+ Initial public release.
42
+ - 19 tools across PowerQuery, S1 Mgmt REST, UAM, SDL API, Hyperautomation.
43
+ - stdio transport only.
44
+ - Credentials via env vars or auto-discovered `credentials.json`.
package/README.md CHANGED
@@ -1,180 +1,229 @@
1
1
  # SentinelOne MCP Server
2
2
 
3
- A Model Context Protocol (MCP) server that orchestrates all six SentinelOne skills and their APIs. Built in pure Node.js 18+ with zero external dependencies.
3
+ Model Context Protocol server orchestrating the SentinelOne Management Console, Singularity Data Lake, UAM Alert Interface, and Hyperautomation APIs. Pure Node.js 18+, zero external dependencies. Supports both stdio (for Claude Desktop / Cowork / Claude Code) and Streamable HTTP (for team-shared VM deployments) transports.
4
+
5
+ - **Single-user, local:** install with `npx`, plug into Claude Desktop in 30 seconds.
6
+ - **Team, VM-hosted:** install on one Linux box, per-user bearer tokens, audit logs, SIGHUP-reloadable rotation.
7
+
8
+ See **[deploy/README.md](./deploy/README.md)** for the full deployment walkthrough across all three topologies.
4
9
 
5
10
  ## What this exposes
6
11
 
7
- **19 tools** covering every skill in the plugin:
12
+ <!-- BEGIN AUTO-GENERATED TOOLS TABLE -->
13
+ **26 tools** across PowerQuery, Mgmt Console, SDL API, Hyperautomation, and UAM Ingest:
8
14
 
9
15
  | Group | Tool | Skill |
10
16
  |-------|------|-------|
11
17
  | PowerQuery | `powerquery_enumerate_sources` | sentinelone-powerquery |
12
18
  | PowerQuery | `powerquery_run` | sentinelone-powerquery |
13
19
  | PowerQuery | `powerquery_schema_discover` | sentinelone-powerquery |
20
+ | Mgmt Console | `purple_ai_alert_summary` | sentinelone-mgmt-console-api |
21
+ | Mgmt Console | `s1_api_delete` | sentinelone-mgmt-console-api |
14
22
  | Mgmt Console | `s1_api_get` | sentinelone-mgmt-console-api |
23
+ | Mgmt Console | `s1_api_patch` | sentinelone-mgmt-console-api |
15
24
  | Mgmt Console | `s1_api_post` | sentinelone-mgmt-console-api |
16
- | Mgmt Console | `purple_ai_query` | sentinelone-mgmt-console-api |
17
- | Mgmt Console | `uam_list_alerts` | sentinelone-mgmt-console-api |
18
- | Mgmt Console | `uam_get_alert` | sentinelone-mgmt-console-api |
25
+ | Mgmt Console | `s1_api_put` | sentinelone-mgmt-console-api |
19
26
  | Mgmt Console | `uam_add_note` | sentinelone-mgmt-console-api |
27
+ | Mgmt Console | `uam_get_alert` | sentinelone-mgmt-console-api |
28
+ | Mgmt Console | `uam_list_alerts` | sentinelone-mgmt-console-api |
20
29
  | Mgmt Console | `uam_set_status` | sentinelone-mgmt-console-api |
21
- | SDL API | `sdl_list_files` | sentinelone-sdl-api / sdl-dashboard / sdl-log-parser |
30
+ | SDL API | `sdl_delete_file` | sentinelone-sdl-api |
22
31
  | SDL API | `sdl_get_file` | sentinelone-sdl-api / sdl-dashboard / sdl-log-parser |
32
+ | SDL API | `sdl_list_files` | sentinelone-sdl-api / sdl-dashboard / sdl-log-parser |
23
33
  | SDL API | `sdl_put_file` | sentinelone-sdl-api / sdl-dashboard / sdl-log-parser |
24
- | SDL API | `sdl_delete_file` | sentinelone-sdl-api |
25
34
  | SDL API | `sdl_upload_logs` | sentinelone-sdl-api / sdl-log-parser |
26
- | Hyperautomation | `ha_list_workflows` | sentinelone-hyperautomation |
35
+ | Hyperautomation | `ha_archive_workflow` | sentinelone-hyperautomation |
36
+ | Hyperautomation | `ha_export_workflow` | sentinelone-hyperautomation |
27
37
  | Hyperautomation | `ha_get_workflow` | sentinelone-hyperautomation |
28
38
  | Hyperautomation | `ha_import_workflow` | sentinelone-hyperautomation |
29
- | Hyperautomation | `ha_export_workflow` | sentinelone-hyperautomation |
39
+ | Hyperautomation | `ha_list_workflows` | sentinelone-hyperautomation |
40
+ | UAM Ingest | `uam_ingest_alert` | sentinelone-mgmt-console-api (UAM Alert Interface) |
41
+ | UAM Ingest | `uam_post_alert` | sentinelone-mgmt-console-api (UAM Alert Interface) |
42
+ | UAM Ingest | `uam_post_indicators` | sentinelone-mgmt-console-api (UAM Alert Interface) |
43
+ <!-- END AUTO-GENERATED TOOLS TABLE -->
30
44
 
31
45
  **2 resources:**
32
- - `sentinelone://soc-context`: CLAUDE.md (full SOC analyst operating instructions)
33
- - `sentinelone://credentials-status`: Which credentials are configured
46
+ - `sentinelone://soc-context` — `CLAUDE.md`, the Principal SOC Analyst operating instructions.
47
+ - `sentinelone://credentials-status` which credentials are configured and which API surfaces are available.
34
48
 
35
49
  **2 prompts:**
36
- - `soc_analyst`: Embeds CLAUDE.md as a system prompt; call at session start
37
- - `session_init`: Structured initialization: enumerate sources + triage alerts in parallel
50
+ - `soc_analyst` embeds `CLAUDE.md` as a system prompt; call at session start.
51
+ - `session_init` structured init: enumerate sources + triage alerts in parallel.
38
52
 
39
- ## Prerequisites
53
+ ## Quick install
40
54
 
41
- - Node.js 18 or later
42
- - No `npm install` needed: zero external dependencies
55
+ Two paths, pick one:
43
56
 
44
- ## Credentials
57
+ ### Easiest: `npx` via Claude Desktop / Claude Code / Cowork
45
58
 
46
- Credentials are passed as environment variables in `claude_desktop_config.json` (see Installation below). The server also auto-discovers a `credentials.json` file by searching from the working directory upward as a backwards-compatible fallback for direct-skill users.
59
+ Add this to `claude_desktop_config.json` (or `.mcp.json` for Claude Code):
47
60
 
48
- `S1_CONSOLE_URL` and `S1_CONSOLE_API_TOKEN` are sufficient for most tools. Add the SDL keys only if you need `sdl_upload_logs` (requires `SDL_LOG_WRITE_KEY`) or `sdl_put_file` (requires `SDL_CONFIG_WRITE_KEY`).
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "sentinelone-mcp": {
65
+ "command": "npx",
66
+ "args": ["-y", "@pmoses-s1/sentinelone-mcp@1.1.0"],
67
+ "env": {
68
+ "S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
69
+ "S1_CONSOLE_API_TOKEN": "eyJ...",
70
+ "S1_HEC_INGEST_URL": "https://ingest.us1.sentinelone.net",
71
+ "SDL_XDR_URL": "https://xdr.us1.sentinelone.net",
72
+ "SDL_LOG_READ_KEY": "...",
73
+ "SDL_LOG_WRITE_KEY": "...",
74
+ "SDL_CONFIG_READ_KEY": "...",
75
+ "SDL_CONFIG_WRITE_KEY": "..."
76
+ }
77
+ }
78
+ }
79
+ }
80
+ ```
49
81
 
50
- | Variable | Description |
51
- |----------|-------------|
52
- | `S1_CONSOLE_URL` | Your console URL, e.g. `https://usea1-acme.sentinelone.net` |
53
- | `S1_CONSOLE_API_TOKEN` | Management Console API token (Settings → Users → Service Users) |
54
- | `S1_HEC_INGEST_URL` | HEC ingest host, e.g. `https://ingest.us1.sentinelone.net` |
55
- | `SDL_XDR_URL` | SDL tenant URL, e.g. `https://xdr.us1.sentinelone.net` |
56
- | `SDL_LOG_WRITE_KEY` | SDL Log Write key (required for `sdl_upload_logs` only) |
57
- | `SDL_LOG_READ_KEY` | SDL Log Read key (required for SDL query operations) |
58
- | `SDL_CONFIG_WRITE_KEY` | SDL Config Write key (required for `sdl_put_file`) |
59
- | `SDL_CONFIG_READ_KEY` | SDL Config Read key (required for `sdl_list_files`, `sdl_get_file`) |
82
+ Restart Claude Desktop. `npx -y` caches the package on first launch.
60
83
 
61
- ## Run the server
84
+ ### Reproducible: install script
62
85
 
63
86
  ```bash
64
- # From the published npm package (no clone, no install)
65
- npx -y @pmoses-s1/sentinelone-mcp
66
-
67
- # Or from a local clone (development)
68
- node /path/to/claude-skills/sentinelone-mcp/index.js
87
+ curl -fsSL https://raw.githubusercontent.com/pmoses-s1/claude-skills/main/sentinelone-mcp/deploy/install.sh | bash
69
88
  ```
70
89
 
71
- ## Installation
90
+ This sets up a per-user npm prefix if needed, installs the package, drops a credentials skeleton at `~/.config/sentinelone/credentials.json` (mode 0600), and prints the wiring instructions for Claude Desktop.
72
91
 
73
- ### Why you need this (or the allowlist alternative)
92
+ For VM deployments, the same script in `--server` mode does everything (system user, systemd unit, initial bearer token, service start). See [deploy/README.md](./deploy/README.md).
74
93
 
75
- The Claude sandbox proxy blocks outbound HTTPS to `*.sentinelone.net` by default. There are two ways to fix this:
94
+ ## Credentials
76
95
 
77
- **Option A: Install sentinelone-mcp (recommended).** The MCP server runs as a local process on your machine outside the sandbox. All API calls go directly from your machine to SentinelOne, bypassing the sandbox proxy entirely. No allowlist changes needed.
96
+ `S1_CONSOLE_URL` and `S1_CONSOLE_API_TOKEN` are sufficient for the PowerQuery, Mgmt Console REST, Purple AI summary, and UAM tools (16 of the 26).
78
97
 
79
- **Option B: Add `*.sentinelone.net` to the Claude sandbox allowlist.** In Claude Desktop go to Settings → Claude Code → Network Access and add `*.sentinelone.net` to the allowed domains. This lets the skills' Python scripts reach the API directly from inside the sandbox. Use this if you prefer to keep everything running in the sandbox rather than install a local server.
98
+ `S1_HEC_INGEST_URL` is **required** for the three UAM Ingest tools (`uam_ingest_alert`, `uam_post_indicators`, `uam_post_alert`). Without it those tools error at call time, the rest still work.
80
99
 
81
- Most users should use Option A: it requires no admin changes and keeps credentials out of the sandbox environment.
100
+ `SDL_*` keys gate the SDL tools as follows:
82
101
 
83
- ### Option A: Add to Claude Desktop
102
+ | Variable | Description | Required for |
103
+ |----------|-------------|--------------|
104
+ | `S1_CONSOLE_URL` | Console URL, e.g. `https://usea1-acme.sentinelone.net` | All Mgmt + PowerQuery tools |
105
+ | `S1_CONSOLE_API_TOKEN` | Mgmt Console API token (Settings → Users → Service Users) | All Mgmt + PowerQuery + UAM tools |
106
+ | `S1_HEC_INGEST_URL` | HEC ingest host, e.g. `https://ingest.us1.sentinelone.net` | `uam_ingest_alert`, `uam_post_indicators`, `uam_post_alert` |
107
+ | `SDL_XDR_URL` | SDL tenant URL, e.g. `https://xdr.us1.sentinelone.net` | All `sdl_*` tools and `powerquery_schema_discover` |
108
+ | `SDL_LOG_READ_KEY` | SDL Log Read key | SDL query operations |
109
+ | `SDL_LOG_WRITE_KEY` | SDL Log Write key (console JWT NOT accepted by this endpoint) | `sdl_upload_logs` |
110
+ | `SDL_CONFIG_READ_KEY` | SDL Config Read key | `sdl_list_files`, `sdl_get_file` |
111
+ | `SDL_CONFIG_WRITE_KEY` | SDL Config Write key | `sdl_put_file`, `sdl_delete_file` |
84
112
 
85
- In `~/Library/Application Support/Claude/claude_desktop_config.json`, add the `sentinelone-mcp` entry to your `mcpServers` block. The server runs via `npx` directly from npm, so there is no clone, no install, and no absolute path to manage. Credentials go in the `env` section:
113
+ ### Credential resolution order (highest priority wins)
86
114
 
87
- ```json
88
- {
89
- "mcpServers": {
90
- "sentinelone-mcp": {
91
- "command": "npx",
92
- "args": ["-y", "@pmoses-s1/sentinelone-mcp"],
93
- "env": {
94
- "S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
95
- "S1_CONSOLE_API_TOKEN": "eyJ...your-api-token...",
96
- "S1_HEC_INGEST_URL": "https://ingest.us1.sentinelone.net",
97
- "SDL_XDR_URL": "https://xdr.us1.sentinelone.net",
98
- "SDL_LOG_WRITE_KEY": "0Z1Fy0...",
99
- "SDL_LOG_READ_KEY": "0tzj...",
100
- "SDL_CONFIG_WRITE_KEY": "0mXas6PD...",
101
- "SDL_CONFIG_READ_KEY": "0MQTx..."
102
- }
103
- }
104
- }
105
- }
106
- ```
115
+ 1. Environment variables (set in `claude_desktop_config.json` `env`, systemd `EnvironmentFile`, or your shell).
116
+ 2. `S1_CREDS_FILE` — explicit path to a JSON file (recommended for VM deployments and secret-store integrations).
117
+ 3. `COWORK_WORKSPACE/credentials.json`.
118
+ 4. Walk-up from the current working directory looking for `credentials.json`.
119
+ 5. `~/mnt/<folder>/credentials.json` (Cowork workspace mounts).
120
+ 6. `$CLAUDE_CONFIG_DIR/sentinelone/credentials.json`.
121
+ 7. `~/.config/sentinelone/credentials.json`.
107
122
 
108
- `npx -y` answers "yes" to the install prompt on first launch, fetches the package, and caches it. Subsequent launches start instantly from the cache. Restart Claude Desktop after saving.
123
+ The server logs the resolved credential source at startup so you can diagnose surprise overrides.
109
124
 
110
- `S1_CONSOLE_URL` and `S1_CONSOLE_API_TOKEN` are the minimum required for most tools. Include the SDL keys only if you need log ingest or parser/dashboard deploy. Set `S1_CLAUDE_MD_PATH` if you keep CLAUDE.md outside your Cowork project folder.
125
+ ## Transport modes
111
126
 
112
- ### Option A: Add to Claude Code
127
+ ### stdio (default)
113
128
 
114
- In `.mcp.json` at your project root, or `~/.mcp.json` globally. Same npx invocation, same env block:
129
+ The transport used by Claude Desktop, Claude Code, Claude Cowork, and any other client launched via `npx` / `node index.js`.
115
130
 
116
- ```json
117
- {
118
- "mcpServers": {
119
- "sentinelone-mcp": {
120
- "command": "npx",
121
- "args": ["-y", "@pmoses-s1/sentinelone-mcp"],
122
- "env": {
123
- "S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
124
- "S1_CONSOLE_API_TOKEN": "eyJ...your-api-token..."
125
- }
126
- }
127
- }
128
- }
131
+ ```bash
132
+ sentinelone-mcp # auto-discovers credentials
133
+ node index.js # same as above, from a local clone
129
134
  ```
130
135
 
131
- ### Option A: Run from a local clone (development only)
132
-
133
- If you are developing the MCP server itself, replace the `npx` invocation with a path to your clone:
136
+ ### Streamable HTTP
134
137
 
135
- ```json
136
- "sentinelone-mcp": {
137
- "command": "node",
138
- "args": ["/absolute/path/to/claude-skills/sentinelone-mcp/index.js"],
139
- "env": { "...": "..." }
140
- }
138
+ ```bash
139
+ sentinelone-mcp --transport http # 127.0.0.1:8765/mcp, no auth
140
+ sentinelone-mcp --transport http --host 0.0.0.0 # all interfaces, no auth (loud warning)
141
+ MCP_BEARER_TOKENS_FILE=/etc/sentinelone-mcp/bearer-tokens.json \
142
+ sentinelone-mcp --transport http --host 0.0.0.0 # team mode with per-user tokens
141
143
  ```
142
144
 
143
- ### Option B: Sandbox allowlist (no MCP server)
145
+ Configuration via flags or environment variables:
146
+
147
+ | Flag | Env var | Default | Purpose |
148
+ |------|---------|---------|---------|
149
+ | `--transport` | `MCP_TRANSPORT` | `stdio` | `stdio` or `http`. |
150
+ | `--host` | `MCP_HTTP_HOST` | `127.0.0.1` | HTTP bind address. Use `0.0.0.0` for cross-host access. |
151
+ | `--port` | `MCP_HTTP_PORT` | `8765` | HTTP port. |
152
+ | `--path` | `MCP_HTTP_PATH` | `/mcp` | MCP endpoint path. |
153
+
154
+ In HTTP mode the server exposes:
155
+
156
+ - `POST /mcp` — accepts JSON-RPC, returns JSON-RPC. The MCP entry point.
157
+ - `GET /healthz` — returns `200 ok`. For load balancer probes; no auth.
158
+
159
+ ### Team auth: bearer tokens
144
160
 
145
- If you prefer to run API calls from inside the Claude sandbox rather than install a local server, add `*.sentinelone.net` to the network allowlist:
161
+ To enable team auth, set one of:
146
162
 
147
- 1. Open Claude Desktop Settings Claude Code Network Access
148
- 2. Add `*.sentinelone.net` to the allowed domains
149
- 3. Restart Claude Desktop
163
+ - `MCP_BEARER_TOKENS_FILE=/path/to/file.json` (recommended). The file is `{ "<name>": "<token>", ... }`. Names appear in audit logs; revoking a user is a one-line edit. SIGHUP reloads without restart.
164
+ - `MCP_BEARER_TOKENS="token1,token2,..."` (fallback, no per-user names).
150
165
 
151
- The skills' Python scripts (`s1_client.py`, `sdl_client.py`, etc.) will then reach the API directly. No MCP server required for the skills to work.
166
+ Token rotation:
152
167
 
153
- ## Workflow: session startup
168
+ ```bash
169
+ sudo vim /etc/sentinelone-mcp/bearer-tokens.json # add/remove entries
170
+ sudo systemctl reload sentinelone-mcp # SIGHUP, no connection drops
171
+ ```
172
+
173
+ If neither env var is set, HTTP transport runs **without** authentication and the server logs a warning at startup. That's acceptable for `--host 127.0.0.1` single-user use; never use it on `0.0.0.0` in production.
174
+
175
+ ### Audit log
176
+
177
+ Every authenticated HTTP request emits a structured stderr line that systemd captures via journald:
154
178
 
155
- When connecting to this MCP server, start every session with:
179
+ ```
180
+ [audit] 2026-05-28T15:01:22.413Z | alice | tools/call | name=powerquery_run | 200 ok
181
+ [audit] 2026-05-28T15:01:34.221Z | bob | tools/list | - | 200 ok
182
+ [audit] 2026-05-28T17:03:11.221Z | - | - | - | 401 unauthorized
183
+ ```
156
184
 
157
- 1. Read the `soc_analyst` prompt (or the `sentinelone://soc-context` resource) to load operating instructions from CLAUDE.md.
158
- 2. Call `powerquery_enumerate_sources` to discover active SDL data sources (mandatory: never assume sources from a prior session).
159
- 3. In parallel, call `uam_list_alerts` with `filter="status=OPEN"` to pull active alerts.
185
+ ## CLI reference
160
186
 
161
- The `session_init` prompt automates steps 2-3 as a structured prompt.
187
+ ```
188
+ sentinelone-mcp [options]
189
+
190
+ OPTIONS
191
+ --transport <stdio|http> Transport. Default: stdio.
192
+ --host <host> HTTP bind address. Default: 127.0.0.1.
193
+ --port <port> HTTP port. Default: 8765.
194
+ --path <path> HTTP MCP endpoint path. Default: /mcp.
195
+ -h, --help Show help.
196
+ -v, --version Show server version.
197
+ ```
162
198
 
163
199
  ## Architecture
164
200
 
165
201
  ```
166
202
  sentinelone-mcp/
167
- index.js Raw MCP JSON-RPC over stdio (no SDK dependency)
203
+ index.js Entry: flag parsing + transport selection
168
204
  lib/
169
- credentials.js Auto-discovers credentials.json (env vars > file > walk-up > ~/mnt/*)
170
- s1.js S1 Mgmt REST API + LRQ PowerQuery + Purple AI + UAM GraphQL
171
- sdl.js SDL config files (get/put/list) + V1 query + uploadLogs
205
+ server-core.js Tool registry, JSON-RPC dispatch (transport-agnostic)
206
+ stdio-transport.js stdin/stdout JSON-RPC loop
207
+ http-transport.js Streamable HTTP (node:http, zero deps)
208
+ auth.js Bearer token allowlist with SIGHUP reload
209
+ credentials.js S1 + SDL credential resolution
210
+ s1.js Mgmt REST + LRQ PowerQuery + Purple AI + UAM GraphQL
211
+ sdl.js SDL config files + V1 query + uploadLogs
212
+ uam-ingest.js HEC alert/indicator ingestion
172
213
  tools/
173
- powerquery.js PowerQuery enumerate/run/schema-discover tools
174
- mgmt-console.js S1 REST + Purple AI + UAM tools
175
- sdl-api.js SDL config file + log ingestion tools
176
- hyperautomation.js Hyperautomation list/get/import/export tools
177
- README.md
214
+ powerquery.js PowerQuery enumerate/run/schema-discover
215
+ mgmt-console.js S1 REST verbs + Purple AI summary + UAM
216
+ sdl-api.js SDL config file + log ingestion tools
217
+ hyperautomation.js Hyperautomation list/get/import/export/archive
218
+ uam-ingest.js UAM Alert Interface ingestion tools
219
+ deploy/
220
+ install.sh One-shot installer (Mac and Linux)
221
+ systemd/ Service unit for Linux VM deployments
222
+ caddy/ TLS reverse proxy template
223
+ README.md Deployment walkthrough
224
+ scripts/
225
+ regen-readme-tools-table.mjs Tools-table regenerator (no drift)
226
+ tests/ Smoke + stdio + HTTP test suites (node --test)
178
227
  ```
179
228
 
180
229
  ## Auth patterns (implemented)
@@ -185,15 +234,38 @@ sentinelone-mcp/
185
234
  | LRQ PowerQuery | `Authorization: Bearer <jwt>` | Same token, different prefix |
186
235
  | Purple AI GraphQL | `Authorization: ApiToken <jwt>` | `S1_CONSOLE_API_TOKEN` |
187
236
  | UAM GraphQL | `Authorization: ApiToken <jwt>` | `S1_CONSOLE_API_TOKEN` |
237
+ | UAM HEC ingest | `Authorization: Bearer <jwt>` | `S1_CONSOLE_API_TOKEN` |
188
238
  | SDL config ops | `Authorization: Bearer <key>` | `SDL_CONFIG_WRITE_KEY` or console JWT |
189
239
  | SDL uploadLogs | `Authorization: Bearer <key>` | `SDL_LOG_WRITE_KEY` only (console JWT rejected) |
190
240
 
241
+ ## Testing
242
+
243
+ ```bash
244
+ npm test
245
+ ```
246
+
247
+ Three test suites under `tests/`:
248
+
249
+ - `smoke.test.mjs` — introspects `ALL_TOOLS` directly, no spawning. Asserts 26 tools by name; catches any drift between code and the README regenerator.
250
+ - `stdio-transport.test.mjs` — spawns the server in stdio mode, exercises `initialize`, `tools/list`, `resources/list`, `prompts/list`, and error handling.
251
+ - `http-transport.test.mjs` — spawns in HTTP mode on a random ephemeral port, exercises `/healthz`, `POST /mcp`, both auth-required and auth-optional flows, and the env-var token fallback.
252
+
253
+ The smoke suite is the source of truth for the tool count and is what `scripts/regen-readme-tools-table.mjs` derives the README table from. If the table goes stale, `npm run regen:readme -- --check` fails CI; `npm run regen:readme` fixes it.
254
+
191
255
  ## Updating CLAUDE.md
192
256
 
193
- The `sentinelone://soc-context` resource and `soc_analyst` prompt load CLAUDE.md at server startup. Resolution order (highest priority wins):
257
+ The `sentinelone://soc-context` resource and `soc_analyst` prompt load `CLAUDE.md` at server startup. Resolution order:
258
+
259
+ 1. `S1_CLAUDE_MD_PATH` env var (explicit absolute path).
260
+ 2. `<cwd>/CLAUDE.md` — your Cowork project folder, when launched from there.
261
+ 3. Same-dir / parent / grandparent of the server's `index.js` — when running from a git clone.
262
+
263
+ For npx installs without a CLAUDE.md nearby, set `S1_CLAUDE_MD_PATH` in the `env` block of `claude_desktop_config.json` to point at the one in your Cowork project folder. Restart Claude Desktop to pick up edits.
264
+
265
+ ## Removed tools
266
+
267
+ `purple_ai_query` and `purple_ai_investigate` were removed on 2026-05-03. Both required a browser-session `teamToken` from `/sdl/v2/graphql` that service-account API tokens never obtain (returns `AsimovError` / `SERVICE_ERROR`). Use `mcp__purple-mcp__purple_ai` instead, which holds the right credentials.
194
268
 
195
- 1. `S1_CLAUDE_MD_PATH` env var (explicit absolute path)
196
- 2. `<cwd>/CLAUDE.md` (your Cowork project folder when launched from a project)
197
- 3. Same-dir / parent / grandparent of the server's `index.js` (when running from a git clone)
269
+ ## Version history
198
270
 
199
- For npx installs, drop a copy of CLAUDE.md into your Cowork project folder, or set `S1_CLAUDE_MD_PATH` in the `env` block of `claude_desktop_config.json`. Restart Claude Desktop to pick up changes.
271
+ See [CHANGELOG.md](./CHANGELOG.md).