Skip to content

feat(secops): add SIEM dashboard MCP tools (native, scheduled reports, Content Hub, legacy Looker and SOAR) - #331

Open
dandye wants to merge 3 commits into
google:mainfrom
dandye:feat__siem-dashboard-tools
Open

dandye wants to merge 3 commits into
google:mainfrom
dandye:feat__siem-dashboard-tools

Conversation

@dandye

@dandye dandye commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds 49 MCP tools to google-secops-mcp for Google SecOps (Chronicle SIEM) dashboards: native dashboards, charts and dashboard queries, scheduled reports, Content Hub featured dashboards, legacy (Looker) dashboards, and legacy SOAR dashboards. All HTTP goes through a new shared chronicle_api_request helper in secops_mcp/utils.py. Every tool in this PR was exercised against a live tenant (see Verification); endpoints that could not be are deferred and documented as future work.

Module Tools Chronicle service(s)
tools/dashboard_management.py 17 NativeDashboardService (list, get, create, update, duplicate, delete, add/edit/remove/duplicate chart, export, import), DashboardQueryService (execute, get), DashboardChartService (get, list, batchGet)
tools/dashboard_reports.py 11 DashboardScheduledReportService (list, get, create, update, delete, fetchHistory, duplicate, trigger), FeaturedContentNativeDashboardService (list, get, install)
tools/legacy_dashboards.py 4 legacy Looker DashboardService (get, list, delete, copy)
tools/legacy_soar_dashboard.py 17 every LegacySoarDashboardService verb (dashboards, widgets, widget values and case IDs, playbook monitoring, case trends, time-to-respond, import, save as report template)

Tool count for the server goes from 68 to 118 (plus the 1 shared helper module). The secops>=0.35.1 dependency floor is unchanged.

Not included

  • ListDashboardQueries is internal-only and ManageDashboardLease is not publicly visible (Method not visible to labels: {PUBLIC}).
  • Future work (noted in the README and docs): the Looker-to-native migration endpoints (dashboards:fetchMigratable, :batchMigrate, :fetchMigrationMetrics, :fetchMigrationJobs) are gated by the under-development enable_looker_migration_wizard feature (403 Looker migration wizard is not enabled for the customer on both tenants tried), and legacy dashboard creation (POST /dashboards) fails server-side (something went wrong, please try again later) with or without LookML. Implementations and unit tests for these five tools exist at commit 9686f9c on this branch and can be restored once the APIs are generally available.

Design notes

  • chronicle_api_request(chronicle, method, path, params=, json_body=) builds https://<region>-chronicle.googleapis.com/<version>/<instance>/... from the SDK client and works whether chronicle.base_url is the newer callable BaseUrl or the older plain string. Non-2xx responses raise with the status and body, which the tools surface as {"error": ...}.
  • Structured arguments (filters, visualization, chart, widget definitions, export payloads) accept either JSON values or JSON-encoded strings via parse_json_arg, since MCP clients differ here.
  • Optional params are Optional[T] = None; logging uses lazy %s formatting; every tool returns {"error": ...} on failure, matching the existing modules.
  • IDs may be passed bare or as full resource names. Path segments use the bare ID; body-level resource references use whatever form the backend expects (full names for chart/dashboard references in NativeDashboardService, the bare ID for DashboardScheduledReport.dashboard.name).

API behaviour learned from live verification (encoded in the tools)

  • create_dashboard / duplicate_dashboard must send type: CUSTOM and an access value (cannot create non custom dashboard, dashboard access is unspecified); access_type defaults to DASHBOARD_PRIVATE.
  • add_dashboard_chart must send startX/startY explicitly (proto3 presence; omitting them yields chartLayout in request: invalid argument) and chartDatasource.dataSources (defaults to ["UDM"]). Charts reference their saved query under chartDatasource.dashboardQuery.
  • edit_dashboard_chart enforces optimistic concurrency on both the chart and its saved query, so the tool fetches them first and sends their etags plus the query name; editMask paths are camelCase (dashboardChart.displayName, dashboardQuery.input).
  • import_dashboards needs the export payload unmodified (dashboard.name and etag present); the API assigns a new ID and returns it in results[].dashboard.
  • DashboardScheduledReport.dashboard.name must be the bare native dashboard ID; a full projects/.../nativeDashboards/{id} name fails with INTERNAL: failed to fetch native dashboard details.
  • install_featured_content_dashboard creates a type: MARKETPLACE dashboard whose ID equals the featured content ID; installing it again returns 409.
  • execute_dashboard_query returns 200 with an empty body (not an error) when the caller lacks UDM data access.
  • The legacySoarDashboard:* verbs require a SOAR-mapped user identity; a service account gets 401 You are not recognized (domain SOAR), the same behaviour as the other SOAR-proxied endpoints.

Verification

  • Unit tests: cd server/secops && uv run --python 3.11 --with pytest --with pytest-asyncio --with pytest-mock pytest tests/ -> 134 passed (one or more per tool, JSON-string argument parsing, error paths, and the shared helper).
  • Tool registration: 118 tools, no duplicate names. ruff check --select I,F,E9 and git diff --check clean on the new files.
  • Tenant A (us, service-account identity with UDM data access): all 17 dashboard_management tools, including a create -> update -> add chart -> edit chart (title, then query window) -> duplicate chart -> remove chart -> duplicate dashboard -> export -> delete -> import-restore -> delete round-trip, plus execute_dashboard_query (inline and saved query), list_dashboard_charts and batch_get_dashboard_charts. Also list_dashboard_scheduled_reports, the three Content Hub tools (install verified then removed), and the four legacy Looker tools (copy_legacy_dashboard predefined -> user copy, then delete_legacy_dashboard).
  • Tenant A, user identity (gcloud auth login --update-adc): all 17 legacy SOAR tools, including a create -> add widget -> query widget -> rename widget -> rename dashboard -> delete widget -> delete dashboard round-trip, an import -> delete round-trip, and legacy_soar_save_dashboard_as_report_template (creates a report template; the service has no delete verb, so one test template remains).
  • Tenant B (us, user identity, scheduled reports enabled with 8 pre-existing reports): the full scheduled-report lifecycle on a scratch dashboard (create with bare-ID and full-name inputs, get, update of dashboard + cron + recipients, fetchHistory, duplicate, trigger, delete), plus the native reads, execute_dashboard_query, Content Hub and legacy SOAR reads. Pre-existing reports were untouched.
  • Tenant-dependent: list_dashboard_charts returns 403 chart library is not enabled for the customer on Tenant B (works on Tenant A).
  • All scratch dashboards, charts, reports, and SOAR dashboards created during testing were deleted.

Docs

  • server/secops/README.md: four new tool sections, feature list entry 18, a use-case bullet, and the future-work note.
  • docs/servers/secops_mcp.md: parameter reference for all 49 tools, the future-work note, and Example 9 (build, query, schedule, export/import a dashboard).

…hboard APIs

Adds 54 tools to google-secops-mcp spanning every public Chronicle
dashboard service, implemented as direct REST calls through a new shared
`chronicle_api_request` helper in `secops_mcp/utils.py` (works across
`secops` SDK releases regardless of how `base_url` is exposed).

New tool modules under `secops_mcp/tools/`:

- `dashboard_management.py` (17): NativeDashboardService (list, get,
  create, update, duplicate, delete, add/edit/remove/duplicate chart,
  export, import), DashboardQueryService (execute, get) and
  DashboardChartService (get, list, batchGet).
- `dashboard_reports.py` (11): DashboardScheduledReportService (list, get,
  create, update, delete, fetchHistory, duplicate, trigger) and
  FeaturedContentNativeDashboardService (list, get, install).
- `legacy_dashboard_migration.py` (9): legacy Looker DashboardService
  (create, get, list, delete, copy) and the Looker-to-native migration
  workflow (fetchMigratable, batchMigrate, fetchMigrationMetrics,
  fetchMigrationJobs).
- `legacy_soar_dashboard.py` (17): every LegacySoarDashboardService verb
  (dashboards, widgets, widget values/case IDs, playbook monitoring, case
  trends, time-to-respond, import, save as report template).

Behaviour learned from live verification against a Chronicle tenant and
encoded in the tools:

- `create_dashboard` / `duplicate_dashboard` must send `type: CUSTOM` and
  an `access` value; both default to `DASHBOARD_PRIVATE`.
- `add_dashboard_chart` must send `startX`/`startY` explicitly (proto3
  presence) and `chartDatasource.dataSources` (defaults to `["UDM"]`).
- `edit_dashboard_chart` enforces optimistic concurrency, so the tool
  fetches the chart (and saved query) first and sends their `etag`s and
  the query `name`; `editMask` paths are camelCase.
- `import_dashboards` requires the export payload unmodified (`name` and
  `etag` present) and returns the new resource name in `results[]`.
- `ManageDashboardLease` is not visible to the public API surface and is
  intentionally not exposed.

Also adds `parse_json_arg` so structured arguments are accepted either as
JSON values or JSON-encoded strings, unit tests for every tool (142 passing
in `server/secops/tests`), and README / docs sections with a usage example.
@dandye
dandye requested a review from a team November 2, 2026 16:15
Live verification on a tenant with scheduled reports enabled showed that
DashboardScheduledReportService stores and resolves `dashboard.name` as
the bare native dashboard ID. Sending the full
`projects/.../nativeDashboards/{id}` resource name fails with
`INTERNAL: failed to fetch native dashboard details`.

`create_dashboard_scheduled_report` and `update_dashboard_scheduled_report`
now reduce full names to the bare ID. Verified live: create (bare ID and
full name inputs), get, update (dashboard + cron), fetchHistory,
duplicate, trigger, and delete all return 200.
…tools

Removes five tools that could not be verified against any available
tenant and notes them as future work in the README and docs:

- fetch_migratable_dashboards, batch_migrate_dashboards,
  fetch_dashboard_migration_metrics, fetch_dashboard_migration_jobs:
  gated by the under-development `enable_looker_migration_wizard`
  feature (`403 Looker migration wizard is not enabled for the customer`).
- create_legacy_dashboard: `POST /dashboards` fails server-side
  (`something went wrong, please try again later`) with or without LookML.

The remaining legacy Looker tools (get, list, delete, copy) were verified
live, so the module is renamed from legacy_dashboard_migration.py to
legacy_dashboards.py. Tool count drops from 123 to 118 (49 new dashboard
tools); 134 unit tests pass.
@dandye dandye changed the title feat(secops): add SIEM dashboard MCP tools covering all Chronicle dashboard APIs feat(secops): add SIEM dashboard MCP tools (native, scheduled reports, Content Hub, legacy Looker and SOAR) Nov 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant