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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"name": "s1-secops-skills",
"source": "./plugins/s1-secops-skills",
"description": "SentinelOne SecOps skills for Claude: PowerQuery threat hunting and STAR/Custom Detection rules; Management Console API; Singularity Data Lake API; SDL dashboards; log parsing (OCSF); Hyperautomation SOAR; z-score anomaly baselining; autonomous DFIR alert investigation (soc-investigator); and one-prompt SDL solutions: source onboarding, asset enrichment, UEBA, ingest health, detection exclusions, Risk-Based Alerting, alert noise reduction, and Detection as Code.",
"version": "1.3.0",
"version": "1.3.1",
"author": {
"name": "Prithvi Moses",
"email": "prithvi.moses@sentinelone.com"
Expand Down
4 changes: 2 additions & 2 deletions mcp/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ End-user reference: [`docs/docker.md`](../docs/docker.md). This file is for imag

## Layout

```
```bash
docker/
├── Dockerfile # multi-arch, all 3 MCPs at pinned versions
├── entrypoint.sh # dispatcher: argv[1] selects which MCP to run
Expand All @@ -29,7 +29,7 @@ When bumping a pin, edit both. They are checked once via `grep` in CI; a mismatc
| What | Source | Current pin |
|---|---|---|
| Image version (`IMAGE_VERSION`) | this repo | `1.3.1` |
| `@pmoses-s1/s1-secops-mcp` | npm | `1.3.1` |
| `@pmoses-s1/s1-secops-mcp` | npm | `1.3.3` |
| `@burtthecoder/mcp-virustotal` | npm | `1.0.21` |
| `purple-mcp` | git | `07d4992` (Sentinel-One/purple-mcp `v0.7.0`, 2026-06-26) |

Expand Down
2 changes: 1 addition & 1 deletion mcp/docker/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ set -euo pipefail
IMAGE_VERSION="${IMAGE_VERSION:-1.3.1}"

# ── Pinned MCP versions ──────────────────────────────────────────────────────
S1_MCP_VERSION="${S1_MCP_VERSION:-1.3.1}"
S1_MCP_VERSION="${S1_MCP_VERSION:-1.3.3}"
VT_MCP_PACKAGE="${VT_MCP_PACKAGE:-@burtthecoder/mcp-virustotal}"
VT_MCP_VERSION="${VT_MCP_VERSION:-1.0.21}"
# purple-mcp v0.7.0 (2026-06-26). Pinned to the release commit, not a floating
Expand Down
119 changes: 118 additions & 1 deletion mcp/s1-secops-mcp/CHANGELOG.md

Large diffs are not rendered by default.

23 changes: 13 additions & 10 deletions mcp/s1-secops-mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ See **[deploy/README.md](./deploy/README.md)** for the full deployment walkthrou
<!-- END AUTO-GENERATED TOOLS TABLE -->

**2 resources:**

- `sentinelone://soc-context`: `CLAUDE.md`, the Principal SOC Analyst operating instructions.
- `sentinelone://credentials-status`: which credentials are configured and which API surfaces are available.

**2 prompts:**

- `soc_analyst`: embeds `CLAUDE.md` as a system prompt; call at session start.
- `session_init`: structured init: enumerate sources + triage alerts in parallel.

Expand All @@ -65,11 +67,11 @@ Add this to `claude_desktop_config.json` (or `.mcp.json` for Claude Code):
"mcpServers": {
"s1-secops-mcp": {
"command": "npx",
"args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.1"],
"args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.3"],
"env": {
"S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
"S1_CONSOLE_API_TOKEN": "eyJ...",
"S1_HEC_INGEST_URL": "https://ingest.us1.sentinelone.net",
"S1_HEC_INGEST_URL": "https://ingest.us1.sentinelone.net"
}
}
}
Expand Down Expand Up @@ -126,16 +128,16 @@ Cmd+Q and reopen Claude Desktop. SentinelOne credentials live on the VM in `/etc

Credential keys, where to get each one, and the two token types are documented canonically in **[docs/credentials.md](../../plugins/s1-secops-skills/docs/credentials.md)**. This section adds the MCP-server-specific detail: which tools each key gates, and the server's full credential-resolution order.

`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).
`S1_CONSOLE_URL` and `S1_CONSOLE_API_TOKEN` are sufficient for the PowerQuery, Mgmt Console REST, Purple AI summary, UAM, Hyperautomation, and SDL config-file tools (22 of the 26).

`S1_HEC_INGEST_URL` is **required** for the three UAM Ingest tools (`uam_ingest_alert`, `uam_post_indicators`, `uam_post_alert`) and for `hec_ingest`. Without it those tools error at call time; the rest still work.
`S1_HEC_INGEST_URL` is **required** for the three UAM Ingest tools (`uam_ingest_alert`, `uam_post_indicators`, `uam_post_alert`) and for `hec_ingest`, the only four tools that need it. Without it those tools error at call time; the rest still work.

`SDL_*` keys gate the SDL tools as follows:
The SDL config-file tools (`sdl_list_files`, `sdl_get_file`, `sdl_put_file`, `sdl_delete_file`) are authorised by `S1_CONSOLE_API_TOKEN` against `POST <console>/sdl/v2/graphql`. The scoped SDL keys (`SDL_CONFIG_READ_KEY`, `SDL_CONFIG_WRITE_KEY`, `SDL_LOG_READ_KEY`, `SDL_LOG_WRITE_KEY`, `SDL_XDR_URL`) are retired and are no longer read.

| Variable | Description | Required for |
|----------|-------------|--------------|
| `S1_CONSOLE_URL` | Console URL, e.g. `https://usea1-acme.sentinelone.net` | All Mgmt + PowerQuery tools |
| `S1_CONSOLE_API_TOKEN` | Mgmt Console API token (Settings → Users → Service Users) | All Mgmt + PowerQuery + UAM tools |
| `S1_CONSOLE_URL` | Console URL, e.g. `https://usea1-acme.sentinelone.net` | All Mgmt + PowerQuery + SDL tools |
| `S1_CONSOLE_API_TOKEN` | Mgmt Console API token (Settings → Users → Service Users) | All Mgmt + PowerQuery + UAM + SDL config-file tools |
| `S1_HEC_INGEST_URL` | HEC ingest host, e.g. `https://ingest.us1.sentinelone.net` | `uam_ingest_alert`, `uam_post_indicators`, `uam_post_alert`, `hec_ingest` |

### Credential resolution order (highest priority wins)
Expand Down Expand Up @@ -204,7 +206,7 @@ If neither env var is set, HTTP transport runs **without** authentication and th

Every authenticated HTTP request emits a structured stderr line that systemd captures via journald:

```
```json
[audit] 2026-05-28T15:01:22.413Z | alice | tools/call | name=powerquery_run | 200 ok
[audit] 2026-05-28T15:01:34.221Z | bob | tools/list | - | 200 ok
[audit] 2026-05-28T17:03:11.221Z | - | - | - | 401 unauthorized
Expand Down Expand Up @@ -434,7 +436,7 @@ The `maxRows` (`powerquery_run`) and `first` (`uam_list_alerts`) parameters are

## CLI reference

```
```text
s1-secops-mcp [options]

OPTIONS
Expand All @@ -448,7 +450,7 @@ OPTIONS

## Architecture

```
```text
s1-secops-mcp/
index.js Entry: flag parsing + transport selection
lib/
Expand Down Expand Up @@ -485,6 +487,7 @@ s1-secops-mcp/
| Purple AI GraphQL | `Authorization: ApiToken <jwt>` | `S1_CONSOLE_API_TOKEN` |
| UAM GraphQL | `Authorization: ApiToken <jwt>` | `S1_CONSOLE_API_TOKEN` |
| UAM HEC ingest | `Authorization: Bearer <jwt>` | `S1_CONSOLE_API_TOKEN` |
| SDL config files (`POST /sdl/v2/graphql`) | `Authorization: Bearer <jwt>`, an `s1-scope` header is ignored, not rejected | `S1_CONSOLE_API_TOKEN` |

## Testing

Expand Down
23 changes: 18 additions & 5 deletions mcp/s1-secops-mcp/deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ bash /tmp/s1-mcp-install.sh --user
```

That runs `install.sh --user`, which:

1. Confirms Node 18+ is present (errors out with install hints if not).
2. Sets up a per-user npm prefix at `~/.npm-global` if one isn't configured.
3. Installs `@pmoses-s1/s1-secops-mcp` globally for your user.
Expand All @@ -33,7 +34,7 @@ Then edit `~/.config/sentinelone/credentials.json` with your real values:
{
"S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
"S1_CONSOLE_API_TOKEN": "eyJ...",
"S1_HEC_INGEST_URL": "https://ingest.us1.sentinelone.net",
"S1_HEC_INGEST_URL": "https://ingest.us1.sentinelone.net"
}
```

Expand All @@ -56,7 +57,7 @@ Or, equivalently, by package name without the install:
"mcpServers": {
"s1-secops-mcp": {
"command": "npx",
"args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.1"]
"args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.3"]
}
}
}
Expand Down Expand Up @@ -117,40 +118,48 @@ Team members connect from their Claude clients with their own bearer token. Audi
1. **Provision the VM.** Anything that runs systemd is fine: Ubuntu 22.04 LTS, Debian 12, Rocky/Alma 9, etc.

2. **Install Node 18+.** Pick one:

```bash
# Ubuntu / Debian
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
```

```bash
# Rocky / Alma
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo dnf install -y nodejs
```

3. **Run the installer in server mode:**

```bash
curl -fsSL https://raw.githubusercontent.com/pmoses-s1/claude-skills/main/s1-secops-mcp/deploy/install.sh | sudo bash -s -- --server
```

It creates the `mcp` user, drops `/etc/s1-secops-mcp/credentials.json` (placeholder) and `/etc/s1-secops-mcp/bearer-tokens.json` (one freshly-generated admin token, printed once to stdout), installs the systemd unit, and starts the service.

4. **Fill in real SentinelOne credentials:**

```bash
sudo vim /etc/s1-secops-mcp/credentials.json
sudo systemctl reload s1-secops-mcp
curl -s http://127.0.0.1:8765/healthz # -> ok
```

5. **Put TLS in front with Caddy** (the recommended option):

```bash
sudo apt install -y caddy
sudo cp /usr/lib/node_modules/@pmoses-s1/s1-secops-mcp/deploy/caddy/Caddyfile.example /etc/caddy/Caddyfile
sudo vim /etc/caddy/Caddyfile # change mcp.s1.internal to your DNS name
sudo systemctl reload caddy
```

Default Caddyfile uses `tls internal` which signs with Caddy's own CA. Distribute `/var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt` to your team for trust, or use `tls <your-email>` with a publicly resolvable hostname for Let's Encrypt.

6. **Add team members.** Generate a token per person and append to the file:

```bash
sudo bash -c 'cat > /etc/s1-secops-mcp/bearer-tokens.json' <<EOF
{
Expand All @@ -164,9 +173,11 @@ Team members connect from their Claude clients with their own bearer token. Audi
sudo chown mcp:mcp /etc/s1-secops-mcp/bearer-tokens.json
sudo systemctl reload s1-secops-mcp # SIGHUP, no downtime
```

Hand each person their token over a secure channel (1Password, Signal, etc.).

7. **Connect from a Claude client.** Each user adds the server to their config with their personal token:

```json
{
"mcpServers": {
Expand All @@ -182,6 +193,7 @@ Team members connect from their Claude clients with their own bearer token. Audi
```

8. **Verify end-to-end.** From a team member's machine:

```bash
curl -s -X POST https://mcp.s1.internal/mcp \
-H "Authorization: Bearer $TOKEN" \
Expand All @@ -191,6 +203,7 @@ Team members connect from their Claude clients with their own bearer token. Audi
```

9. **Watch the audit log.** Every authenticated request is logged with the bearer name, method, and param summary:

```bash
sudo journalctl -u s1-secops-mcp -f | grep '\[audit\]'
# [audit] 2026-05-28T15:01:22.413Z | alice | tools/call | name=powerquery_run | 200 ok
Expand Down Expand Up @@ -231,7 +244,7 @@ sudo systemctl restart s1-secops-mcp

The structured audit lines look like:

```
```json
[audit] 2026-05-28T15:01:22.413Z | alice | tools/call | name=powerquery_run | 200 ok
[audit] 2026-05-28T16:42:55.108Z | bob | tools/list | - | 200 ok
[audit] 2026-05-28T17:03:11.221Z | - | - | - | 401 unauthorized
Expand Down Expand Up @@ -296,7 +309,7 @@ The instance's `*.compute.internal` DNS name (e.g. `ip-172-31-7-227.ap-southeast

If you try to issue a cert for the EC2 public DNS, LE returns:

```
```text
HTTP 400 urn:ietf:params:acme:error:rejectedIdentifier
The ACME server refuses to issue a certificate for this domain name, because it is forbidden by policy
```
Expand Down Expand Up @@ -363,7 +376,7 @@ Both block the W+X memory mappings V8 needs to JIT JavaScript. Adding them cause

These are supported but not first-class:

- **Docker / docker-compose.** Not shipped in this version. The single-file Node binary doesn't need it. If you want a container, the install is `FROM node:20-alpine` + `RUN npm install -g @pmoses-s1/s1-secops-mcp@1.3.1` + `CMD ["s1-secops-mcp", "--transport", "http", "--host", "0.0.0.0"]`. Mount creds at `/etc/s1-secops-mcp/credentials.json` and tokens at `/etc/s1-secops-mcp/bearer-tokens.json`.
- **Docker / docker-compose.** Not shipped in this version. The single-file Node binary doesn't need it. If you want a container, the install is `FROM node:20-alpine` + `RUN npm install -g @pmoses-s1/s1-secops-mcp@1.3.3` + `CMD ["s1-secops-mcp", "--transport", "http", "--host", "0.0.0.0"]`. Mount creds at `/etc/s1-secops-mcp/credentials.json` and tokens at `/etc/s1-secops-mcp/bearer-tokens.json`.

- **External bridge (`supergateway`, `mcp-proxy`).** Pre-1.1.0 deployments used these to wrap the stdio-only server. They still work; this server's native HTTP mode is functionally equivalent and removes the extra process. Prefer native unless you have a specific reason.

Expand Down
2 changes: 1 addition & 1 deletion mcp/s1-secops-mcp/deploy/bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \

## How it works

```
```text
+----------------+ stdio JSON-RPC +--------+ HTTPS POST /mcp +------+
| Claude Desktop | <------------------------------> | bridge | <---------------------------> | VM |
+----------------+ +--------+ Bearer auth, JSON in/out +------+
Expand Down
Loading
Loading