@pmoses-s1/s1-secops-mcp 1.3.5 → 1.3.6

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 CHANGED
@@ -1,5 +1,53 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.6 - 2026-08-17
4
+
5
+ Behaviour change, found by deploying to a real site and not being able to see the
6
+ result.
7
+
8
+ ### Changed
9
+
10
+ - **`isPublic` / `public` now defaults to TRUE on dashboard creation** in both
11
+ clients, where the raw `createDashboardV2` API defaults it to false.
12
+
13
+ `access.owner` is set to the calling identity. With an API service-account
14
+ token that is `serviceuser-<uuid>@mgmt-<n>.sentinelone.net`, not a person, so a
15
+ private dashboard is readable through the API and **invisible in the console to
16
+ the human operator at any scope**. That is indistinguishable from a failed
17
+ deploy: the tool reports success, the object exists, and the user sees nothing.
18
+
19
+ Verified live: the identical config at the identical site scope went from
20
+ invisible to visible purely by recreating it with `public: true`, and every
21
+ pre-existing dashboard at that site carried `public: true`.
22
+
23
+ Pass `isPublic: false` / `public=False` deliberately for a dashboard that
24
+ should stay private to the service account. `shareResource` to a scope does not
25
+ flip `public`; the two are independent.
26
+
27
+ ### Documented
28
+
29
+ - **Dashboard names reject punctuation, with only `Invalid name` as the error.**
30
+ Probed one character class at a time against a live tenant:
31
+
32
+ | Accepted | Rejected |
33
+ |---|---|
34
+ | letters, digits, space, `-`, `_`, `.`, `/` | `(` `)` `[` `]` `{` `}` `:` `,` `&` `'` `%` `#` |
35
+
36
+ So `My Dashboard (prod)` fails with no indication of which character offended.
37
+ Recorded in the tool description, both client docstrings,
38
+ `sdl-api/references/config-file-graphql.md` and
39
+ `sdl-dashboard/references/deployment.md`.
40
+
41
+ ### Tests
42
+
43
+ 115 JS (+1), 61 Python client (+2), 19 panel-safety. The two default-value
44
+ assertions were updated and paired with explicit-false cases, so an accidental
45
+ revert of the default fails the suite.
46
+
47
+ ### Docker
48
+
49
+ Bundle image stays **1.3.2**; its npm pin moves to 1.3.6.
50
+
3
51
  ## 1.3.5 - 2026-08-17
4
52
 
5
53
  Completes the scope work in 1.3.4. **Upgrade from 1.3.4 is recommended.**
package/README.md CHANGED
@@ -73,7 +73,7 @@ Add this to `claude_desktop_config.json` (or `.mcp.json` for Claude Code):
73
73
  "mcpServers": {
74
74
  "s1-secops-mcp": {
75
75
  "command": "npx",
76
- "args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.5"],
76
+ "args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.6"],
77
77
  "env": {
78
78
  "S1_CONSOLE_URL": "https://usea1-yourorg.sentinelone.net",
79
79
  "S1_CONSOLE_API_TOKEN": "eyJ...",
package/deploy/README.md CHANGED
@@ -57,7 +57,7 @@ Or, equivalently, by package name without the install:
57
57
  "mcpServers": {
58
58
  "s1-secops-mcp": {
59
59
  "command": "npx",
60
- "args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.5"]
60
+ "args": ["-y", "@pmoses-s1/s1-secops-mcp@1.3.6"]
61
61
  }
62
62
  }
63
63
  }
@@ -376,7 +376,7 @@ Both block the W+X memory mappings V8 needs to JIT JavaScript. Adding them cause
376
376
 
377
377
  These are supported but not first-class:
378
378
 
379
- - **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.5` + `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`.
379
+ - **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.6` + `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`.
380
380
 
381
381
  - **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.
382
382
 
package/lib/sdl.js CHANGED
@@ -516,13 +516,25 @@ export async function getDashboard({ id, name, scope }) {
516
516
  * "Content is invalid json" / "Additional text after JSON object". Going
517
517
  * through this function cannot hit that class of error.
518
518
  *
519
+ * `isPublic` DEFAULTS TO TRUE, unlike the raw API, which defaults it to false.
520
+ * `access.owner` is set to the calling identity; with a service-account token that
521
+ * is `serviceuser-<uuid>@mgmt-<n>.sentinelone.net`, not a person. A private
522
+ * service-user dashboard is readable through the API and INVISIBLE in the console
523
+ * to the human operator, at any scope, which is indistinguishable from a failed
524
+ * deploy. Verified live: the same config at the same scope became visible purely
525
+ * by recreating it with public true. Pass `isPublic: false` deliberately if the
526
+ * dashboard really should be private to the service account.
527
+ *
528
+ * Names reject punctuation and answer only "Invalid name". Accepted: letters,
529
+ * digits, space, `-`, `_`, `.`, `/`. Rejected: `( ) [ ] { } : , & ' % #`.
530
+ *
519
531
  * DUPLICATE NAMES ARE ALLOWED HERE, unlike putConfigFile. The console itself
520
532
  * creates "<name> - Copy" siblings, and shareResource addresses dashboards by
521
533
  * id, so duplicate names are not the footgun they are for name-addressed
522
534
  * config-file writes. Set `failIfNameExists` to opt into the stricter
523
535
  * behaviour; it costs one extra listing call.
524
536
  */
525
- export async function createDashboard({ name, config, isPublic = false, scope, failIfNameExists = false }) {
537
+ export async function createDashboard({ name, config, isPublic = true, scope, failIfNameExists = false }) {
526
538
  if (!name || typeof name !== 'string') throw new Error('createDashboard requires a name');
527
539
  if (typeof config !== 'string' || !config.trim()) {
528
540
  throw new Error('createDashboard requires config as a JSON string (the full dashboard document).');
@@ -94,7 +94,7 @@ const PROMPTS = [
94
94
 
95
95
  export const SERVER_INFO = {
96
96
  name: 's1-secops-mcp-server',
97
- version: '1.3.5',
97
+ version: '1.3.6',
98
98
  };
99
99
 
100
100
  export const PROTOCOL_VERSION = '2024-11-05';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmoses-s1/s1-secops-mcp",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "MCP server orchestrating SentinelOne skills, APIs, and SOC analyst context. Stdio or Streamable HTTP transport with per-user bearer auth for team deployments.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/tools/sdl-api.js CHANGED
@@ -213,13 +213,13 @@ export const tools = [
213
213
  // ─── sdl_create_dashboard ─────────────────────────────────────────────────
214
214
  {
215
215
  name: 'sdl_create_dashboard',
216
- description: `Create a dashboard from a complete dashboard-JSON document via createDashboardV2, filed at the given scope. THIS IS THE PREFERRED WAY TO DEPLOY A NEW DASHBOARD: it accepts the whole document (configType, duration, description, tabs[]) in one call, unlike sdl_put_file which writes the raw config file. It also avoids the console's stub-append trap, where creating an empty dashboard in the UI and pasting JSON after the existing "{graphs: []}" stub yields "Content is invalid json / Additional text after JSON object" and leaves an empty dashboard behind. To deploy to a SITE, either pass scope as "<accountId>:<siteId>" here, or create at account scope and then use sdl_share_dashboard. Duplicate names ARE allowed (the console itself makes "<name> - Copy" siblings); set failIfNameExists to refuse instead. ${SCOPE_NOTE}`,
216
+ description: `Create a dashboard from a complete dashboard-JSON document via createDashboardV2, filed at the given scope. THIS IS THE PREFERRED WAY TO DEPLOY A NEW DASHBOARD: it accepts the whole document (configType, duration, description, tabs[]) in one call, unlike sdl_put_file which writes the raw config file. It also avoids the console's stub-append trap, where creating an empty dashboard in the UI and pasting JSON after the existing "{graphs: []}" stub yields "Content is invalid json / Additional text after JSON object" and leaves an empty dashboard behind. To deploy to a SITE, either pass scope as "<accountId>:<siteId>" here, or create at account scope and then use sdl_share_dashboard. Duplicate names ARE allowed (the console itself makes "<name> - Copy" siblings); set failIfNameExists to refuse instead. Names reject punctuation with only "Invalid name" as the error: accepted are letters, digits, space, hyphen, underscore, dot and slash; rejected are ( ) [ ] { } : , & ' % #. ${SCOPE_NOTE}`,
217
217
  inputSchema: {
218
218
  type: 'object',
219
219
  properties: {
220
220
  name: { type: 'string', description: 'Dashboard display name, e.g. "Metacortex Site Replica".' },
221
221
  config: { type: 'string', description: 'The full dashboard JSON document as a string: {"configType":"TABBED","duration":"24h","description":"...","tabs":[...]}. Validated as JSON before the mutation is sent.' },
222
- isPublic: { type: 'boolean', description: 'Share with all users in scope (the console\'s "Public" badge). Default false.' },
222
+ isPublic: { type: 'boolean', default: true, description: 'Share with all users in scope (the console\'s "Public" badge). DEFAULTS TO TRUE, unlike the raw API. access.owner is the calling identity, so with a service-account token a private dashboard is readable via API but INVISIBLE in the console to the human operator at any scope, which looks exactly like a failed deploy. Set false only if the dashboard should stay private to the service account.' },
223
223
  failIfNameExists: { type: 'boolean', description: 'Refuse if a dashboard of this name already exists at this scope. Default false, which permits siblings. Costs one extra listing call.' },
224
224
  scope: scopeProp,
225
225
  },