Skip to main content
Glama

Mallory

bulkUpdateFindings

Change the status and/or severity of a SET of findings in the caller's own organization, selected either by filter or by an explicit list of uuids. Use it for "dismiss all the unrotated-secret findings", "mark everything on foo/bar medium". For ONE finding you already hold a uuid for, use updateFinding instead; this tool is for sets you can only describe.

READ THIS BEFORE CALLING — IT WRITES THE MOMENT IT IS CALLED. There is no dry run, no preview mode and no confirmation step inside this tool, and no single call that puts back what it changed. The check has to happen BEFORE the call, in conversation with the user:

  1. Work out the selection, and resolve it yourself with listFindings using the SAME filters.

  2. Show the user what it covers — the count, and the findings themselves (name them if there are only a few).

  3. Say in one sentence what will change and how many findings it affects.

  4. Get a plain yes. Then call this ONCE. Never call this tool to find out what would match: listFindings and queryApi answer that and write nothing. Never call it on your own initiative, and never as a tidy-up you decided on yourself.

SCOPE is required and has no default. Over MCP it must be "tenant" — every finding in the organization, including ones filed months ago by people who are not in this conversation. "this_thread" only means anything inside the Mallory chat app, which is the only place a conversation thread exists; here it can only fail with MISSING_THREAD. If you do get MISSING_THREAD, do not "fix" it by re-sending with "tenant": that is a far larger write than the one that just failed, and the user has not agreed to it.

SELECTING

  • Pass filters or uuids, never both, and never invent a uuid — use only uuids a previous tool result actually returned (at most 500).

  • A tenant selection needs at least one filter, or a uuid list — there is no "everything we have" call. A selection filtered ONLY by status and/or severity still covers essentially the whole organization; the result flags that as broad, and you must say so explicitly when you report back.

  • A phrase like "the unrotated-secret findings" names a problem TYPE, not an asset: find the definition first with queryApi, path "/v1/finding_definitions" (slug__contains / title__contains), then filter on definition_slug, or definition_slug__in when several definitions match the phrase. definition_category is a whole category and much broader than a slug — confirm before using it.

  • An asset is asset_identifier (e.g. "foo/bar" for a GitHub repo). Try the exact value first and fall back to asset_identifier__contains only if it matched nothing.

  • Add status: "open" unless the user said otherwise: re-dismissing already-dismissed findings is churn they did not ask for.

  • search is fuzzy full text over title, asset_identifier, body, summary, remediation and asset_details — the only way to reach a cloud account id, region or owner, none of which are filterable columns. It is also the easiest way to hit findings the user did not mean, so with search especially, show them the listFindings result before you call.

BOUNDS AND REFUSALS — each is an answer, not an obstacle to route around:

  • TOO_MANY_MATCHES: the selection is over the 500-finding limit for one update. Narrow it (a definition_slug, a category, an asset filter, a created_at range). Do NOT page through it, and do NOT split it into several calls to get under the limit — each call is its own write, and the user agreed to one change rather than to a sequence of them.

  • NO_MATCHES: nothing matched, or everything matched already holds the value you are setting. When you set ONE field on a filters selection, findings already at that value are excluded from the selection, so the count you get back is the real blast radius and repeating the call for the same request is inert. Setting status AND severity together drops that exclusion — the selector cannot express "not both at once" — so there the count can include findings that were already correct, and a repeat call writes them all again. A uuids selection is never narrowed that way — the uuid list IS the selection — so a repeat call there rewrites every finding you named. Never re-issue a call to find out whether the first one landed: the result told you. Tell the user there was nothing to change and stop. Do NOT loosen the filters and retry until something matches: a selection that only appeared after you widened it is not the one the user described.

  • PLAN_LIMIT / FORBIDDEN: nothing was written and nothing is broken. Say so plainly and do not retry.

In user-facing status messages, use "fixed" and "mark as fixed" consistently.

ENUM VALUES ARE CASE-SENSITIVE AND EACH FIELD HAS ITS OWN SET. severity is UPPERCASE (CRITICAL, HIGH, MEDIUM, LOW, INFO). status is lowercase with exactly three values: open, fixed, dismissed — "resolved", "closed" and "assigned" are not statuses. The resolution reasons (false_positive, accepted_risk, not_applicable, duplicate, fixed_externally, other) are a SEPARATE field that this tool cannot set: false_positive is not a status. definition_category is lowercase-hyphenated.

WHAT IT CANNOT DO: title, summary, body, remediation, confidence and qualifier — those are updateFinding, one finding at a time, because they are per-asset prose or part of core's dedupe key. resolution_reason is settable by NEITHER tool: it is recorded in the Mallory UI when someone closes a finding there, so listFindings can read and filter on it but nothing here writes it. It cannot create findings (createFinding). Setting status back to "open" is allowed but unusual; reopening two findings onto the same asset trips core's open-finding uniqueness constraint and rolls the whole batch back, so do not retry that — reopen one at a time.

Input Schema

NameRequiredDescriptionDefault
setYesThe change to apply to every selected finding. At least one field must be set.
scopeYesREQUIRED, and a real decision — there is no default and you must not guess. 'this_thread' restricts the update to findings created in THIS conversation; that is what a user means by 'those findings', 'the ones you just filed', 'these'. 'tenant' searches every finding in the organisation, including ones filed months ago by someone else — use it only when the user clearly means their whole findings backlog. If the wording could mean either, ASK before calling this tool.
uuidsNoExact findings to update, by uuid. Use ONLY when you already have real uuids from a previous tool result — never invent or guess one. Mutually exclusive with `filters`, and requires scope 'tenant': a uuid list is already exact, so thread scoping could only silently drop findings you named. Core still enforces that every uuid belongs to the caller's tenant.
filtersNoSelect findings by attribute instead of by uuid. Mutually exclusive with `uuids`. Required when scope is 'tenant'; optional when scope is 'this_thread' (omit it to mean every finding from this conversation).

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observed

TDQS

A4.9/5.0
Behavior5/5

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

No annotations exist, so the description carries the full burden and does: it discloses that it writes immediately with no dry run, no preview, no confirmation, and no single call that reverses the change. It further documents the 500-finding bound, what NO_MATCHES / TOO_MANY_MATCHES / PLAN_LIMIT / FORBIDDEN mean, the repeat-call semantics per selection type, and the batch-rollback on reopen collisions.

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

Conciseness4/5

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

Front-loaded with purpose then the write-now warning, and organized under clear headers (SCOPE, SELECTING, BOUNDS AND REFUSALS, ENUM VALUES, WHAT IT CANNOT DO). It is nonetheless very long, and the 'don't call it to see what matches' and 'don't retry after NO_MATCHES' points are restated in two or three places, which could be tightened.

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?

For a destructive, nested-schema, no-annotations, no-output-schema batch mutation, everything an agent needs is present: pre-call verification procedure, scope decision rule, selection construction, error interpretation, and what the tool cannot do versus updateFinding/createFinding. It even flags that the result marks broad selections so the agent can report blast radius.

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?

Schema coverage is already 100%, but the description adds real selection strategy beyond it: filters/uuids are mutually exclusive, uuids must come from prior tool output, tenant selections need at least one filter, add status:"open" by default, try asset_identifier exact before __contains, and resolve problem-type phrases to definition_slug via queryApi first. Its enum case-sensitivity recap (severity UPPERCASE, status three lowercase values, resolution_reason being a separate unsettable field) resolves the most likely invocation error.

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 opening sentence states a specific verb+resource+scope: change status/severity of a SET of findings in the caller's own organization, by filter or uuid list. It explicitly contrasts with the closest sibling ("For ONE finding you already hold a uuid for, use updateFinding instead"), so an agent can route between them without opening either schema.

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?

Gives explicit when-to-use worked examples ("dismiss all the unrotated-secret findings"), when-not (never on own initiative, never as a self-decided tidy-up, never to preview), and names the alternative tools for each case: listFindings and queryApi answer 'what would match'. It even prescribes a four-step confirm-with-user workflow before calling.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources