Skip to main content
Glama
nh4ttruong

secobserve-mcp

Read SecObserve Metrics

secobserve_product_metrics
Read-onlyIdempotent

Retrieve pre-aggregated vulnerability counts by severity and status for a product, group, or instance. Choose current, timeline, delta, or status views to track changes without querying raw findings.

Instructions

Read pre-aggregated observation counts for a product, a group, or the whole instance.

Far cheaper than counting rows with secobserve_list: these come from the metrics tables a background job maintains. That also means they are as old as the last calculation -- kind="status" tells you how old, and is worth reading before quoting a number as current.

License counts are not in here: use secobserve_list("products") for the per-product *_licenses_count fields, or the license_overview action on license_components for counts grouped by license.

kind="delta" answers "what changed between these two dates", which the API itself cannot: it offers relative windows only, has no delta endpoint, and its timeline skips the days the background job did not run.

Args: kind (str): "current", "timeline", "delta" or "status". product_id (Optional[int]): One product, or every product in a group when the id is a product group. Resolved before the metrics are read, because the endpoints answer for the whole instance when the id matches nothing. Omit for the instance. age (Optional[MetricsAge]): Window for "timeline": "Past 7 days", "Past 30 days", "Past 90 days", "Past 365 days". since (Optional[str]): Start of the range for "delta", YYYY-MM-DD. until (Optional[str]): End of the range for "delta", YYYY-MM-DD, today when omitted. response_format (ResponseFormat): "json" (default) or "markdown".

Returns: str: For kind="current", a JSON object of fifteen counts: six by severity (active_critical, active_high, active_medium, active_low, active_none, active_unknown) and nine by status (open, affected, resolved, duplicate, false_positive, in_review, not_affected, not_security, risk_accepted). It carries an extra "stale" block when the metrics job has not run for several of its own calculation intervals, because the endpoint then answers 200 with every count at zero instead of failing. The warning says how long ago it last ran. For kind="timeline", a JSON object keyed by ISO date, each value the counts for that day. For kind="delta", {"since": {"requested", "used"}, "until": {"requested", "used"}, "start": counts, "end": counts, "delta": signed change per counter, "missing_days": days in the range the job never wrote}. Quote "used" rather than "requested" whenever they differ, since the counts come from the dates that exist. For kind="status", {"last_calculated": ISO timestamp, "calculation_interval": minutes}.

Examples: - Use when: "how many critical findings are open in product 12?" -> kind="current", product_id=12 - Use when: "is our backlog growing?" -> kind="timeline", age="Past 90 days" - Use when: "what changed in August?" -> kind="delta", since="2026-08-01", until="2026-08-31" - Use when: a metric looks wrong -> kind="status", to check the job has run. - Don't use when: you need the findings themselves (use secobserve_list). - Don't use when: you need license counts, see above.

Error Handling: 403 means no view permission on the product, and an unknown product_id is refused rather than silently widened to the whole instance. An empty timeline usually means the metrics job has not run yet for that window -- check kind="status". A "stale" block on kind="current" is not an error, but the zeros under it are not an answer: report the staleness instead of the counts. kind="delta" refuses a since after until, a since older than everything the instance retains (the error names the earliest date it has), and since or until on another kind.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ageNoTime window, for kind='timeline' only. Omit for the full retained history.
kindYes'current' = observation counts by severity and status as of the last calculation; 'timeline' = one entry per day; 'delta' = the signed change between since and until; 'status' = when metrics were last calculated and how often, which tells you how stale 'current' is.
sinceNoStart of the range for kind='delta', ISO YYYY-MM-DD. The nearest date with metrics at or before it is used, and the result names it.
untilNoEnd of the range for kind='delta', ISO YYYY-MM-DD. Defaults to today, resolved like since.
product_idNoRestrict to one product, or to every product in a product group when the id is a group. An id that matches neither is refused. Omit for the whole instance.
response_formatNoOutput format.json

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv0.5.0
    • changedInput schema / properties / product_id / description
      Previous value: -"Restrict to one product, or to every product in a product group when the id is a group. Omit for the whole instance."New value: +"Restrict to one product, or to every product in a product group when the id is a group. An id that matches neither is refused. Omit for the whole instance."
  2. Changed10 schema fields changedv0.3.0
    • removedInput schema / $defs / MetricsInput
      Removed value: -{
      -  "additionalProperties": false,
      -  "description": "Input model for reading product metrics.",
      -  "properties": {
      -    "age": {
      -      "anyOf": [
      -        {
      -          "$ref": "#/$defs/MetricsAge"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "Time window, for kind='timeline' only. Omit for the full retained history."
      -    },
      -    "kind": {
      -      "description": "'current' = severity and license counts as of the last calculation; 'timeline' = one entry per day; 'status' = when metrics were last calculated and how often, which tells you how stale 'current' is.",
      -      "enum": [
      -        "current",
      -        "timeline",
      -        "status"
      -      ],
      -      "title": "Kind",
      -      "type": "string"
      -    },
      -    "product_id": {
      -      "anyOf": [
      -        {
      -          "minimum": 1,
      -          "type": "integer"
      -        },
      -        {
      -          "type": "null"
      -        }
      -      ],
      -      "default": null,
      -      "description": "Restrict to one product, or to every product in a product group when the id is a group. Omit for the whole instance.",
      -      "title": "Product Id"
      -    },
      -    "response_format": {
      -      "$ref": "#/$defs/ResponseFormat",
      -      "default": "json",
      -      "description": "Output format."
      -    }
      -  },
      -  "required": [
      -    "kind"
      -  ],
      -  "title": "MetricsInput",
      -  "type": "object"
      -}
    • addedInput schema / additionalProperties
      Added value: +false
    • addedInput schema / properties / age
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/MetricsAge"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Time window, for kind='timeline' only. Omit for the full retained history."
      +}
    • addedInput schema / properties / kind
      Added value: +{
      +  "description": "'current' = observation counts by severity and status as of the last calculation; 'timeline' = one entry per day; 'delta' = the signed change between since and until; 'status' = when metrics were last calculated and how often, which tells you how stale 'current' is.",
      +  "enum": [
      +    "current",
      +    "timeline",
      +    "status",
      +    "delta"
      +  ],
      +  "title": "Kind",
      +  "type": "string"
      +}
    • removedInput schema / properties / params
      Removed value: -{
      -  "$ref": "#/$defs/MetricsInput"
      -}
    • addedInput schema / properties / product_id
      Added value: +{
      +  "anyOf": [
      +    {
      +      "minimum": 1,
      +      "type": "integer"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Restrict to one product, or to every product in a product group when the id is a group. Omit for the whole instance.",
      +  "title": "Product Id"
      +}
    • addedInput schema / properties / response_format
      Added value: +{
      +  "$ref": "#/$defs/ResponseFormat",
      +  "default": "json",
      +  "description": "Output format."
      +}
    • addedInput schema / properties / since
      Added value: +{
      +  "anyOf": [
      +    {
      +      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "Start of the range for kind='delta', ISO YYYY-MM-DD. The nearest date with metrics at or before it is used, and the result names it.",
      +  "title": "Since"
      +}
    • addedInput schema / properties / until
      Added value: +{
      +  "anyOf": [
      +    {
      +      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "End of the range for kind='delta', ISO YYYY-MM-DD. Defaults to today, resolved like since.",
      +  "title": "Until"
      +}
    • changedInput schema / required
      Previous value: -[
      -  "params"
      -]New value: +[
      +  "kind"
      +]
  3. First observedv0.1.2

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already mark the tool readOnly, idempotent, and non-destructive, and the description adds substantial behavioral context beyond that: metrics can be stale, kind='status' reveals staleness, a 'stale' block means zeros are not meaningful, delta uses actual available dates rather than requested ones, and unknown product_id is refused rather than widened. The error-handling section further clarifies 403 and empty-timeline behavior. This fully compensates for any behavior the annotations do not state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every section earns its place: purpose, staleness caveat, exclusions, parameter semantics, return shapes per kind, usage examples, and error handling. The most important scoping and staleness information is front-loaded in the first paragraph, and the structured Args/Returns/Examples/Error Handling layout makes it easy to scan. Given the tool's four modes, this length is justified.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is complex with four kinds, six parameters, staleness caveats, and nontrivial error conditions, and the description covers all of them: return shapes, date-resolution behavior, stale-data warnings, permission errors, and an empty-timeline diagnostic. The output schema exists and the Returns section still adds value by explaining 'used' vs 'requested' dates and the 'stale' block. This is complete enough for an agent to call the tool correctly in every documented scenario.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although schema coverage is 100%, the description adds meaning the schema alone does not convey: product_id resolves to a product group, an unmatched id is refused, since/until are snapped to the nearest available metrics date, age applies only to timeline, and response_format controls output type. The Returns section maps parameter combinations to concrete response shapes, which is more than the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Read pre-aggregated observation counts for a product, a group, or the whole instance.' It distinguishes itself from siblings by explicitly contrasting with secobserve_list ('Far cheaper than counting rows') and by excluding license counts ('License counts are not in here'). An agent can tell exactly what this tool does and what it does not do.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit 'Use when' and 'Don't use when' guidance with concrete examples, such as using kind='delta' because the API 'has no delta endpoint'. It also names alternatives for excluded cases: secobserve_list for findings and license counts, and the license_components action for license counts grouped by license. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.