Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions plugins/s1-secops-skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ Prerequisite: Docker Desktop (macOS/Windows) or Docker Engine (Linux), running.
docker pull ghcr.io/pmoses-s1/s1-mcps:1.3.1
```

`:1.2.3` is the current pinned release (bundles s1-secops-mcp 1.2.2, purple-mcp v0.7.0, virustotal-mcp 1.0.21). `:latest` also works; pin an explicit version for reproducible, forensically consistent installs. About 250 MB compressed.
`:1.3.1` is the current pinned release (bundles s1-secops-mcp 1.3.1, purple-mcp v0.7.0, virustotal-mcp 1.0.21). `:latest` also works; pin an explicit version for reproducible, forensically consistent installs. About 250 MB compressed.

**Step 2: Configure credentials**

Expand Down Expand Up @@ -403,14 +403,14 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
"command": "docker",
"args": [
"run", "-i", "--rm", "--pull=missing",
"-e", "PURPLEMCP_CONSOLE_TOKEN",
"-e", "PURPLEMCP_CONSOLE_BASE_URL",
"-e", "S1_CONSOLE_URL",
"-e", "S1_CONSOLE_API_TOKEN",
"ghcr.io/pmoses-s1/s1-mcps:1.3.1",
"purple-mcp"
],
"env": {
"PURPLEMCP_CONSOLE_TOKEN": "eyJ...your-api-token...",
"PURPLEMCP_CONSOLE_BASE_URL": "https://usea1-yourorg.sentinelone.net"
"S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
"S1_CONSOLE_API_TOKEN": "eyJ...your-api-token..."
}
},
"virustotal": {
Expand All @@ -437,8 +437,8 @@ Where to get each value:

| Placeholder | What it is | Where to get it |
|---|---|---|
| `S1_CONSOLE_URL`, `PURPLEMCP_CONSOLE_BASE_URL` | Your console URL | e.g. `https://usea1-yourorg.sentinelone.net` |
| `S1_CONSOLE_API_TOKEN`, `PURPLEMCP_CONSOLE_TOKEN` | Mgmt Console API token (the **same** token for both) | Settings → Users → Service Users → Create New Service User ([guide](https://community.sentinelone.com/s/article/000005291)) |
| `S1_CONSOLE_URL` | Your console URL | e.g. `https://usea1-yourorg.sentinelone.net` |
| `S1_CONSOLE_API_TOKEN` | Mgmt Console API token | Settings → Users → Service Users → Create New Service User ([guide](https://community.sentinelone.com/s/article/000005291)) |
| `S1_HEC_INGEST_URL` | HEC ingest host for your region | [Endpoint URLs by Region](https://community.sentinelone.com/s/article/000004961) |
| `VIRUSTOTAL_API_KEY` | VirusTotal API key (free tier is fine) | [virustotal.com/gui/my-apikey](https://www.virustotal.com/gui/my-apikey) |

Expand Down Expand Up @@ -475,7 +475,7 @@ The second command returns one JSON line with `serverInfo.name = "s1-secops-mcp-
| MCP shows red in Cowork → MCP Servers | Confirm Docker is running: `docker info \| head -3`. Start Docker Desktop, then restart Claude Desktop. |
| `Cannot connect to the Docker daemon` in the logs | Docker Desktop is not running. |
| `denied: permission_denied` from ghcr.io | Image is private or your network blocks ghcr.io: `docker login ghcr.io`, or check VPN/proxy. |
| `VIRUSTOTAL_API_KEY ... required`, or a `PURPLEMCP_*` validation error | The env value did not propagate; re-check the `env` block and that each `-e VAR` name matches a key. |
| `VIRUSTOTAL_API_KEY ... required`, or a `PURPLEMCP_*` validation error | The value did not reach the container. Check each `-e VAR` name has a matching key in the same block's `env`. |
| `S1 Mgmt API: NOT configured` | No console token reached the container; check `S1_CONSOLE_URL` + `S1_CONSOLE_API_TOKEN`. |

Per-MCP logs are at `~/Library/Logs/Claude/mcp-server-<name>.log`. Upgrading from 1.2.x? See **[docs/upgrading.md](./docs/upgrading.md)**.
Expand Down
Binary file modified plugins/s1-secops-skills/dist/hyperautomation.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/mgmt-console-api.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/powerquery.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/s1-secops-skills-v1.3.0.plugin
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/sdl-api.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/sdl-dashboard.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/sdl-log-parser.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/sdl-solutions.skill
Binary file not shown.
Binary file modified plugins/s1-secops-skills/dist/soc-investigator.skill
Binary file not shown.
4 changes: 2 additions & 2 deletions plugins/s1-secops-skills/docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ All three servers run from public package registries: `s1-secops-mcp` and `@burt
"stdio"
],
"env": {
"PURPLEMCP_CONSOLE_TOKEN": "eyJ...your-api-token...",
"PURPLEMCP_CONSOLE_BASE_URL": "https://usea1-yourorg.sentinelone.net"
"S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
"S1_CONSOLE_API_TOKEN": "eyJ...your-api-token..."
}
},
"virustotal": {
Expand Down
8 changes: 4 additions & 4 deletions plugins/s1-secops-skills/docs/vm-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ Paste this into the config file. Replace `<THEIR_PERSONAL_TOKEN>` with the beare
"command": "uvx",
"args": ["--from", "git+https://github.com/Sentinel-One/purple-mcp.git", "purple-mcp", "--mode", "stdio"],
"env": {
"PURPLEMCP_CONSOLE_TOKEN": "...",
"PURPLEMCP_CONSOLE_BASE_URL": "..."
"S1_CONSOLE_URL": "...",
"S1_CONSOLE_API_TOKEN": "..."
}
},
"virustotal": {
Expand Down Expand Up @@ -220,8 +220,8 @@ Open `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
"command": "uvx",
"args": ["--from", "git+https://github.com/Sentinel-One/purple-mcp.git", "purple-mcp", "--mode", "stdio"],
"env": {
"PURPLEMCP_CONSOLE_TOKEN": "...",
"PURPLEMCP_CONSOLE_BASE_URL": "..."
"S1_CONSOLE_URL": "...",
"S1_CONSOLE_API_TOKEN": "..."
}
},
"virustotal": {
Expand Down
6 changes: 3 additions & 3 deletions plugins/s1-secops-skills/skills/sdl-api/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: sdl-api
author: Prithvi Moses <prithvi.moses@sentinelone.com>
description: Use whenever the user wants to read data and manage configuration through the SentinelOne Singularity Data Lake (SDL) API: run queries or manage configuration files (parsers, dashboards, alerts, lookups, datatables) on a Scalyr/SDL/XDR tenant. Trigger on "SDL", "SDL API", "Singularity Data Lake", "Scalyr", "DataSet", "xdr.us1.sentinelone.net" or any "*.sentinelone.net/api/*" URL, and on the method names "query", "powerQuery", "facetQuery", "timeseriesQuery", "numericQuery", "getFile", "putFile", "listFiles". Also trigger on tasks like "run a powerQuery", "list configuration files", "edit my parser via API", "deploy a dashboard JSON", "compute the rate of failures over time", or anything involving SDL Bearer-token auth or the S1-Scope header. Wraps every SDL method with a Python client and CLI.
description: Use whenever the user wants to read data and manage configuration through the SentinelOne Singularity Data Lake (SDL) API: run queries or manage configuration files (parsers, dashboards, alerts, lookups, datatables) on a Scalyr/SDL/XDR tenant. Trigger on "SDL", "SDL API", "Singularity Data Lake", "Scalyr", "DataSet", or any "*.sentinelone.net/sdl/api/*" URL, and on the method names "query", "powerQuery", "facetQuery", "timeseriesQuery", "numericQuery", "getFile", "putFile", "listFiles". Also trigger on tasks like "run a powerQuery", "list configuration files", "edit my parser via API", "deploy a dashboard JSON", "compute the rate of failures over time", or anything involving SDL Bearer-token auth or the S1-Scope header. Wraps every SDL method with a Python client and CLI.
---

# SentinelOne SDL API
Expand Down Expand Up @@ -107,15 +107,15 @@ json.dump(schemas, open(out, "w"), indent=2)

**Direct MCP tools bypass sandbox proxy entirely.**

The Cowork sandboxed shell blocks all outbound HTTPS to `xdr.us1.sentinelone.net`. Use the
The Cowork sandboxed shell blocks all outbound HTTPS to `*.sentinelone.net`. Use the
s1-secops-mcp MCP tools instead, which run locally and bypass the proxy:

| Operation | s1-secops-mcp tool |
|---|---|
| PowerQuery | `mcp__s1-secops-mcp__powerquery_run` or `mcp__s1-secops-mcp__powerquery_schema_discover` |
| `put_file` / `get_file` / `list_files` | `mcp__s1-secops-mcp__sdl_put_file`, `mcp__s1-secops-mcp__sdl_get_file`, `mcp__s1-secops-mcp__sdl_list_files` |

All of these tools run on your local machine and make direct HTTPS calls to `xdr.us1.sentinelone.net`
All of these tools run on your local machine and make direct HTTPS calls to the console host
without sandbox proxy interference. No fallback or workaround needed.

```python
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ End of document. Treat this file as a living artefact: append new gotchas as the

These tools run locally and bypass the sandbox proxy entirely. Do not fall back to any other approach.

An earlier attempt at schema discovery in the same session ran inside the sandboxed Bash shell, which blocks all outbound HTTPS to `xdr.us1.sentinelone.net`. The V1 query calls returned a proxy error. Because the error was not recognized as a sandbox-specific block, the empty output was interpreted as the source having no useful fields, and a plausible-looking but entirely fabricated field list was deployed into the GRC dashboard panels.
An earlier attempt at schema discovery in the same session ran inside the sandboxed Bash shell, which blocks all outbound HTTPS to `*.sentinelone.net`. The V1 query calls returned a proxy error. Because the error was not recognized as a sandbox-specific block, the empty output was interpreted as the source having no useful fields, and a plausible-looking but entirely fabricated field list was deployed into the GRC dashboard panels.

The fabrication was only caught when the user asked to re-verify the schemas, at which point the operation was re-run using the s1-secops-mcp tools and returned the real data: 126 fields for `asset`, 41 fields for `ActivityFeed`.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_comment": "RBA RISK COLLECTOR (tenant-validated 2026-06-25). Scheduled HA flow: runs RBA contributor(s) via the SYNCHRONOUS SDL PowerQuery endpoint ({{S1_CONSOLE_URL}}/sdl/api/powerQuery on the xdr/SDL host, e.g. https://xdr.us1.sentinelone.net/api/powerQuery; NOT {{Connection.url}}/sdl/api/powerQuery, which returns an error and was the collector's original bug, tenant-confirmed 2026-07-06) which returns body.columns + body.values in one call, no async poll, maps rows to named objects (MAP_TABLE), shapes them to NDJSON risk events (JQ tojson, which safely escapes command-line quotes/backslashes), and publishes them into dataSource.name='risk'. DEPLOY ORDER (mandatory, the SDL connection CANNOT be bound via API): (1) import via POST /web/api/v2.1/hyper-automate/api/public/workflow-import-export/import?accountIds={{ACCOUNT_ID}} body {data:<this>}; (2) publish to Shared Draft: POST /web/api/v2.1/hyper-automate/api/v1/workflows/{id}/publish?accountIds={{ACCOUNT_ID}} (bodyless, 204); (3) PROMPT the user to bind the 'SentinelOne SDL' (Bearer) connection on BOTH http_request actions in the console; (4) after confirmation, activate: POST /web/api/v2.1/hyper-automate/api/v1/workflows/{id}/{version_id}/activation?accountIds={{ACCOUNT_ID}} (204); (5) run-now to test: POST /web/api/v2.1/hyper-automate/api/public/workflow-execution/manual/{id}/{version_id}?accountIds={{ACCOUNT_ID}}, then poll GET .../workflow-execution/{execution_id} until state=Completed (validated: 6/6 actions, ~2.9s, no error_actions). The example query below is the suspicious_powershell_flags contributor; production unions all contributors from rba_contributors.json. Schedule is daily 02:00 UTC for safe import; set hourly in the console for production RBA and match the query startTime to the interval. Both HTTP actions: use_authentication_data=true, bound to 'SentinelOne SDL'.",
"_comment": "RBA RISK COLLECTOR (tenant-validated 2026-06-25). Scheduled HA flow: runs RBA contributor(s) via the SYNCHRONOUS SDL PowerQuery endpoint ({{S1_CONSOLE_URL}}/sdl/api/powerQuery on the console host; NOT {{Connection.url}}/sdl/api/powerQuery, which returns an error and was the collector's original bug, tenant-confirmed 2026-07-06) which returns body.columns + body.values in one call, no async poll, maps rows to named objects (MAP_TABLE), shapes them to NDJSON risk events (JQ tojson, which safely escapes command-line quotes/backslashes), and publishes them into dataSource.name='risk'. DEPLOY ORDER (mandatory, the SDL connection CANNOT be bound via API): (1) import via POST /web/api/v2.1/hyper-automate/api/public/workflow-import-export/import?accountIds={{ACCOUNT_ID}} body {data:<this>}; (2) publish to Shared Draft: POST /web/api/v2.1/hyper-automate/api/v1/workflows/{id}/publish?accountIds={{ACCOUNT_ID}} (bodyless, 204); (3) PROMPT the user to bind the 'SentinelOne SDL' (Bearer) connection on BOTH http_request actions in the console; (4) after confirmation, activate: POST /web/api/v2.1/hyper-automate/api/v1/workflows/{id}/{version_id}/activation?accountIds={{ACCOUNT_ID}} (204); (5) run-now to test: POST /web/api/v2.1/hyper-automate/api/public/workflow-execution/manual/{id}/{version_id}?accountIds={{ACCOUNT_ID}}, then poll GET .../workflow-execution/{execution_id} until state=Completed (validated: 6/6 actions, ~2.9s, no error_actions). The example query below is the suspicious_powershell_flags contributor; production unions all contributors from rba_contributors.json. Schedule is daily 02:00 UTC for safe import; set hourly in the console for production RBA and match the query startTime to the interval. Both HTTP actions: use_authentication_data=true, bound to 'SentinelOne SDL'.",
"name": "{{PREFIX}} - RBA Risk Collector",
"description": "RBA collector: runs contributors via sync SDL PowerQuery, applies risk-factor multipliers, and publishes risk events into dataSource.name='risk'. Bind 'SentinelOne SDL' (Bearer) on both HTTP actions, then activate.",
"actions": [
Expand Down
Loading