Costory: Your Finops MCP
Server Details
Ask your AI assistant a cost question. Get allocation, correlation, and explanation in one response. Costory connects Claude, Codex, or Cursor to normalized cost data across AWS, GCP, Azure, Datadog, OpenAI, and Anthropic. https://costory.io
Free trial 14 days, 250 USD / month up to 10M Spend
- Status
- Healthy
- Uptime
- 98.6% over 48 days
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
TDQS
Scored across 50 tools
Many tools have clearly distinct purposes, but the set includes several overlapping or easily confused boundaries: generic `get`, `query`, and `search` coexist with more specific getters, `archive_object` overlaps with deprecated `archive_report`, and report-execution tools (`get_report_execution`, `retry_report_execution`, `run_report_now`, `transfer_report_execution`) require careful distinction. The very detailed descriptions help, but the sheer number of related CRUD/preview/publish operations creates ambiguity for an agent.
Most tool names follow a predictable snake_case verb_noun pattern (`create_alert`, `update_dashboard`, `list_reports`, `archive_object`). There are minor deviations: bare `get`, `query`, and `search`; noun-only `virtual_dimension_overlap_matrix`; and mixed constructions like `run_report_now` versus `retry_report_execution` versus `transfer_report_execution`. These are readable and mostly consistent, but not perfectly uniform.
With 50 tools, the surface is significantly over-scoped for a single MCP server. Although the FinOps domain is broad, many tools could be consolidated (e.g. widget data/image getters, report execution actions, permissions get/set), and several are deprecated or highly niche. This is too many tools for an agent to select from reliably.
The surface covers a lot of the FinOps lifecycle: querying, dashboards, reports, alerts, events, virtual dimensions, tags, teams, users, permissions, and documentation. However, notable gaps remain: there is no budget creation or update tool despite budget querying and permissions being supported, and tag lifecycle is only partially covered (list/delete, but no explicit create/update). These gaps would force agents to work around missing operations.
Available Tools
50 toolsarchive_objectADestructiveInspect
Archive one report, cost alert, event, virtual dimension, or dashboard. Pass resourceKind and resourceId.
report sets archivedAt.
cost alert sets archivedAt.
event sets archivedAt.
virtual dimension sets deletedAt.
dashboard deletes the row.
A virtual-dimension draft stays on discard_virtual_dimension_draft. A tag stays on delete_tag.
No bulk. Do not use this to hard-delete an alert.
A budget alert is not its own kind: pass resourceKind costAlert and alertType budget or cost. alertType is the type field from list_alerts, and it is only valid when resourceKind is costAlert.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| alertType | No | ||
| resourceId | Yes | ||
| resourceKind | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true, but the description goes well beyond that by disclosing the exact per-kind effect: reports/cost alerts/events set archivedAt, virtual dimensions set deletedAt, and dashboards delete the row outright. That distinction between soft-archive and hard-delete is precisely the behavioral detail an agent needs before calling a destructive tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action and the required parameters, and every line carries information. The three near-identical 'X sets `archivedAt`' sentences are slightly repetitive and could be collapsed, but no sentence is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive multi-kind mutation with no output schema and thin schema coverage, the description covers kind selection, per-kind effects, the budget-alert special case, and alternative tools. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 25%, but the description compensates substantially: it explains the resourceKind/alertType coupling, states alertType is the `type` field from list_alerts, and constrains it to costAlert with cost|budget values. This adds meaning the bare enum schema does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (archive) and enumerates the exact resource kinds it accepts, then draws boundaries against siblings (discard_virtual_dimension_draft, delete_tag). An agent can distinguish this from archive_report and the delete/discard tools without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit exclusions with named alternatives are given: virtual-dimension drafts go to discard_virtual_dimension_draft, tags go to delete_tag, no bulk operation, and it must not be used for hard-deleting an alert. It also tells the agent how to route a budget alert through the costAlert kind.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
archive_reportADestructiveInspect
Use archive_object. Deprecated on 1 October 2026. Archive a report (soft delete; no MCP restore).
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| reportId | Yes | Report id to archive. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, readOnlyHint=false, and openWorldHint=false. The description adds genuinely non-redundant context: the deletion is a soft delete and there is no restore path via MCP, which materially changes how an agent should weigh the call.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short, waste-free clauses covering redirect, deprecation date, and semantics. Slightly awkward ordering, since the redirect precedes the statement of what the tool does, but nothing is padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter mutation with full schema coverage, safety annotations, and no output schema, the description covers the essentials plus the undo limitation. Only missing nicety is explicit permission requirements for archiving a report.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and both parameters (reportId, slug) are documented in-schema, including the auto-detect caveat for slug. The description adds no parameter-level detail, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Archive a report') and names the replacement sibling archive_object, so the agent can distinguish it from the other 50+ tools. It is not a tautology, but the purpose sentence is pushed to third position behind the redirect and deprecation notice.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to archive_object and gives a concrete temporal condition ('Deprecated on 1 October 2026'), which effectively says when not to use this tool. No other exclusions or prerequisites are stated, but the alternative is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_alertAInspect
Create a cost alert that monitors one or more queries and notifies when a condition fires. New alerts are creator-only (you get Admin). To share, call set_object_permissions with resourceKind "costAlert". Accepts the same query config as query (prefer datePreset over hand-computed from/to). scopeId is the same id as list_teams. It is stored on the alert and is not merged into the query filters. Omit it for no scope. The firing rule is a single condition boolean expression over the query names, e.g. a > 1000, rollingSum(a, 7, DAY) > 50000, or (a - timeShift(a, 1, DAY)) / timeShift(a, 1, DAY) > 0.2. Window math (rollingSum/weekToDateSum/monthToDateSum/timeShift) is evaluated daily in BigQuery, so you do NOT pick an evaluation period — instead set dedup to control re-notification frequency (CALENDAR once per WEEK/MONTH, or ROLLING once every N days). The period (datePreset or from/to) defines the preview/look-back window for the underlying queries. Use list_available_destinations for SLACK/TEAMS channel IDs. Optional tagIds: existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created. Omit and [] both leave the alert untagged. Returns a URL that you MUST include in your response so the user can view/edit the alert.
EXAMPLE: "Alert me on Slack if our production AWS spend exceeds $50k over any 7 days, at most once a week" → { name: "Prod AWS weekly alert", queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_provider in ["AWS"] && cos_environment in ["prod"]" }], datePreset: "TRAILING_90_DAYS", condition: "rollingSum(a, 7, DAY) > 50000", dedup: { kind: "CALENDAR", calendarUnit: "WEEK" }, notificationChannel: "SLACK", slackChannelId: "C01ABC" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Explicit window end (inclusive, YYYY-MM-DD). Use with from instead of datePreset. | |
| from | No | Explicit window start (YYYY-MM-DD). Use with to instead of datePreset. | |
| name | Yes | Display name for the alert. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| aggBy | No | Time grain for the series: Hour, Day, Week, Month, or Period (default Month). | Month |
| dedup | Yes | Deduplication config controlling how often a still-firing group notifies. The window is per groupBy value; delivery stays one message listing newly eligible groups. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days. | |
| limit | No | Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. | |
| emails | No | Email addresses (required if EMAIL) | |
| tagIds | No | `list_tags` id or `{ name, color? }` (created if missing, default #6366F1). Omit and [] leave it untagged. | |
| compare | No | Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders. | |
| queries | No | Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. | |
| scopeId | No | Team scope id (list_teams). Stored on the alert, not merged into query filters. Omit for no scope. | |
| condition | Yes | Alerts v3 firing rule: a single boolean expression over the query names (`name` field of each query). Supports arithmetic (+ - * /), comparisons (> >= < <= == !=), logical and/or/not, parentheses, and these window functions: rollingSum(a, N, UNIT) (trailing sum over the last N units, UNIT ∈ DAY|WEEK|MONTH, inclusive of today), weekToDateSum(a) (Monday-to-date), monthToDateSum(a) (1st-of-month-to-date), and timeShift(a, N, UNIT) (value shifted back N units; may wrap a window function). Examples: `a > 1000`, `rollingSum(a, 7, DAY) > 1000`, `(a - timeShift(a, 1, DAY)) / timeShift(a, 1, DAY) > 0.2`, `a > 10000 or rollingSum(a, 7, DAY) > 50000`. | |
| datePreset | No | Official date preset (same DatePreset as dashboards/reports, e.g. MTD, LAST_MONTH, TRAILING_30_DAYS). Prefer this over hand-computed from/to when a preset matches. Mutually exclusive with from/to. | |
| slackChannelId | No | Slack target id (required if SLACK): a channel id (C…) to post to a channel, or a Slack user id (U…) to deliver a direct message to that user. Use list_available_destinations to discover both channels and the signed-in user's DM. | |
| teamsChannelId | No | Teams channel ID (required if TEAMS) | |
| notificationChannel | Yes | Notification channel |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly=false, destructive=false, and openWorld=false, but the description adds substantial unsurfaced behavior: creator-only ownership with Admin rights, the need to call set_object_permissions to share, scopeId storage semantics, daily BigQuery evaluation of window math, dedup re-notification behavior, and the mandatory return URL. This is rich behavioral context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loads the core purpose and then addresses permissions, query config, scope, condition, dedup, tags, and return value in a logical order. Most sentences earn their place, though some details duplicate the rich schema descriptions and could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (17 nested parameters, 4 required), the lack of an output schema, and the available annotations, the description covers the critical behaviors an agent needs: creator-only access, sharing path, query configuration, firing-rule syntax, deduplication semantics, tag handling, and the required return URL usage. Nothing essential appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description nevertheless adds value by giving concrete condition examples, explaining the relationship to the query tool's config, clarifying that scopeId is not merged into filters, and illustrating tagIds creation behavior. It does not describe every parameter beyond the schema, so it does not reach 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Starts with a specific verb and resource: 'Create a cost alert that monitors one or more queries and notifies when a condition fires.' This clearly distinguishes it from sibling tools like preview_alert and update_alert.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to set_object_permissions for sharing, list_available_destinations for channel IDs, and list_tags for tag IDs. It also states a preference for datePreset over hand-computed from/to. However, it does not explicitly say when to use create_alert versus preview_alert or update_alert, so it falls short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_dashboardAInspect
Create a dashboard with one or more widgets. New dashboards are creator-only (you get Admin). To share, call set_object_permissions after create — do not send PRIVATE/PUBLIC or teamId. Call get_skill with skillId: "dashboards" first — see skill for context-first workflow and inheritance rules. Put shared settings in dashboardContext (period required when chart widgets are present: prefer datePreset when possible, otherwise startDate/endDate; text-only dashboards may omit period; plus metricId, common groupBy, currency, optional conditionsCel). Chart widgets inherit by default and should only specify overrides: do not repeat from/to, datePreset, groupBy, metricId, currency, or conditionsCel when they match dashboardContext. The legacy context alias is temporarily accepted but deprecated; never send both. Text widgets use { type: "text", title, textContent } — no queries or period. Comparison widgets add compare: omit its from/to to compare against the preceding period automatically (preset-aware), and set compare.chartType to WATERFALL (default), TABLE, or KPI_BREAKDOWN. Widgets auto-pack into aligned grid rows; set per-widget w/h to override default sizing, or x/y together to pin a widget at an exact grid position. Optional tags: existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created. Omit and [] both leave the dashboard untagged. Returns a URL — you MUST include it in your response.
EXAMPLE: "AWS overview dashboard" → see skill dashboards Workflow A.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Dashboard title. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tags | No | Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). Omit and [] both leave the dashboard untagged. | |
| context | No | Deprecated alias for `dashboardContext`; supported temporarily for backward compatibility. Do not send both fields. | |
| widgets | No | List of widgets — chart widgets (queries, aggBy) or text widgets (`type: "text"`, `textContent`). Chart `queries` match the `query` tool `queries` shape — prefer sparse cost series and inherit from `dashboardContext`. Call get_skill skillId: "dashboards" first. Widgets are auto-packed into aligned grid rows; set optional `w`/`h` to override default sizing, or `x`/`y` together to pin a widget at an exact position. Example: [{"type":"text","title":"Notes","textContent":"Dashboard notes"}]. | |
| description | No | Optional longer description (default empty). | |
| dashboardContext | No | Dashboard-level shared context. Period (datePreset or startDate/endDate) is required when the dashboard includes chart widgets; text-only dashboards may omit it. Put metricId, common groupBy, currency, and shared filters here — chart widgets omit those fields to inherit. `conditionsCel` is inherited by default (`extendDashboardConditions` defaults to true). Call get_skill skillId: "dashboards" first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations (readOnlyHint=false, destructiveHint=false, openWorldHint=false): it discloses permission behavior ('New dashboards are creator-only (you get Admin)'), a required output behavior ('Returns a URL — you MUST include it in your response'), side effects ('missing tags are created'), and a deprecation policy for the legacy `context` alias. It does not cover failure modes or limits, but the annotation-covered safety profile plus these disclosures is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and the get_skill prerequisite are front-loaded, and the dense middle is largely justified by the tool's nested 7-parameter surface. There is some redundancy with the schema (tag color default, grid w/h/x/y rules, the repeated get_skill call), which keeps it from being maximally tight, but almost every sentence carries usable instruction.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a deeply nested schema, seven parameters, and no output schema, the description covers period requirements, inheritance, sharing/permissions, tag creation, widget positioning, comparison behavior, and the returned URL. An agent has everything needed to construct a valid call without consulting other tools beyond the explicitly named get_skill.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine cross-parameter semantics the schema does not: inheritance rules (chart widgets inherit and should not repeat from/to, datePreset, groupBy, metricId, currency, conditionsCel), the ban on sending both `context` and `dashboardContext`, and the compare-block auto-derivation. These interactions are meaningfully beyond field-level docs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Create a dashboard with one or more widgets') and immediately scopes it against siblings: sharing is routed to set_object_permissions, and pre-reading is routed to get_skill skillId "dashboards". An agent can distinguish this from update_dashboard/create_report without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states the prerequisite ('Call get_skill with skillId: "dashboards" first'), the follow-up action for sharing, when to prefer datePreset over startDate/endDate, when period may be omitted (text-only dashboards), and when to put settings in dashboardContext vs. repeating them per widget. When/when-not/alternative guidance is all present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_eventAInspect
Create a new event to annotate a cost change. Use this when the user wants to document why costs changed — e.g. a deployment, migration, pricing update, or business decision. Events are displayed on cost charts as annotations so the team can correlate cost movements with real-world changes. STRONGLY RECOMMENDED: provide a "widget" definition describing the chart this event explains — this creates a visual annotation tied directly to the relevant cost movement. Omit widget only for purely informational org-wide events. Optionally attach tags (string labels such as "migration" or "scaling") to the event — call list_tags to discover existing tag values in the org before inventing new ones. Optionally attach metadata key-value pairs (e.g. link to a PR, owner, external reference) — source is a column set by the server and should not be sent inside metadata. widget.scopeId is stored on the event (same id as list_teams). Omit it for no scope. null is the same as omit on create.
EXAMPLE: "Log the Kubernetes migration we did last Tuesday" → { name: "Kubernetes cluster migration", date: "2026-03-18", description: "Migrated from k8s v1.27 to v1.30, caused temporary node scaling cost spike", tags: ["migration"], metadata: { link: "https://github.com/acme/infra/pull/42", owner: "platform-team" }, widget: { title: "K8s node cost", queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_service_name in ["AmazonEC2"]" }], from: "2026-03-10", to: "2026-03-25", aggBy: "Day" } }
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | Event date (YYYY-MM-DD) | |
| name | Yes | Event name (min 5 characters) | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tags | No | Tags to attach to the event (e.g. 'migration', 'scaling'). Use list_tags to discover existing tag values. | |
| labels | No | Deprecated alias for tags. Ignored when tags is sent. | |
| widget | No | Annotation chart for the event. Same shape as the `query` tool (`queries`, `datePreset` or `from`/`to`, `aggBy`, `compare`, `limit`, `scopeId`) plus `title` and optional `description`. Providing a widget creates a visual annotation so the team can see which cost movement the event documents. On create: STRONGLY RECOMMENDED; omit only for truly org-wide events with no cost chart. On update: omit to leave the chart unchanged. If sent, widget is a full replacement (title, queries, and datePreset or from/to required; scopeId alone is invalid). Pass widgetEventId when the event has multiple charts. | |
| category | No | Deprecated. If sent, merged into tags then discarded. | |
| metadata | No | Optional key-value metadata (e.g. link, owner, event). source is always set to api. | |
| description | Yes | What happened and why it affects costs |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, openWorldHint=false, and destructiveHint=false, but the description adds substantial behavioral context: events appear as chart annotations, widget definitions create visual annotations, widget is a full replacement on update, source is server-set, deprecated aliases are ignored, and list_tags should be used before creating tags. No annotation contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured and front-loads the purpose and primary usage guidance before parameter details and the example. The example is useful for a nested widget object, though some widget and metadata guidance is repeated from the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex create tool with nine parameters, nested widget objects, and no output schema, the description is thorough about creation semantics, annotation behavior, tag discovery, metadata handling, and example usage. It does not describe the return value, error behavior, or permission requirements, but those gaps are relatively minor given the schema coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description goes beyond the schema by emphasizing that widget is STRONGLY RECOMMENDED, explaining when to omit it, noting that null is equivalent to omit for scopeId on create, and providing a complete example invocation. Some of this overlaps with schema descriptions, but it meaningfully improves parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Create a new event to annotate a cost change.' It explains the event's role as a chart annotation, which distinguishes this create/annotation tool from list_events and update_event 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear when-to-use context ('when the user wants to document why costs changed') and instructions for the widget parameter, including when to omit it. It also points to list_tags before inventing new tag values. It does not explicitly say when to use update_event instead for existing events, so it falls short of full alternative routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_reportAInspect
Create a report with shared reportContext and one or more widgets plus destinations. New reports are creator-only (you get Admin). To share, call set_object_permissions after create — do not send PRIVATE/PUBLIC or teamId. Call get_skill with skillId: "reports" first — see skill for context-first workflow and DIGEST preview iteration. Put shared settings in reportContext (period required when query widgets are present: prefer datePreset; plus metricId, common groupBy, currency, optional conditionsCel / scopeId). Query widgets inherit by default and should only specify overrides — do not repeat from/to, datePreset, groupBy, metricId, or currency when they match reportContext. The legacy context alias is temporarily accepted but deprecated; never send both. Schedule modes:
• NOW — create an on-demand report and immediately deliver it to every destination. DANGER ZONE: this sends real messages now. Summarize scope, schedule, and every destination, then ask the user to confirm before calling.
• UNSCHEDULED — save an on-demand draft without delivery.
• SCHEDULED — create a recurring report (DAILY / WEEKDAYS / WEEKLY / MONTHLY). DANGER ZONE: this authorizes future channel delivery. Summarize the recurring schedule and every destination, then ask the user to confirm before calling.
Optional tagIds: existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created. Omit and [] both leave the report untagged. Widget types: DASHBOARD_PDF, GRAPH_SNAPSHOT, TOP_FLOP, TEXT, and DIGEST. A DASHBOARD_PDF widget renders an existing dashboard as a PDF — pass { type: "DASHBOARD_PDF", dashboardId: "<id from search>" } (dashboardId required). TEXT widgets use { type: "TEXT", contentMarkdown } (not dashboard textContent). DIGEST hierarchy: reportContext.groupBy (root, preferred) + ordered additionalGroupBy (deeper levels); also supports thresholds. DIGEST AI (opt-in, slower): display ("tree" default | "table" | "summary" for LLM executive narrative) and enableAiInvestigation (boolean, default false — per-node deep analysis, independent of display). DIGEST aggBy is Month or Week — never Period (Period is TOP_FLOP). Preview and create use the same reportContext + widget shape. For WEEKLY schedules, schedule.weekday is required (0 = Sunday … 6 = Saturday). Destinations: SLACK/TEAMS take channelId; EMAIL takes email (a single address, the literal ALL_ACTIVE_USERS to reach every active user, or destinations.email.self from list_available_destinations for "email me"). Returns report URL and nextRunAt; NOW also returns execution URLs and per-destination status.
EXAMPLE monthly DIGEST: { schedule: { mode: "UNSCHEDULED" }, reportContext: { datePreset: "LAST_MONTH", groupBy: "cos_environment", metricId: "cost", currency: "USD" }, widgets: [{ type: "DIGEST", queries: [{ type: "cost", name: "a" }], aggBy: "Month", additionalGroupBy: ["cos_sub_account_id", "cos_service_name"], minAbsoluteDiff: 100, minRelativeDiff: 5, topLargestAbsoluteChange: 20, display: "summary", enableAiInvestigation: false }], destinations: [{ destinationType: "SLACK", channelId: "C…" }] }
EXAMPLE GRAPH_SNAPSHOT + TOP_FLOP: { schedule: { mode: "SCHEDULED", period: "WEEKLY", weekday: 1, firstRunAt: "2026-08-04T10:00:00.000Z" }, reportContext: { metricId: "cost", currency: "USD", groupBy: "cos_service_name", datePreset: "LAST_WEEK" }, widgets: [{ type: "GRAPH_SNAPSHOT", title: "Cost by service — trailing weeks", queries: [{ type: "cost", name: "a", chartType: "LINE" }], datePreset: "TRAILING_14_WEEKS", aggBy: "Week" }, { type: "TOP_FLOP", title: "Last week movers by service", queries: [{ type: "cost", name: "a" }], aggBy: "Period", topN: 5, flopN: 5 }], destinations: [{ destinationType: "SLACK", channelId: "C…" }] }
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional; empty or omitted → server generates a name from content. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tagIds | No | Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). Omit and [] both leave the report untagged. | |
| context | No | Deprecated alias for `reportContext`; supported temporarily for backward compatibility. Do not send both fields. | |
| widgets | Yes | One or more report widgets (DIGEST / GRAPH_SNAPSHOT / TOP_FLOP / TEXT / DASHBOARD_PDF). For query-backed widgets prefer sparse cost `queries` and inherit period / groupBy / metricId / currency from `reportContext`. Call get_skill skillId: "reports" for examples. | |
| schedule | Yes | Delivery mode: NOW (sends immediately), UNSCHEDULED (draft), or SCHEDULED (recurring; WEEKLY requires `weekday`). | |
| description | No | Optional longer description (default empty). | |
| destinations | Yes | Delivery targets: `{ destinationType, channelId }` or `{ destinationType: "EMAIL", email }`. Resolve channels via `list_available_destinations`. | |
| reportContext | No | Report-level shared context. Put period (prefer datePreset), metricId, common groupBy, currency, optional conditionsCel / scopeId here — query widgets omit those fields to inherit. Call get_skill skillId: "reports" first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the annotations (readOnlyHint=false, openWorldHint=true, destructiveHint=false) by naming the destructive-adjacent behavior of NOW (real messages sent immediately) and SCHEDULED (authorizes future delivery), requiring user confirmation, and disclosing creator-only Admin ownership plus return values (report URL, nextRunAt, per-destination status).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core purpose and organized into labeled schedule-mode and widget-type sections, plus two worked examples that earn their length. It is still very long and repeats the get_skill 'reports' instruction several times, so it is dense rather than tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema and a deeply nested input (9 params, oneOf widget/schedule/destination variants), the description covers the workflow, confirmation requirements, inheritance model, destination resolution, and return values, leaving no major gap for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (baseline 3), but the description adds non-obvious cross-field semantics: query widgets should carry only overrides and not repeat reportContext fields, the deprecated `context` alias must never be sent alongside reportContext, DIGEST aggBy is Month/Week (never Period), and WEEKLY requires schedule.weekday.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Create a report') and immediately lists the structural components (reportContext, widgets, destinations). It also distinguishes this from neighbors like update_report, run_report_now, and preview_report_widget by describing its creation/authoring role and the sharing handoff to set_object_permissions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: call get_skill skillId 'reports' first, use set_object_permissions to share, and never send PRIVATE/PUBLIC or teamId. It enumerates when each schedule mode applies (NOW vs UNSCHEDULED vs SCHEDULED), mandates confirmation before the DANGER ZONE modes, and points to list_tags / list_available_destinations for resolving inputs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_virtual_dimension_draftAInspect
Create a new virtual dimension draft without publishing. virtualDimensionId in inputs equals id from list/get/search. Call get_skill with skillId: "virtual-dimensions" first — see skill for allocation shapes, CEL rules, and full workflow. For a new telemetry allocation, discover a live integration metric via list_metrics with includeExternal: true and a specific search term, then persist inline externalMetric { provider, integrationId, metricName, aggregator, groupByFields }. For BigQuery/S3, also set dateColumn and metricColumn from the returned schema (first DATE / first NUMERIC) and gapFillingMethod (default FORWARD_FILL). Catalog-complete providers (Tsuga, Datadog, CloudWatch, Amplitude, Google Cloud Monitoring) do not need those fields. Do not set datasource on new reallocations. bqName (BigQuery/CEL query field, e.g. Environment → virtual_environment) is set once from name at create and is immutable. Ordered rules use conditionCel + allocation — do not send rule id (generated on save) or values (derived). Do not include catch-all/leftover in rules — leftoverRule is auto-added. Rejects invalid payloads — nothing persisted unless validation passes. On success: virtualDimensionId, bqName, name, description, tags, values, rules with generated ids, leftoverRule, draftValidation: { ok: true }, draftPersisted: true. Use preview_virtual_dimension_draft next. Use returned bqName for groupBy/filterCel in query after publish. Does not publish or refresh BigQuery. Clerk MCP only.
EXAMPLE: "Draft an Environment VDIM with a prod rule" → { name: "Environment", tagNames: ["finops"], rules: [{ name: "Production", conditionCel: "cos_environment in ["prod"]", allocation: { allocationType: "dimensionValue", dimensionValue: "prod" } }] }
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name for the new virtual dimension | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| rules | No | Ordered rules with conditionCel and allocation | |
| tagNames | No | Tag names to attach (created if missing) | |
| description | No | Optional longer description (default empty). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only indicate readOnlyHint false / destructiveHint false, so the description carries the burden and delivers extensively: 'Rejects invalid payloads — nothing persisted unless validation passes', success includes 'draftPersisted: true' and 'draftValidation: { ok: true }', rule ids are 'generated on save', leftoverRule is 'auto-added', and 'bqName … is immutable'. Side effects and validation behavior are fully disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The prose is front-loaded with the core purpose, then organized into lifecycle guidance, allocation details, constraints, response shape, and an example before the schema. However, it is extremely dense and contains redundancy — the BigQuery/S3 dateColumn/metricColumn/gapFillingMethod requirement is stated twice ('For BigQuery/S3, also set…' and 'For BigQuery/S3, dateColumn, metricColumn, and gapFillingMethod are required…').
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no separate output schema, the description enumerates the full success response ('virtualDimensionId, bqName, name, description, tags, values, rules with generated ids, leftoverRule, draftValidation: { ok: true }, draftPersisted: true'), covers failure behavior, advanced allocation semantics, required prerequisite skill, following tool, and a worked example — complete for a tool with this much nested logic and many relevant siblings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While the schema already covers all 5 parameters, the description adds substantial meaning beyond it: 'virtualDimensionId in inputs equals id from list/get/search', 'bqName … is set once from name at create', rules must not send 'id (generated on save) or values (derived)', the four allocation types are spelled out, and BigQuery/S3 requirements (dateColumn/metricColumn from list_metrics schema, default gapFillingMethod FORWARD_FILL) go far beyond the bare property definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opening phrase 'Create a new virtual dimension draft without publishing' names a specific verb, resource, and lifecycle state, distinguishing it from the sibling publish_virtual_dimension_draft. The embedded EXAMPLE ('Draft an Environment VDIM with a prod rule') makes the intent unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs prerequisites ('Call get_skill with skillId: "virtual-dimensions" first'), next steps ('Use preview_virtual_dimension_draft next'), and exclusions ('Do not set datasource on new reallocations', 'Do not include catch-all/leftover in rules', 'Does not publish or refresh BigQuery'). It also explains the relationship to query after publish ('Use returned bqName for groupBy/filterCel in query after publish').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_tagADestructiveInspect
Delete an unused tag from the organization. Only tags with zero tagged resources can be deleted — call list_tags first to check usage counts. Returns an error if the tag is still in use. EXAMPLE: "Delete the obsolete 'v1' tag" (after list_tags confirmed 0 uses) → { tagId: "tag_old" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tagId | Yes | ID of the tag to delete |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the operation as destructive (destructiveHint=true). The description adds valuable behavioral context beyond the annotations by specifying the zero-usage requirement, the error condition, and the need to check list_tags first. This clarifies the operational constraints without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, with the main purpose in the first sentence and additional context in a second sentence. The example is helpful but slightly redundant, though it does not inflate the text excessively. Overall it is well-structured and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive delete operation with only two parameters and no output schema, the description covers the essential conditions, error behavior, and prerequisite. It does not mention authentication or success response details, but these are less critical given the annotations and the tool's simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema covers both parameters (slug and tagId) with clear descriptions, achieving 100% schema description coverage. The description includes an example that illustrates tagId usage but does not add semantic meaning beyond what the schema provides, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Delete') and resource ('tag') with organizational scope, clearly distinguishing this tool from sibling tools like list_tags or update_dashboard. It explicitly states the purpose and the key constraint of deleting only unused tags.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage guidance: call list_tags first to check usage counts, and notes that an error occurs if the tag is still in use. This tells the agent when to use the tool and what prerequisite steps are needed, effectively differentiating it from non-delete alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
discard_virtual_dimension_draftADestructiveInspect
Discard the pending draft for an existing virtual dimension without publishing. virtualDimensionId in inputs equals id from list/get/search. Use when a draft is corrupt/unreadable or you want to abandon in-progress edits. On success returns draftDiscarded: true. Returns an error when no pending draft exists. Does not delete published virtual dimensions. Clerk MCP only.
EXAMPLE: "Discard corrupt Environment VDIM draft" → { virtualDimensionId: "<virtualDimensionId from list/get>" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| virtualDimensionId | Yes | Virtual dimension id (stable public identifier) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, but the description adds valuable context: it does not delete published virtual dimensions, returns 'draftDiscarded: true' on success, and errors when no draft exists. This clarifies the exact scope of destruction and the outcome, exceeding what annotations alone provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with the main purpose. It includes necessary details (return value, error case, non-destructive scope, example) without fluff. It is slightly longer than the bare minimum but every sentence adds value, making it appropriately sized for a tool with multiple behavioral caveats.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity, the description covers the key aspects: what it does, when to use it, parameter mapping, success/error behavior, and scope. There is no output schema, but the return value is mentioned. The description is sufficient for an agent to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides descriptions for both parameters at 100% coverage, but the description adds crucial mapping: 'virtualDimensionId in inputs equals id from list/get/search.' The example also demonstrates how to fill the parameter, which is helpful beyond the schema's minLength constraint.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action: 'Discard the pending draft for an existing virtual dimension without publishing.' The verb 'discard' and specific resource 'pending draft' are unambiguous, and 'without publishing' distinguishes it from the publish tool. This completely separates it from sibling tools like create/update/publish.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Use when a draft is corrupt/unreadable or you want to abandon in-progress edits.' It also notes the error condition when no draft exists. However, it doesn't explicitly compare with update_virtual_dimension_draft or other alternatives, though the 'without publishing' phrase implies a contrast.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_cost_change_factorsARead-onlyInspect
Find which dimension values drove a cost change between two periods. This is a before/after analysis — compare is required ({} auto-derives the previous window, same as query). Prefer suggest_groupby / search / get_context first, then pass 2–4 columns (max 8). Do not invent columns. filterCel omitted or "" is unfiltered (not AWS-only). nestingEdges: a child's spend sits inside the parent — do not sum a contributor with its ancestors or descendants; independent contributors may be summed. Prefer omitting aggregationMethod (SUM).
EXAMPLES:
• "Why did last month's EC2 cost change?" → { datePreset: "LAST_MONTH", compare: {}, filterCel: "cos_service_name in ["AmazonEC2"]", columns: ["cos_region", "cos_usage_type"] }
• "What drove the RDS jump in May?" → { from: "2026-05-01", to: "2026-05-31", compare: { from: "2026-04-01", to: "2026-04-30" }, filterCel: "cos_provider in ["AWS"] && cos_service_name in ["AmazonRDS"]", columns: ["cos_sub_account_id", { column: "cos_charge_description", contains: "IOPS" }] }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Current period end (YYYY-MM-DD), inclusive. Omit when using datePreset. | |
| from | No | Current period start (YYYY-MM-DD). Omit when using datePreset. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| columns | Yes | 2–4 dimensions to investigate (max 8). Prefer suggest_groupby / search / get_context names (e.g. "cos_service_name"), or { column, contains } for a single-token substring (e.g. { column: "cos_charge_description", contains: "GPU" }). Do not invent columns. | |
| compare | Yes | Previous period. `{}` auto-derives from the current window (same helper as query). `{ from, to }` pins it. | |
| filterCel | No | Optional CEL scope. Omit or "" for unfiltered (Billy where_clause TRUE). That is not an AWS-only filter even though columns_where_clause falls back to ["cos_provider"]. | |
| datePreset | No | ||
| aggregationMethod | No | Prefer omitting this (SUM). AVG is rarely right for cost. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is known Stephen. The description adds substantial behavioral detail beyond annotations: compare is required, `{}` auto-derives the previous window, filterCel omitted or "" is unfiltered (not AWS-only), and nestingEdges warns against summing hierarchical contributors. This addresses a subtle and likely accidental misuse (double-counting) that the schema would not reveal. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence serves a distinct purpose: purpose, prerequisite, constraint, a behavioral warning, a default preference, and two examples. There is no redundancy or filler. The structure naturally front-loads the core purpose and then progressively adds nuance, making it easy for an agent to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 parameters Prix, no output schema, and a non-trivial analysis workflow, the description covers everything an agent needs to invoke it correctly: required fields, ordering of discovery calls, column selection rules, filter semantics, aggregation defaults, and sample invocations. It even includes a hierarchical aggregation caveat that would otherwise be an easy to make error. The only thing missing is an explicit return format, but given the complexity handled and the annotations covering safety, this is complete enough.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is ~88%, but the description goes beyond the schema to explain critical semantics: compare's `{}` shorthand, filterCel's unfiltered default, aggregationMethod's preference to omit, and the recommended 2–4 column range (with max 8). The examples demonstrate the exact structure for columns, including the { column, contains } object form. This is high-value practical context that the schema alone does not provide.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb and resource: 'Find which dimension values drove a cost change between two periods.' This clearly distinguishes the tool from siblings like query (raw data) or suggest_groupby (suggest dimensions), and it explicitly frames the tool as a before/after analysis. The name and description are aligned, and the purpose is immediately actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage direction: 'Prefer suggest_groupby / search / get_context first, then pass 2–4 columns (max 8).' It tells the agent the prerequisite discovery tools Series and the recommended column count. It also provides a clear exclusion ('Do not invent columns') and clarifies filterCel semantics. The two concrete examples further illustrate both datePreset and from/to usage, leaving little ambiguity about when to call this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
getARead-onlyInspect
Fetch a V2 dashboard, budget, cost alert, report, or virtual dimension by ID. Response includes a type discriminator (dashboard | budget | costAlert | report | virtualDimension) — branch on it.
Budgets: pass the parent budget id (from search) or a budget version id; response includes budgetVersionId for query, plus costMetricId, currency, virtualDimension (id, name, bqName, values), excludedValues, lines, includedVirtualDimensionValues, filterCelRestrictToIncludedLines, and filterCelExcludeExcludedVirtualDimensionValues for aligning query cost with budget. howToQueryAlignedCost gives concrete example payloads.
Cost alerts: pass the alert id (from search or list_alerts); response includes alert configuration plus firingHistory (every stored firing day and group values that fired). The payload includes scopeId, the saved team scope. The returned queries are the stored internal shape — read-only, and they do not contain that scope filter. To change the series with update_alert, write a fresh queries array with filterCel exactly as for create_alert; do not echo these back.
Reports: config in words plus run-health (status, nextRunDate, lastRunHealth, widgets array, recent per-destination delivery with executionIds). Read-only. To inspect a specific delivery's content, call get_report_execution (then get_report_execution_widget for drill-down).
Virtual dimensions: id / virtualDimensionId (same), hasPendingDraft, immutable bqName (BigQuery/CEL field for groupBy/filterCel — never derive from display name); published and optional draft (name, description, tags, computeStatus, values, rules, leftoverRule; draft may include draftValidation); dependencies. values is derived — output only. leftoverRule is separate from rules — do not put it in the rules array or pass it to update. For updates, copy rules from draft if pending else published, project each rule to { id, name, conditionCel, allocation } (omit position, isLeftovers, and any leftover/catch-all rule), and pass that full rules array to update_virtual_dimension_draft. Call get_skill with skillId: "virtual-dimensions" for allocation shapes and workflow.
Dashboards: call get_skill with skillId: "dashboards" before create/update. Chart widgets include x/y/w/h and resolved queryConfig; text widgets have type: "text" and textContent. After fetch, use get_dashboard_widget_data / get_dashboard_widget_image, or update_dashboard to mutate.
Use "search" (or list tools) first to discover IDs. EXAMPLE: "Open the Kubernetes dashboard" (after search returned its ID) → { id: "clx9abc123" }
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ID of a V2 dashboard, budget, cost alert, report, or virtual dimension (from search / list tools). For budgets, pass parent budget id or budget version id. Response includes a `type` discriminator: dashboard | budget | costAlert | report | virtualDimension. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the read-only safety profile, yet the description adds substantial behavioral context beyond them: budgets expose budgetVersionId/costMetricId/currency, cost alerts include firingHistory plus a critical warning that the returned `queries` are stored read-only and must not be echoed back to update_alert, and virtual dimensions flag `bqName` as immutable and warn never to derive it from the display `name`. This is exactly the kind of non-obvious caveat structured fields cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the discriminator are correctly front-loaded, and the per-type prose is organized into labeled sections. However, for a single getter it is very long and folds update/mutation workflows (writing fresh `queries`, projecting rules, update_virtual_dimension_draft) into what is a read operation, which dilutes focus.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the full burden of describing returns, and it does so thoroughly per type: the `type` discriminator, budget fields, alert firingHistory/scopeId, report config plus run-health and widgets, and virtual-dimension published/draft structure. An agent has enough to interpret and branch on the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both `id` and `slug`; the baseline is 3. The description adds the budget-specific nuance that `id` may be a parent budget id or a budget version id, but otherwise largely restates what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb ('Fetch') plus an enumeration of the exact resource types it handles (V2 dashboard, budget, cost alert, report, virtual dimension) by ID, and names the `type` discriminator. This lets an agent distinguish it from sibling getters like get_report_execution or get_dashboard_widget_data without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells the agent to call 'search' (or list tools) first to discover IDs, and names concrete alternatives and follow-ups: get_skill for dashboards/virtual dimensions, get_report_execution for delivery drill-down, and update_alert / update_virtual_dimension_draft / update_dashboard for mutation. This is explicit when-to-use and routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_contextARead-onlyInspect
Get workspace context: popular groupBy dimensions, recent dashboards, and connected external-metric integrations (e.g. Tsuga, BigQuery, Datadog, CloudWatch, Google Cloud Monitoring). Call this FIRST at the start of every conversation. Each dimension has name (CEL/groupBy field: cos_* native or virtual_* bqName) and label (pretty display name) — use label only in user-facing text; use name for filterCel/groupBy (never the label). To discover CEL field names and dimension values for filterCel/groupBy, use search instead — pass type: ["dimensions"] with an empty query to list all dimensions, or a keyword (e.g. "account", "environment") to narrow results. When externalMetricIntegrations is non-empty, call list_metrics with includeExternal: true and a specific search term to browse matching live integration metrics. For new virtual-dimension telemetry (split-by-usage-metric) reallocations, use that same includeExternal search and persist an inline externalMetric (do not set datasource). For BigQuery or S3, pick dateColumn / metricColumn from the returned schema (DATE / NUMERIC) and set gapFillingMethod (default FORWARD_FILL). Custom virtual dimensions: use immutable bqName from list/get VDIM tools as groupBy / filterCel (not display name). Poll computeStatus until COMPLETED after publish.
EXAMPLE: "What cloud cost data do you have available?" → {}
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish the tool as read-only and non-destructive. The description adds meaningful behavioral context by describing the shape of returned dimensions (name vs label), the role of externalMetricIntegrations, and downstream computeStatus polling expectations. It does not disclose every possible response detail, but the added value beyond annotations is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long and dense, but nearly every sentence carries a distinct operational instruction. It is front-loaded with the core purpose and call-first directive before moving into detailed domain rules. A bit of restructuring into clearer segments would improve scannability, but the length is justified by the tool's central role.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description gives enough information about return contents and naming conventions for an agent to correctly consume the response. It also covers related workflows — search for dimensions, list_metrics for integrations, bqName usage for custom virtual dimensions, and computeStatus polling — so the agent is fully oriented for follow-up actions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, slug, is already fully described in the input schema with clear guidance about omitting it for auto-detection. Schema description coverage is 100%, so the description does not need to add parameter semantics. The extensive domain guidance in the description applies more to downstream tool usage than to this tool's parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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: "Get workspace context" and enumerates exactly what it returns — popular groupBy dimensions, recent dashboards, and connected external-metric integrations. It clearly distinguishes this tool from siblings by positioning it as the initial discovery/context tool and explicitly redirecting to search for dimension discovery and list_metrics for integrations.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit invocation rule: "Call this FIRST at the start of every conversation." It also provides concrete when-to-use-alternatives guidance, such as using search with type ["dimensions"] to discover CEL field names and list_metrics with includeExternal for integration metrics. This is unusually actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_dashboard_widget_dataARead-onlyInspect
Run a saved widget by id and return its cost data — no need to re-specify the query config. Loads the widget's stored request, applies the dashboard's conditionsCel unless the widget has extendDashboardConditions=false, and returns the same series/timeSeries or comparison breakdown as the query tool. Use after get to get data for a specific widgetId. EXAMPLE: "Show me data for the EC2 widget" (after get returned widgetId "wid_123") → { dashboardId: "clx9abc", widgetId: "wid_123" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| widgetId | Yes | Widget id (from get or get_context widgets list). | |
| dashboardId | Yes | DashboardV2 id. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that the tool applies the dashboard's conditionsCel unless extendDashboardConditions=false, and that it returns the same series/timeSeries or comparison breakdown as the query tool. Annotations already cover read-only/non-destructive behavior, so this adds meaningful context without contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than strictly necessary but front-loaded with the main action and includes a helpful example. Every sentence contributes functional or usage information, so no waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by specifying the return format ('same series/timeSeries or comparison breakdown as the query tool') and the workflow. It doesn't detail error cases or edge conditions, but for a 3-param tool this is adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already provides 100% parameter coverage, but the description adds value by noting widgetId comes from get/get_context, explaining dashboardId's role in applying conditionsCel, and providing an example mapping natural language to arguments. This exceeds bare schema labels.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Run a saved widget') and resource ('by id and return its cost data'), clearly differentiating it from sibling tools like get_dashboard_widget_image (image) and query (ad-hoc). It also states the return format matches the query tool, providing strong semantic distinction.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs 'Use after get to get data for a specific widgetId' and gives a concrete example flow. It doesn't enumerate when NOT to use, but the workflow context and relationship to the query tool make usage clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_dashboard_widget_imageAInspect
Render a saved widget as a PNG chart, upload to GCS, and optionally return the binary image inline. Use when the user wants to see a visual snapshot or embed an image in a chat reply. Applies the same dashboard conditionsCel merge as get_dashboard_widget_data. Returns imageUrl (GCS PNG). By default also returns the PNG bytes as a base64 image block (set includeBinaryImage: false to skip binary if the payload is too large). No separate share URL — the widget already lives on the dashboard; link to the dashboard if the user wants the live chart. Does NOT work for text or table-only widgets (returns UNSUPPORTED_WIDGET). EXAMPLE: "Share my EC2 cost widget as an image" (after get returned widgetId "wid_123") → { dashboardId: "clx9abc", widgetId: "wid_123" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| widgetId | Yes | Widget id (from get). | |
| dashboardId | Yes | DashboardV2 id. | |
| includeBinaryImage | No | If true (default), the response includes a base64-encoded PNG image block in addition to the share/image URLs. Set to false to receive URLs only (smaller payload for clients that don't display inline images). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses the GCS upload side effect, the default inclusion of base64 PNG bytes, the option to skip binary for large payloads, the condition merge behavior, and the UNSUPPORTED_WIDGET failure case. This is rich behavioral context that annotations alone don't provide. The readOnlyHint=false annotation aligns with the stated upload side effect, showing no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized for the tool's complexity. Every sentence contributes meaningful information: purpose, when to use, condition merge, return format, binary option, no-share-URL note, unsupported types, and a working example. Despite its length, it remains structured and front-loaded, with the core action stated first.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by stating the return values (imageUrl, base64 image block). It also covers failure modes, parameter interplay, and usage context. The example provides a complete scenario from widgetId to call, making the tool's behavior fully understandable for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by explaining the purpose of includeBinaryImage (skip binary for large payloads), clarifying widgetId comes from a get call, and providing a concrete example with parameter values. However, the schema already thoroughly documents each parameter, so the description doesn't need to compensate heavily.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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: 'Render a saved widget as a PNG chart, upload to GCS, and optionally return the binary image inline.' It clearly distinguishes from sibling tools like get_dashboard_widget_data by focusing on the visual image output rather than data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: 'Use when the user wants to see a visual snapshot or embed an image in a chat reply.' It also provides an exclusion ('Does NOT work for text or table-only widgets') and suggests an alternative (link to the dashboard for the live chart), making the usage guidance very actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_documentation_pageARead-onlyInspect
Retrieve the full content of a specific Costory documentation page by its public docs URL or page path. Use this after search_documentation returns results. Response starts with Url: https://docs.costory.io/.... When citing this page in chat, use that exact Url: as the markdown href — do not convert to a relative app path.
EXAMPLE: "Show me the full page about cost explorer" → { page: "https://docs.costory.io/features/cost-explorer" }
| Name | Required | Description | Default |
|---|---|---|---|
| page | Yes | Documentation page URL or path from search results (e.g. 'https://docs.costory.io/get-started/welcome' or 'features/cost-explorer') |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite readOnlyHint=true in annotations, the description adds valuable behavioral context: the response format ('Response starts with `Url: ...`') and a specific citation instruction using that exact URL as markdown href. This goes beyond the annotation safety profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences plus a formatted example. Every sentence earns its place: first states the purpose, second gives usage sequence and response details, then a labeled example. No redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read tool with one parameter and no output schema, the description covers all necessary aspects: what it does, when to use it, what the response looks like, and how to handle the response correctly. The example completes the context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameter is fully documented. The description reinforces meaning with a concrete example ('Show me the full page about cost explorer' → { page: ... }), adding usage context beyond the schema but not new parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description specifies a clear verb ('Retrieve'), resource ('full content of a specific Costory documentation page'), and input method ('by its public docs URL or page path'). It distinguishes itself from sibling tool search_documentation by explicitly stating this is the follow-up retrieval step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit usage context: 'Use this after search_documentation returns results.' This clearly indicates when to use the tool in relation to its sibling. The example also demonstrates a natural-language-to-parameter mapping.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_object_permissionsARead-onlyInspect
List direct Reader/Editor/Admin grants on a dashboard, report, budget, or cost alert. One tool for all four kinds — pass resourceKind plus the object id from search / get / create. Returns grants (person, team, organization, or organization admins), the caller's effective role, and capabilities (canView / canEdit / canManage). canManage is true only for Admin. Budget alerts use resourceKind "budget" and the parent budget id. Templates have no ordinary grants. Read-only.
EXAMPLES:
• "Who can open the AWS dashboard?" → { resourceKind: "dashboard", resourceId: "clx9aws" }
• "What access do I have on this report?" → { resourceKind: "report", resourceId: "clx9rpt" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| resourceId | Yes | Object id from search, get, or create. | |
| resourceKind | Yes | dashboard, report, budget, or costAlert. Budget alerts use the parent budget. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces this with 'Read-only.' It adds meaningful behavioral context beyond annotations: the special case for budget alerts (use parent budget id), the fact that templates have no ordinary grants, and the semantic detail that canManage is true only for Admin. It does not describe pagination or error behavior, but for a read-only listing tool this is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: the core purpose appears in the first sentence, followed by return-value summary, special cases, and examples. Every sentence earns its place, and the examples are illustrative without being verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only tool with full schema coverage and safety annotations, the description covers the essential invocation details: resource kinds, id provenance, return contents, and edge cases (budget alerts, templates). It lacks an output schema and does not describe pagination or error conditions, but those are not critical for a permissions-listing tool with this level of guidance.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all three parameters. The description adds value by explaining the relationship between resourceKind and resourceId ('pass resourceKind plus the object id from search / get / create'), clarifying the budget-alert special case, and providing concrete example values. It slightly exceeds the baseline 3 by enriching the enum semantics with usage context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('List') and resource ('direct Reader/Editor/Admin grants on a dashboard, report, budget, or cost alert'), and explicitly distinguishes itself from the sibling set by covering all four resource kinds in one tool. It also clarifies what it returns (grants, caller's effective role, capabilities), making the tool's purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: 'One tool for all four kinds — pass resourceKind plus the object id from search / get / create.' It also provides exclusions ('Templates have no ordinary grants') and two concrete examples mapping natural-language questions to parameter values. This is strong routing guidance relative to siblings like set_object_permissions and get.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_report_executionARead-onlyInspect
Get a condensed execution summary: status, destination, per-widget summaries, URLs. Omits raw time-series and full DIGEST trees. Widgets with drillDown can be expanded via get_report_execution_widget. For DIGEST, tell the user tree and node investigations are available. EXAMPLE: { executionId: "clx…" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| executionId | Yes | Report execution id. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint/destructiveHint annotations, the description discloses the condensed behavior, the omission of raw time-series and full DIGEST trees, and the ability to expand widget drillDowns. This is exactly the kind of behavioral context that annotations alone do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded, using three sentences plus a short example. Each sentence adds necessary information: what the tool returns, what it omits, and which sibling to use for more detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even without an output schema, the description enumerates the key returned fields (status, destination, per-widget summaries, URLs) and explicitly lists what is not included. This is sufficient for an agent to contract on a read-only summary call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description includes a concrete example with executionId, but it does not add significant meaning beyond what the schema already provides for either parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a specific verb-resource pair ('Get a condensed execution summary') and lists concrete contents (status, destination, per-widget summaries, URLs). It also distinguishes itself from get_report_execution_widget by explicitly pointing to that sibling for expanded widget details.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly states what the tool omits (raw time-series, full DIGEST trees) and names get_report_execution_widget as the alternative for drillDown widgets. It also instructs the agent to mention DIGEST tree/node investigations are available, which is practical guidance for when to use this tool vs. other investigation paths.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_report_execution_widgetARead-onlyInspect
Fetch an advanced view for one widget from get_report_execution. Pass widgetId and a view from its drillDown list. DIGEST tree returns the full formatted tree. Read-only; never starts new investigations. EXAMPLE: { executionId: "clx…", widgetId: "clx…", view: "tree" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| view | Yes | `series`, `tree`, or `investigations`. | |
| widgetId | Yes | Widget id within that execution. | |
| executionId | Yes | Report execution id. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description reinforces this with 'Read-only; never starts new investigations', adding the specific safeguard against triggering investigations. It also explains behavior for the `tree` view ('returns the full formatted tree'), which is context beyond the schema. Slightly more could be said about error cases or response format, but the annotation coverage lowers the burden.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences plus an example, with zero filler. It front-loads the core purpose first, then provides invocation detail, and closes with a concrete example. Every sentence contributes information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has no output schema, the description covers the main usage flow and highlights a key behavior (full tree). The annotations cover safety. However, it does not describe what each enum value (`series`, `investigations`) returns or what happens if the provided view is not in the drillDown list. Still, the tool is relatively simple and the example helps fill gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds meaningful value by explaining that `widgetId` and `view` come from a drillDown list and by specifying that the `tree` view returns a full formatted tree. This goes beyond the schema's bare enum descriptions. The example also shows real-world usage. No param-specific info is missing that would require a higher score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Fetch') and clearly identifies the resource ('advanced view for one widget from get_report_execution'). It also distinguishes this from the sibling `get_report_execution` by focusing on drill-down views per widget. The example and mention of 'DIGEST tree' further clarify scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description instructs the agent to 'Pass widgetId and a view from its drillDown list', giving explicit invocation guidance and a concrete example. It also notes the read-only nature, which prevents misuse. However, it does not explicitly contrast with alternatives like `preview_report_widget` or state when this is preferable to `get_report_execution`.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_skillARead-onlyInspect
Load a workflow guide or any markdown file from the Costory skills GitHub repo. Returns markdown — call BEFORE starting the task, AFTER get_context. For build tasks, load a mechanics skill ("virtual-dimensions", "dashboards", "reports", "query"). When a curated recipe fits the user's outcome, call with skillId: "recipes", then load the matching card via its repo-relative .md path. Pass either: (1) a catalog skillId from list_skills (e.g. "virtual-dimensions", "dashboards", "reports", "query", "recipes"), or (2) a repo-relative .md path (e.g. a recipe card or skill reference under plugins/costory/skills/…). Paths must be relative, end in .md, and cannot traverse (..). Call this instead of guessing the tool sequence. EXAMPLES: • "Match outcome to a recipe card" → { skillId: "recipes" } • "Build an AWS overview dashboard" → { skillId: "dashboards" } • "Generate a report of monthly changes" → { skillId: "reports" } • "Create a custom cost axis" → { skillId: "virtual-dimensions" } • "Load the marketplace spend recipe" → { skillId: "plugins/costory/skills/recipes/marketplace-spend.md" } • "Load the dashboards interesting-overview reference" → { skillId: "plugins/costory/skills/dashboards/references/how-to-generate-interesting-dashboards.md" }
| Name | Required | Description | Default |
|---|---|---|---|
| skillId | Yes | Catalog skillId from list_skills (e.g. "dashboards", "reports", "recipes", "virtual-dimensions") OR a repo-relative .md path in the skills GitHub repo (e.g. "plugins/costory/skills/recipes/explain-period-change.md"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool as read-only and non-destructive. The description adds valuable behavioral context beyond the annotations: it returns markdown, must be called after get_context, and enforces path constraints ('relative, end in .md, and cannot traverse (..)'). It doesn't detail error behavior or auth requirements, but the safety profile is already covered by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured, starting with purpose and timing, then offering examples. The numbered input options and bulleted examples improve scannability. It is somewhat repetitive (the same skill IDs appear in the prose and examples), but the extra length is justified by the tool's need to disambiguate between two input modes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the core contextual needs: what the tool loads, the return format (markdown), sequencing relative to get_context, the two accepted input forms, and path constraints. For a read-only, single-parameter tool with no output schema, this is largely sufficient. It stops short of explaining failure modes or listing all possible skill IDs, but those are discoverable via list_skills.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds substantial meaning beyond the schema: it maps user intents to concrete skillId values, provides representative examples for both catalog IDs and repo-relative paths, and introduces path validation rules not present in the schema. This exceeds the baseline expected for fully covered schemas.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's action: 'Load a workflow guide or any markdown file from the Costory skills GitHub repo' and notes it 'Returns markdown.' It distinguishes itself from siblings by specifying its timing ('call BEFORE starting the task, AFTER get_context') and by saying 'Call this instead of guessing the tool sequence.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit guidance on when to use the tool: before starting a task, after get_context, and for build tasks it recommends specific mechanics skills. It also explains the recipe workflow and the two valid input forms. However, it doesn't explicitly state when not to use the tool versus alternatives like get_documentation_page, so it falls short of full exclusionary guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_alertsARead-onlyInspect
List all cost alerts and budget alerts for an organization with their current status. Cost alerts return their firing condition expression and dedup config (legacy alerts that predate conditions return their structured thresholds instead). Only call this when the user explicitly asks about their current cost monitors or alerts.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| type | No | Filter by alert type | all |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds valuable behavioral details beyond the readOnlyHint annotation: it explains that cost alerts return a firing condition expression and dedup config, while legacy alerts return thresholds. This clarifies response shape and handling of legacy data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences front-load the core purpose and then add necessary detail on return formats and usage constraint. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description adequately conveys what the tool returns and differentiates legacy vs. current alerts. It could mention pagination or statuses, but the core context is well covered for a read-only list operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with both parameters (slug and type) already described in the schema. The description does not add further parameter semantics beyond what the schema provides, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists all cost and budget alerts for an organization with their current status, using a specific verb and resource. This distinguishes it from sibling tools like create_alert and preview_alert.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Only call this when the user explicitly asks about their current cost monitors or alerts,' providing strong when-to-use guidance. It does not name alternatives explicitly, but the purpose is distinct from creating or previewing alerts.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_available_destinationsAInspect
List every destination the organization can deliver reports or alerts to. Returns an object keyed by provider — destinations.slack.channels[] (channelId, name), destinations.teams.channels[] (channelId, name, teamName), and destinations.email (free-form; no list to enumerate). Each provider also carries a "connected" boolean so you can tell the user when an integration isn't set up. Call this before create_report or create_alert to resolve channelId values. For email, destinations.email.self (when present) is the signed-in user's own address — use it for "email me" instead of asking; destinations.email also accepts the literal ALL_ACTIVE_USERS to reach every active user. For any other explicit recipient, ask the user for the address. EXAMPLE: "Send me a weekly digest to #infra-costs" → { } (then pick channels[].channelId where name === "infra-costs")
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, openWorldHint=true, destructiveHint=false. The description adds valuable behavioral context: it explains the return structure (object keyed by provider), the 'connected' boolean for telling users about unset integrations, and the special ALL_ACTIVE_USERS literal. It doesn't contradict annotations. It could mention that the tool may fail if the user belongs to multiple orgs and slug is omitted, but that's covered in the parameter description. The description goes beyond annotations by explaining the shape and semantics of the response.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with the core purpose. It includes a concrete example that illustrates the expected usage pattern. It's a bit long but every sentence earns its place: the return structure, the connected boolean, the email special cases, and the example all add necessary information. The example is particularly helpful for an agent to understand how to map user requests to channelId values.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a list tool with one optional parameter and no output schema, the description is quite complete. It explains the return shape, the special email cases, and the relationship to sibling tools. It doesn't describe pagination or rate limits, but for a destination-listing tool that's likely not a concern. The description covers what an agent needs to know to call it correctly and interpret the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents the slug parameter. The description adds context by explaining when slug matters (omit to auto-detect, fails if multiple orgs) and how it relates to the tool's behavior. It doesn't repeat the schema but adds a small amount of value. Baseline 3 is exceeded slightly because the description clarifies the failure mode and the auto-detection behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: listing every destination the organization can deliver reports or alerts to. It specifies the resource (destinations) and the verb (list), and distinguishes it from sibling tools like create_report and create_alert by explicitly saying to call it before those to resolve channelId values. The example further clarifies the intended use.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Call this before create_report or create_alert to resolve channelId values.' It also gives specific instructions for handling email recipients, including using destinations.email.self for 'email me' and asking the user for any other explicit recipient. This is clear, actionable guidance that an agent can follow without ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_eventsARead-onlyInspect
List events (deployments, business changes, provider updates) for an organization within a date range. Use this to correlate cost changes with real-world events. After query you may optionally call this to check if any events happened around that time. Returns events sorted by date descending, including metadata (e.g. link, owner, source) and widgetEvents (annotation chart ids/titles — use widgetEventId with update_event when an event has multiple charts). The payload includes scopeId. EXAMPLES: • "Did anything happen around that cost spike in mid-March?" → { from: "2026-03-10", to: "2026-03-20" } • "Show me all deployments in March" → { from: "2026-03-01", to: "2026-03-31", tags: ["deployment"] }
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | End date (YYYY-MM-DD) | |
| from | Yes | Start date (YYYY-MM-DD) | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tags | No | Filter by tag values |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the read-only safety profile, but the description adds real value by disclosing the return shape (sorted date-descending, metadata like link/owner/source, widgetEvents and scopeId). With no output schema this is the only place that return behavior is described, though pagination/limits are still unaddressed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the examples are genuinely useful. The sentence 'After query you may optionally call this to check if any events happened around that time' is somewhat meandering and slightly redundant, but overall the text is efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter read tool with no output schema, the description covers purpose, filtering, and return contents adequately. Minor gaps remain around pagination or result limits, but nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the examples add meaning by showing how from/to bracket a spike window and how tags filter (e.g. ['deployment']), clarifying intended usage beyond the field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) plus resource (events), clarifies the event types (deployments, business changes, provider updates), and scopes to an organization within a date range. It does not explicitly distinguish itself from sibling list/create/update tools, but the verb+resource combination is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this to correlate cost changes with real-world events' gives a clear use context, and the two examples show concrete invocations. It does not, however, name when-not to use it or route to alternatives like search or find_cost_change_factors, so exclusions are missing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_metricsARead-onlyInspect
List saved Costory business metrics and, optionally, matching live external metrics from connected integrations (e.g. Tsuga, BigQuery, Datadog, CloudWatch, Google Cloud Monitoring). Saved metrics return id/name/type for { type: "metric", metricId: "..." } in query. Set includeExternal: true with a specific search term to return externalMetrics with provider, integrationId, integrationName, metricName, unit, capabilities, and attributes. That catalog is enough to build { type: "externalMetric", provider, integrationId, metricName, aggregator, groupByFields, conditions } for Tsuga, Datadog (attributes are tag keys; metricName is the Datadog metric name), CloudWatch (metricName is Namespace/MetricName such as AWS/EC2/CPUUtilization; attributes are dimension names), Google Cloud Monitoring (provider "cloudmonitoring"; metricName is the metric type such as compute.googleapis.com/instance/cpu/utilization; attributes are fully-qualified group-by fields such as metric.label.state or resource.label.project_id, and conditions is an optional Monitoring filter), or Amplitude. For BigQuery or S3, the same hit also includes schema (typed columns: DATE / NUMERIC / STRING). Pick dateColumn from the first DATE field, metricColumn from the first NUMERIC field, and groupByFields from STRING fields (also listed in attributes). gapFillingMethod is not in the catalog — default FORWARD_FILL unless the user specifies ZERO, LINEAR_INTERPOLATION, or SPREAD. Those three fields are required on both query and a new virtual-dimension telemetry allocation. Do not call includeExternal without search; external catalogs can be large, and the tool will ask for a search term instead of listing everything. externalLimit (default 50, max 50) caps matching external results. New virtual-dimension telemetry reallocations use includeExternal + an inline externalMetric (same fields as a query series, plus exactly one groupByFields entry as the series key). Pass datasourceId only to inspect a leftover saved usage-metric datasource's groupByDimensions — not for new reallocations. Then call query (type: "metric", metricId, groupBy) to inspect the values for that leftover datasource. The datasourceId is the same metricsDatasource id this tool returns as a saved-metric id (strip any ::metricName suffix). Does not return values (use query).
EXAMPLES:
• "What business metrics do we have?" → {}
• "Find Tsuga metrics about requests" → { includeExternal: true, search: "request" }
• "Find BigQuery tables about revenue" → { includeExternal: true, search: "revenue" }
• "Find Datadog metrics about CPU" → { includeExternal: true, search: "cpu" }
• "Find CloudWatch metrics about CPU" → { includeExternal: true, search: "CPUUtilization" }
• "Find GCP Monitoring metrics about CPU" → { includeExternal: true, search: "cpu/utilization" } (Cloud Monitoring search matches the metric type, not its display name)
• "What live metrics can I reallocate shared cost by?" → { includeExternal: true, search: "cpu" }
• "What can I split a leftover saved-metric datasource by?" → { datasourceId: "clx…" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| search | No | Optional filter on saved metric names. Required when includeExternal is true because external metric catalogs can be large. | |
| datasourceId | No | Special list mode (not a query field): return groupBy dimensions for ONE leftover saved usage-metric datasource. New telemetry reallocations use includeExternal + inline externalMetric instead. Pass the metricsDatasource id from metrics[].id (strip any `::metricName` suffix). Then call query with type:"metric", metricId:<same id>, groupBy:<dimension>. Do not confuse with query metricId — that is the identity field on the query tool; this filter only returns dimensions. When set, search / includeExternal are ignored. | |
| externalLimit | No | Max matching external metrics to return when includeExternal is true and search is provided (capped at 50). | |
| includeExternal | No | When true and search is provided, include matching live integration-backed external metrics in externalMetrics. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only and non-destructive behavior, so the description does not need to re-establish safety. It adds useful behavioral context: external catalogs can be large and will ask for a search term, externalLimit is capped at 50, gapFillingMethod defaults to FORWARD_FILL, and the tool returns metadata only, not actual values.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but dense, and nearly every sentence carries provider-specific behavior an agent needs. It front-loads the core purpose, then details edge cases and ends with examples. It is not as compact as the best definitions, but it contains little filler and uses examples to illustrate the main parameter modes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the full burden of explaining return shapes, and it does: saved metric id/name/type, externalMetrics fields, BigQuery/S3 schema, datasourceId special mode, gapFilling default, and required fields for query or reallocations. For a tool with this many provider-specific modes, the coverage is comprehensive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though schema coverage is 100%, the description substantially enriches the parameters: search is required when includeExternal is true, datasourceId has a special list mode with exact id-suffix handling, includeExternal selects a specific return shape, and externalLimit has documented defaults and caps. It also explains how returned fields map to downstream query and externalMetric construction.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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: list saved Costory business metrics and optionally matching live external metrics. It clearly separates this tool from query by stating it does not return values, which helps an agent pick it correctly among the sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage rules: use includeExternal only with a search term, pass datasourceId only for inspecting leftover datasource groupByDimensions, and call query afterwards to inspect values. The examples map natural-language requests to concrete parameter objects, which is strong when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_organizationsARead-onlyInspect
List organizations you have access to. Use this to discover available orgs or when a user needs to choose between multiple organizations. Note: slug is now optional on all tools and will auto-detect from your account.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds useful context about slug auto-detection and scoping results to accessible orgs, going beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences with front-loaded purpose. The usage note and slug clarification are valuable with no redundant content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple zero-parameter list tool with no output schema, the description covers purpose, usage, and a relevant behavioral note. It lacks explicit return format, but this is manageable for such a straightforward operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so baseline is 4. The description adds no parameter-specific info, but none is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'List organizations you have access to' with a specific verb and resource. It distinguishes itself from other list tools by focusing on organizations and adding context about discovering orgs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use: 'to discover available orgs or when a user needs to choose between multiple organizations.' It does not mention alternatives or when-not, but the guidance is clear and contextually relevant.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_reportsARead-onlyInspect
List compact report summaries for the organization: id, name, kind (SCHEDULED / ONE_TIME), status, nextRunDate, destination counts, and lastRunHealth. Includes on-demand reports. Optional filters: kind (SCHEDULED / ONE_TIME), teamId (reports scoped to one team), and includeArchived (default false). Read-only.
EXAMPLE: { kind: "SCHEDULED" }
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | Filter by cadence: SCHEDULED or ONE_TIME. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| teamId | No | Filter by owning team id. | |
| includeArchived | No | Include archived reports (default false). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false; the description goes beyond by specifying the returned fields (id, name, kind, status, nextRunDate, destination counts, lastRunHealth), confirming on-demand reports are included, and documenting the includeArchived default. It does not address pagination or permission requirements, but given the strong annotations this is sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with the essential purpose, followed by a field list, filters, a short read-only note, and an example. Every sentence contributes; there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a list operation with no required parameters and no output schema, the description covers the return fields and filters. It does not mention pagination or potential size limits, but the tool's simplicity and the provided field list make it complete enough for an agent to use correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% coverage with descriptions for all four parameters, so the baseline is 3. The description adds a concrete usage example ({ kind: 'SCHEDULED' }) and restates filter semantics, which slightly enhances the parameter understanding beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'List compact report summaries.' It defines the output fields and states the scope ('for the organization'), clearly distinguishing it from sibling report-execution tools like get_report_execution, run_report_now, and update_report. It also clarifies inclusion of on-demand reports.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implicitly establishes when to use this tool (when you need a compact overview of reports) and lists available filters, but it does not explicitly mention when not to use it or point to alternatives (e.g., use get_report_execution for full details). The read-only hint is clear context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_skillsARead-onlyInspect
List available Costory MCP workflow skills (skillId, name, description) from the remote skill catalog. Call this when you are unsure which skillId to load, then call get_skill with the chosen skillId. If already known (virtual-dimensions, dashboards, reports, query, recipes), you may skip this and call get_skill directly. EXAMPLE: "Which workflow guides can I load?" → {}
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true and destructiveHint=false. The description adds that the catalog is remote and lists the returned fields, providing useful context beyond the annotations, though it doesn't cover other behaviors like pagination.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact, front-loads the primary purpose, and includes usage guidance and an example without any redundant filler. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple zero-parameter read-only listing tool, the description fully covers what it does, when to use it, what it returns, and the relationship to get_skill. There are no significant gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so schema coverage is trivially complete. The description includes an example invocation with {}, satisfying the baseline for the zero-param case.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') with a clear resource ('Costory MCP workflow skills') and enumerates the returned fields (skillId, name, description), making it distinct from the sibling get_skill tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to call this tool ('when you are unsure which skillId to load') and when to skip it in favor of get_skill ('If already known... call get_skill directly'), naming the alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tagsARead-onlyInspect
List all tags in the organization with a count of tagged resources (dashboards, views, explorers, alerts). Use to discover available tag values before passing them to create_event or update_event as the tags array, or before calling create_alert, update_alert, create_dashboard, update_dashboard, create_report, or update_report with tags.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description goes beyond that by disclosing org-level scope and the composition of the returned data (per-type counts), which an agent cannot infer from the annotations alone. It stops short of describing pagination or whether empty orgs return an empty list.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and return shape, then the usage guidance. The enumeration of eight downstream tools is long but each entry earns its place by naming a real integration point; only minor trimming is possible.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description usefully compensates by describing the return contents (tags with resource counts). Combined with a fully documented single parameter and read-only annotations, an agent has what it needs, though ordering and pagination behavior remain unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single slug parameter is fully documented in the schema (including auto-detect behavior and the multi-org failure case). The description adds nothing about the parameter, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource ("List all tags in the organization") plus a precise statement of what each entry contains (counts of tagged dashboards, views, explorers, alerts). This clearly distinguishes it from siblings like delete_tag or the various list_* tools by stating both scope and payload.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to call it ("discover available tag values before passing them to...") and enumerates the concrete downstream tools that consume tags (create_event, update_event, create_alert, update_dashboard, etc.). This is a textbook when-to-use routing instruction with named alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_teamsARead-onlyInspect
List all non-archived teams in the organization, including teams the current user is not a member of. Each team has an id (pass as teamId on set_object_permissions), visibility, and role (the caller's membership role when they belong to the team; omitted otherwise). Each scope has an id (pass as scopeId to query or a report widget to apply the saved scope) and a pre-defined filterCel string (pass as filterCel for the same effect inline). Optional query filters by team name. Use when the user asks about a team's costs, wants to filter by team scope, or needs a teamId to share a dashboard, report, budget, or cost alert.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| query | No | Optional team-name search. Omit to list every non-archived team in the organization. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, covering the safety profile. The description adds valuable behavioral context: it includes non-member teams, explains that 'role' is omitted when the caller isn't a member, and details how the returned 'id' and scope 'id'/'filterCel' should be used in other calls. This goes beyond the annotation basics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is moderately long but every sentence carries functional information: resource scope, field meanings, how to repurpose IDs, and usage triggers. It is front-loaded with the core purpose and organized logically, though it could be tightened by merging the scope/filterCel details into a more compact phrasing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Since there is no output schema, the description appropriately explains the return structure (id, visibility, role, scope id, filterCel) and how to consume those fields in other tools. It omits pagination or ordering, but those are not critical for a list operation, and the provided information is sufficient for correct invocation and interpretation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents both parameters fully (slug auto-detection behavior, and query as optional team-name search) with 100% coverage. The description reiterates the query filtering but adds no new parameter-level meaning. Per the rubric, high schema coverage yields a baseline of 3, which is appropriate here.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise verb and resource ('List all non-archived teams') and clearly distinguishes itself by noting it includes teams the user is not a member of, which sets it apart from other list_* tools. It also specifies the data returned per team, removing any ambiguity about what the tool outputs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly lists three concrete use cases: when the user asks about team costs, wants to filter by team scope, or needs a teamId for sharing dashboards/reports/budgets/alerts. It does not mention when not to use it or name alternative tools, though the listed scenarios are sufficiently specific to guide selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_usersARead-onlyInspect
List organization members so you can share a dashboard, report, budget, or cost alert with a person. Returns id, name, and email. Optional query filters by first name, last name, or email; omit it to list up to 100 members. Use the returned id as userId on set_object_permissions USER grants. You do not need to share a team with someone to list or share with them.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| query | No | Optional name or email search. Omit to list up to 100 organization members. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as read-only, and the description adds that it returns id, name, and email, lists up to 100 members by default, and filters by name/email. The extra note that sharing does not require sharing a team is useful behavioral context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four focused sentences cover purpose, return fields, query behavior, and the downstream use of the returned id. Every sentence earns its place and no filler is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, read-only, zero-required-parameter tool with complete schema coverage, the description is complete. It explains results, filtering, limits, and how to use the output, so an agent has enough to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds the first-name/last-name/email detail and reinforces the omit-query behavior, but does not materially expand on the slug parameter beyond what the schema already says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific verb 'List' and resource 'organization members', plus the purpose of sharing dashboards/reports/budgets/alerts. Clearly distinguishes from siblings like list_teams and list_alerts by focusing on people rather than resources.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear when-to-use context: when you need a person's id to share something with them or to set object permissions. It explains the optional query behavior and the no-team-sharing prerequisite, though it does not explicitly name an alternative like list_teams for team-related queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_virtual_dimensionsARead-onlyInspect
List virtual dimensions (custom cost axes) in the organization. virtualDimensionId in inputs equals id from list/get/search. Returns id, virtualDimensionId (same as id), name, bqName, description, status, computeStatus, tags (string[] of tag names), hasPendingDraft, and optionally draftValidation.ok when includeDraftStatus is true. bqName is the immutable BigQuery/CEL field name (e.g. virtual_environment) — set once at create from the initial name and never updated, even when name changes. Always use bqName (not name) for groupBy/filterCel in query. Use query to match name/bqName/description/tag; status filters DRAFT vs COMPLETED. Newly created MCP drafts typically have status DRAFT; published VDIMs are COMPLETED. Paginate with limit (default 50, max 100) and offset. Call this before get when the user refers to a VDIM by name rather than id.
EXAMPLE: "Show our environment virtual dimensions" → { query: "environment" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| limit | No | Max rows to return (default 50, cap 100) | |
| query | No | Filter by name, bqName, description, or tag name | |
| offset | No | Number of rows to skip before returning results | |
| status | No | Filter by virtual dimension status | all |
| includeDraftStatus | No | Include draftValidation.ok for rows with a pending draft |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true and destructiveHint=false; the description adds substantial behavioral context: bqName is immutable and set once at creation, status semantics for drafts vs published VDIMs, and the exact return fields. It also specifies that virtualDimensionId equals id, which is a non-obvious data model behavior. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence is purposeful. It front-loads the core purpose, then explains field relationships, usage rules, and ends with a concrete example. There is no fluff or repetition of schema content; the structure is logical and scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description takes full responsibility for explaining return values, which it does explicitly (id, virtualDimensionId, name, bqName, description, status, computeStatus, tags, hasPendingDraft, draftValidation.ok). It also covers edge cases like draft vs published status, pagination behavior, and the bqName immutability pitfall. This is complete for an agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameter descriptions at 100% (baseline 3). The description adds meaningful context beyond the schema, notably the immutability of bqName and the interpretation of status values (DRAFT vs COMPLETED). It also reiterates the limit cap of 100 and shows an example query, but does not deeply enhance every parameter; hence a 4 rather than 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'List virtual dimensions (custom cost axes) in the organization.' It clearly differentiates from the 'get' sibling by stating 'Call this before get when the user refers to a VDIM by name rather than id.' The example further anchors the purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance against the sibling 'get' tool. It also explains how to use query filters, status filters (DRAFT vs COMPLETED), pagination, and the critical rule to always use bqName (not name) for groupBy/filterCel. This is actionable direction beyond generic listing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
preview_alertARead-onlyInspect
Backtest a cost-alert condition BEFORE creating it: replays the condition against the last lookbackDays (default 45) of data and reports how many times it would have fired. Takes the same queries + condition + dedup as create_alert (no notification channel needed). The dedup window is per groupBy value; delivery stays one message listing newly eligible groups. Returns the evaluation window, firingDays (distinct days the condition held), firingRows (per-group fires), notificationsCount (days on which at least one newly eligible group would notify) and a sample of firing dates with per-group state. Use this to sanity-check a condition/threshold (and tune dedup) before calling create_alert.
EXAMPLE: "Would 'alert if 7-day AWS spend tops $50k' have fired this month?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_provider in ["AWS"]" }], condition: "rollingSum(a, 7, DAY) > 50000", dedup: { kind: "CALENDAR", calendarUnit: "WEEK" }, lookbackDays: 30 }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| dedup | No | Deduplication config controlling how often a still-firing group notifies. The window is per groupBy value; delivery stays one message listing newly eligible groups. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days. Optional here: when provided the result also reports how many notifications would actually be sent after dedup. | |
| queries | No | Same series objects as the `query` tool `queries` array (referenced by `condition`). | |
| condition | Yes | Alerts v3 firing rule: a single boolean expression over the query names (`name` field of each query). Supports arithmetic (+ - * /), comparisons (> >= < <= == !=), logical and/or/not, parentheses, and these window functions: rollingSum(a, N, UNIT) (trailing sum over the last N units, UNIT ∈ DAY|WEEK|MONTH, inclusive of today), weekToDateSum(a) (Monday-to-date), monthToDateSum(a) (1st-of-month-to-date), and timeShift(a, N, UNIT) (value shifted back N units; may wrap a window function). Examples: `a > 1000`, `rollingSum(a, 7, DAY) > 1000`, `(a - timeShift(a, 1, DAY)) / timeShift(a, 1, DAY) > 0.2`, `a > 10000 or rollingSum(a, 7, DAY) > 50000`. | |
| lookbackDays | No | Trailing window length in days to replay the condition over (default 45). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag this as read-only, and the description adds behavior well beyond that: it replays a condition, reports firing days/rows, applies dedup semantics per groupBy value, and describes the notification count computation. It also discloses the default lookback window and returns a sample of firing dates with state, giving the agent a clear model of what execution will do.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and is organized clearly: what it does, what it takes, what it returns, and when to use it. It is somewhat long and repeats dedup details that also appear in the schema, but the example and return-field listing earn their place for such a complex tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by enumerating the return values (`firingDays`, `firingRows`, `notificationsCount`, sample dates with `state`), which is essential for the agent to interpret results. The schema handles parameter syntax fully, the annotations handle safety, and the description provides workflow context and a realistic invocation example. Nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by explaining how parameters combine with a concrete example ('alert if 7-day AWS spend tops $50k') and by tying queries/condition/dedup to create_alert semantics. It also clarifies that dedup affects the returned notification counts, which is not explicit in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 ('Backtest a cost-alert condition') and explicitly distinguishes itself from create_alert by saying it runs BEFORE creation and needs no notification channel. This makes the tool's role unmistakable even among many sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says to use this to 'sanity-check a condition/threshold (and tune dedup) before calling create_alert,' naming the closely related sibling and the intended workflow. It also communicates that notification-channel setup is not needed here, reinforcing the boundary between previewing and creating.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
preview_report_widgetARead-onlyInspect
Read-only preview of ONE report widget — pass the same reportContext + widget object you will later hand to create_report. Returns a token-bounded, per-type result; never creates or delivers. By type: DIGEST → resolvedPeriod, comparison totals, largest changes, root nodes, summary markdown (when display is "summary"), recommendations; TOP_FLOP → bounded top/flop rows; GRAPH_SNAPSHOT → bounded series sample (no image) + explorerUrl; DASHBOARD_PDF/TEXT → validity pointer. Put shared period / groupBy / metric / currency / conditionsCel in reportContext; widgets omit matching fields to inherit. The legacy context alias is temporarily accepted but deprecated; never send both. DIGEST hierarchy: reportContext.groupBy (root, preferred) + additionalGroupBy (deeper levels). DIGEST threshold defaults: 100 absolute / 5% relative / 20 changes (topLargestAbsoluteChange integer 1–100, applied at every tree level). DIGEST AI (opt-in): display ("tree" default | "table" | "summary" for LLM executive narrative) and enableAiInvestigation (boolean, default false — per-node deep analysis, independent of display). DIGEST aggBy is Month or Week — never Period (Period is TOP_FLOP).
EXAMPLES:
• Monthly DIGEST by env → project → service (AI summary) → { reportContext: { datePreset: "LAST_MONTH", groupBy: "cos_environment", metricId: "cost", currency: "USD" }, widget: { type: "DIGEST", queries: [{ type: "cost", name: "a" }], aggBy: "Month", additionalGroupBy: ["cos_sub_account_id", "cos_service_name"], minAbsoluteDiff: 100, minRelativeDiff: 5, topLargestAbsoluteChange: 20, display: "summary", enableAiInvestigation: false } }
• Weekly TOP_FLOP → { reportContext: { datePreset: "LAST_WEEK", groupBy: "cos_service_name", metricId: "cost", currency: "USD" }, widget: { type: "TOP_FLOP", queries: [{ type: "cost", name: "a" }], aggBy: "Period", topN: 5, flopN: 5 } }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| widget | Yes | Single widget to preview (same shape as create/update `widgets[]` items). | |
| context | No | Deprecated alias for `reportContext`; supported temporarily for backward compatibility. Do not send both fields. | |
| reportContext | No | Optional shared context the widget inherits (same shape as `create_report` `reportContext`). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, and the description adds substantial behavioral context: results are 'token-bounded', GRAPH_SNAPSHOT returns 'no image', DIGEST returns resolvedPeriod/comparison totals/largest changes/root nodes, and the legacy context alias is deprecated. This goes well beyond what annotations alone provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose and safety, then organized cleanly by widget type with uppercase labels and examples. It is long, but the length is largely justified by four widget variants and numerous DIGEST options; still, the density keeps it from being maximally concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the per-type return breakdown (resolvedPeriod, bounded rows, explorerUrl, validity pointer, summary markdown) carries the burden of explaining return values, and it does thoroughly. Combined with constraints, defaults, deprecation guidance, and examples, an agent has everything needed to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema: inheritance semantics (shared reportContext fields; widgets omit matching fields to inherit), DIGEST hierarchy via groupBy + additionalGroupBy, threshold defaults, AI option behavior, aggBy constraints, and two concrete payload examples. This materially reduces misparameterization risk.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('preview of ONE report widget') on a specific resource, and immediately differentiates it from create_report by instructing the agent to pass the same object it will later hand to create_report. It also clarifies the boundary with 'never creates or delivers' and enumerates per-type outputs, so purpose is unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The preview-before-create workflow is explicit: 'pass the same reportContext + widget object you will later hand to create_report.' It also gives clear exclusions — never creates or delivers, never send both context and reportContext, and DIGEST aggBy is never Period — which tells the agent when not to use this tool and how to avoid misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
preview_virtual_dimension_draftARead-onlyInspect
Preview per-rule costs or a groupBy breakdown (read-only BigQuery). virtualDimensionId in inputs equals id from list/get/search. Operates on the latest pending draft if one exists (draftPersisted: true); otherwise previews published rules in memory without creating a draft (draftPersisted: false — not publishable). To publish changes after editing published-only state, call update_virtual_dimension_draft for an existing virtualDimensionId (or create_virtual_dimension_draft for a brand-new VDIM) — preview alone does not persist. Call after a successful create/update. Read the "summary" field first. Both modes share the same time window: optional datePreset (official presets — TRAILING_30_DAYS default, same enum as dashboards) or explicit from/to (YYYY-MM-DD, same shape as query); mutually exclusive. mode "costs": per-rule costs with totals.namedRulesSharePercent / totals.leftoverSharePercent, each rule's conditionCel and allocatesTo, plus howToRead. mode "breakdown": spend inside a target rule (ruleId or ruleIndex) split by groupBy — returns period (datePreset or from/to), scope.explanation, topValues[{ value, cost, percentOfScope }], and dailyTotals (not raw chart JSON). Discover valid groupBy values via search with type: ["dimensions"]. position (from get and preview costs output) and ruleIndex (preview/overlap input) refer to the same 0-based ordinal in the ordered rule list [...rules, leftover]. Prefer ruleId over ruleIndex. Target leftover via ruleId of leftoverRule or ruleIndex: rules.length. Optional currency (default USD) — pass the same value as query cost queries to align totals. costMetric selects the cost column (default cost); valid ids: cost, effective_cost, list_cost, contracted_cost (also accepts contracted_costs), unblended_cost, net_unblended_cost, amortized_cost, net_amortized_cost.
EXAMPLES:
• "Preview Environment VDIM draft costs" → { virtualDimensionId: "<virtualDimensionId from create/list>", mode: "costs" }
• "What cos_region values sit inside Production?" → { virtualDimensionId: "<virtualDimensionId from create/list>", mode: "breakdown", ruleId: "<from get/preview costs>", groupBy: "cos_region", datePreset: "TRAILING_30_DAYS" }
• "Preview costs in EUR to match earlier queries" → { virtualDimensionId: "<virtualDimensionId from create/list>", mode: "costs", currency: "EUR", from: "2025-01-01", to: "2025-03-31" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Explicit window end (YYYY-MM-DD). Use with from instead of datePreset — same shape as query. | |
| from | No | Explicit window start (YYYY-MM-DD). Use with to instead of datePreset — same shape as query. | |
| mode | Yes | `costs` (per-rule costs including leftover and rule metadata) or `breakdown` (drill into a rule). | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| ruleId | No | Target rule id (breakdown mode; preferred over ruleIndex) | |
| groupBy | No | Dimension to split by (breakdown mode) | |
| currency | No | Cost currency (default USD). Pass the same currency as prior query calls to align preview totals. | |
| ruleIndex | No | Target rule index (breakdown mode fallback) | |
| costMetric | No | Cost column id (default cost). Valid: cost, effective_cost, list_cost, contracted_cost, unblended_cost, net_unblended_cost, amortized_cost, net_amortized_cost (also accepts contracted_costs → contracted_cost). | |
| datePreset | No | Official date preset (same values as dashboards/query context). Default TRAILING_30_DAYS. Mutually exclusive with from/to. | |
| virtualDimensionId | Yes | Virtual dimension id (stable public identifier) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint and destructiveHint annotations, the description discloses rich behavioral detail: it operates on the latest pending draft if present, otherwise previews published rules in memory without creating a draft, and marks such previews as not publishable. It also explains return fields per mode, the draftPersisted flag semantics, and the relationship between position and ruleIndex. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but dense, and almost every sentence adds necessary information for an 11-parameter tool with no output schema. It is front-loaded with the core purpose and includes three concrete examples. It loses a point for being a wall of text with some minor redundancy around persistence, though the length is largely justified by the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the full burden of explaining return values, and it does so thoroughly: summary, totals, howToRead, topValues, dailyTotals, and scope.explanation are all mentioned. It also covers edge cases like targeting leftover rules, draft persistence states, costMetric aliases, and time-window selection. Nothing an agent needs to invoke this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema description coverage is 100%, the description adds substantial meaning beyond the schema: virtualDimensionId maps to id from list/get/search, datePreset uses the same enum as dashboards, from/to share shape with query, ruleIndex is equivalent to position, costMetric accepts aliases, and currency should match prior query calls. This far exceeds the baseline for a fully documented schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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: 'Preview per-rule costs or a groupBy breakdown (read-only BigQuery).' It clearly names the two modes and explicitly distinguishes this preview tool from create/update/publish siblings by stating that preview alone does not persist. This leaves no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: call after a successful create/update, use update_virtual_dimension_draft or create_virtual_dimension_draft to publish, and prefer ruleId over ruleIndex. It also gives concrete exclusions, such as datePreset being mutually exclusive with from/to, and explains how to discover valid groupBy values via search. This is exemplary routing and precondition guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
publish_virtual_dimensionAInspect
Validate the latest persisted pending virtual dimension draft, then promote it to published and kick off a BigQuery refresh. virtualDimensionId in inputs equals id from list/get/search. Requires a pending draft row — call update_virtual_dimension_draft for an existing virtualDimensionId (or create_virtual_dimension_draft for a brand-new VDIM) before publish. Preview alone does not create a draft. Rejects invalid drafts with draftValidation errors (no publish). On success returns virtualDimensionId, immutable bqName, name, computeStatus, and published rules — does not wait for the refresh job. After publish, query using returned bqName for groupBy/filterCel in query — bqName is immutable (set at create from the initial name) and does not change when name is renamed. computeStatus is REFRESHING when the refresh job was queued (async — poll via get or list_virtual_dimensions until COMPLETED before querying), or TO_REFRESH when the draft was promoted but queuing the refresh job failed — do not query yet; retry publish or re-check computeStatus until COMPLETED. Deletes the pending draft version. Clerk MCP only — not available on the service route.
EXAMPLE: "Publish the Environment VDIM draft" → { virtualDimensionId: "<virtualDimensionId from create/list>" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| virtualDimensionId | Yes | Virtual dimension id (stable public identifier) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses critical behaviors not captured by annotations: it deletes the pending draft version, starts an async BigQuery refresh, returns computeStatus values (REFRESHING/TO_REFRESH) with guidance on querying, explains bqName immutability, and notes it does not wait for the refresh job. Since annotations provide no safety hints (all false), this full transparency is essential and well-provided.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-structured, front-loading the main action and followed by prerequisites, error handling, return value details, and an example. Each sentence adds essential information without redundancy, making the length appropriate for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite lacking an output schema, the description fully covers return fields, error conditions, async behavior, side effects (deletion), and operational caveats. It explains what happens in every computeStatus state and instructs on next steps, making the description self-sufficient for an agent to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful context by stating that virtualDimensionId equals the id from list/get/search and providing a concrete example mapping the example utterance to the parameter. This goes beyond the schema's 'stable public identifier' description, earning a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states the tool validates and promotes a persisted pending virtual dimension draft, then triggers a BigQuery refresh. It clearly distinguishes from siblings like create_virtual_dimension_draft, update_virtual_dimension_draft, and preview_virtual_dimension_draft by focusing on the publish action and its effects.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit prerequisites: a pending draft must exist, and directs the user to update_virtual_dimension_draft or create_virtual_dimension_draft beforehand. It also warns that preview alone does not create a draft, explains when to poll with get/list_virtual_dimensions, and notes the tool is Clerk MCP only, giving clear when-to-use and alternative context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
queryARead-onlyInspect
MINIMUM VALID CALL: { "queries": [{ "type": "cost", "name": "a", "metricId": "cost", "currency": "USD" }], "datePreset": "MTD", "aggBy": "Day" } Required per series: type (cost|metric|usage|formula|budget|externalMetric) and name. Put labels in alias.
Unified query tool for cost data, custom metrics, usage metrics, external (live integration) metrics, period comparisons, formulas, and budgets. QUERY NAMING: set type and name (prefer short ids like a/b/c for formulas); put human labels in alias (e.g. "Cost by environment") — never in name. Example: { type: "cost", name: "a", alias: "Cost by environment", groupBy: "cos_environment", ... }. For costs: metricId (cost column, default "cost") and currency (default "USD"). Use costMetricId and currency from get when aligning with a budget. For custom business metrics: use [{ type: "metric", metricId: "..." }] — get IDs from list_metrics. For infra usage metrics (e.g. CPU hours, network bytes): use [{ type: "usage", metricId: "..." }] — call suggest_usage_metrics first to discover valid metricIds for your scope. For live external metrics (not saved as Costory metrics): use [{ type: "externalMetric", provider: "...", integrationId: "...", metricName: "...", aggregator: "SUM", groupByFields: [], conditions: "..." }] — discover provider, integrationId, and metricName via list_metrics with includeExternal: true and a specific search term. Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. Datadog: same shape as Tsuga — metricName is the Datadog metric name (e.g. system.cpu.user), groupByFields are tag keys (e.g. host, service), conditions is an optional Datadog tag filter (e.g. env:prod). When query is set it is the Datadog metrics query string (pass-through); metricName / aggregator / conditions / groupByFields are ignored; .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. CloudWatch: set provider: "cloudwatch"; metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names (e.g. InstanceId); conditions is an optional dimension filter. BigQuery: set provider: "bigquery"; metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required — pick dateColumn/metricColumn from list_metrics schema (first DATE / first NUMERIC) and default gapFillingMethod to FORWARD_FILL; groupByFields are string column names (not CEL). S3: set provider: "s3"; identical field shape to bigquery — metricName is the fully-qualified table id returned by list_metrics (a Costory-managed external table over the customer's mirrored Parquet); same schema-derived columns. Use externalMetric for exploration when no saved metric matches; prefer saved { type: "metric" } when one exists. PERIOD: prefer datePreset (same DatePreset enum as dashboards/reports, e.g. MTD, LAST_MONTH, TRAILING_30_DAYS, LAST_3_MONTHS, YTD) over hand-computed from/to whenever a preset matches — mutually exclusive with from/to. Response includes the resolved period dates. For comparison: add compare: {} (or compare: { from, to }) — omit compare dates to auto-derive the preceding period (preset-aware, e.g. LAST_MONTH → previous calendar month). For formulas: add { type: "formula", formula: "a / b" } referencing other queries by name. For budgets: use [{ type: "budget", budgetId: "..." }] — despite the field name, this must be the budget version ID (same value as budgetVersionId from get); search returns the parent budget id only, so call get with that id to obtain budgetVersionId before querying. Optional chartType on each query: BAR, LINE, AREA, WATERFALL, or TABLE (defaults to LINE). groupBy is the SPLIT dimension, filterCel is the SCOPE (CEL). Before guessing CEL field names, call search with type: ["dimensions"] — empty query lists all fields; a keyword narrows to matching values. Costory label dimensions use a cos_ prefix (e.g. cos_service_name). Unlabelled resources have null on label dimensions; use filterCel with == null / != null (not is_null or string "null"). Custom virtual dimensions: use immutable bqName from list/get VDIM tools as groupBy / filterCel (not display name). Poll computeStatus until COMPLETED after publish. Optional analyze.changePoint (true or { ignoreWeekends }) runs change-point detection once per query after a timeseries result (incompatible with compare). Optional limit (integer 1–1000): max groups/rows per series. Do NOT set limit unless you need a different cap — when omitted, results default to 100 groups. Set limit above 100 (e.g. 250 or 500) when the user asks for a long tail or full breakdown list. OPTIONAL: After receiving results, consider calling "list_events" for the same date range to correlate cost changes with events, and "suggest_actions" to present follow-up options to the user.
EXAMPLES:
• "What are my total costs this month?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }], datePreset: "MTD", aggBy: "Day" }
• "Break down AWS costs by service over the last 90 days" → { queries: [{ type: "cost", name: "a", alias: "AWS by service", metricId: "cost", currency: "USD", groupBy: "cos_service_name", filterCel: "cos_provider in ["AWS"]" }], datePreset: "TRAILING_90_DAYS", aggBy: "Week" }
• "Show costs for resources without an environment label" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_environment == null" }], datePreset: "TRAILING_30_DAYS", aggBy: "Day" }
• "How did our costs change vs last month?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }], datePreset: "LAST_MONTH", compare: {} }
• "Show CPU hours alongside compute costs" (call suggest_usage_metrics first to get valid metricIds) → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "usage", name: "b", metricId: "k8s_cpu_hours" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "What is our cost per request?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "metric", name: "b", metricId: "" }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS" }
• "Cost per request volume" (after list_metrics with includeExternal: true and search: "request") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "tsuga", integrationId: "", metricName: "", aggregator: "SUM" }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Cost per BigQuery revenue table" (after list_metrics with includeExternal: true and search: "revenue") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "bigquery", integrationId: "", metricName: "my-project.analytics.revenue", dateColumn: "event_date", metricColumn: "amount", gapFillingMethod: "ZERO", aggregator: "SUM" }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Cost per CPU usage from Datadog" (after list_metrics with includeExternal: true and search: "cpu") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "datadog", integrationId: "", metricName: "system.cpu.user", aggregator: "AVG", groupByFields: ["host"] }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Cost per EC2 CPU from CloudWatch" (after list_metrics with includeExternal: true and search: "CPUUtilization") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "cloudwatch", integrationId: "", metricName: "AWS/EC2/CPUUtilization", aggregator: "AVG", groupByFields: ["InstanceId"] }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Budget per calendar month" → { queries: [{ type: "budget", name: "a", budgetId: "" }], datePreset: "LAST_3_MONTHS", aggBy: "Month" } (budgetVersionId from get, not the parent id from search)
• "Budget month-to-date by day (cumulative within each month — which day did we reach the budget?)" → { queries: [{ type: "budget", name: "a", budgetId: "", rollingAggregation: { aggregator: "SUM", window: { preset: "MONTH" } } }], datePreset: "MTD", aggBy: "Day" }
• "Formula: month-to-date cost vs month-to-date budget (both rolling SUM per month, e.g. utilization a/b)" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", rollingAggregation: { aggregator: "SUM", window: { preset: "MONTH" } } }, { type: "budget", name: "b", budgetId: "", rollingAggregation: { aggregator: "SUM", window: { preset: "MONTH" } } }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "MTD", aggBy: "Day" }
• Custom one-off range → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }], from: "2026-01-15", to: "2026-02-12", aggBy: "Day" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Explicit window end (inclusive, YYYY-MM-DD). Use with from instead of datePreset. | |
| from | No | Explicit window start (YYYY-MM-DD). Use with to instead of datePreset. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| aggBy | No | Time grain for the series: Hour, Day, Week, Month, or Period (default Month). Hour requires Costory Pro. | Month |
| limit | No | Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. | |
| analyze | No | Optional analyses. Set changePoint true (or { ignoreWeekends }) to run change-point detection once per query after a timeseries result. Incompatible with compare. | |
| compare | No | Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders. | |
| queries | No | One or more series (cost / metric / usage / externalMetric / formula / budget). Each needs a `name` for formulas (prefer short ids like a/b/c); put human labels in `alias`. | |
| scopeId | No | Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries. | |
| datePreset | No | Official date preset (same DatePreset as dashboards/reports, e.g. MTD, LAST_MONTH, TRAILING_30_DAYS). Prefer this over hand-computed from/to when a preset matches. Mutually exclusive with from/to. | |
| forecastDays | No | Extend the query window by N days into the future and include forecasted values. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses key operational behaviors: 'Hour requires Costory Pro', 'default to 100 groups', 'Costory will not fill an empty weekly series', and 'Response includes the resolved period dates.' It also flags incompatibilities (changePoint with compare) and default values (limit, aggregator). No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear section labels (MINIMUM VALID CALL, QUERY NAMING, PERIOD, EXAMPLES) and is front-loaded with a minimal call example. It is long and repeats some provider details already present in the schema's externalMetric description, but the organized headers and numerous concrete examples justify most of the length for a tool of this complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (11 parameters, nested oneOf schemas, multiple provider integrations, and no output schema), the description covers all necessary invocation aspects: query types, naming, period selection, comparison, formulas, budgets, limits, and post-call follow-up suggestions (list_events, suggest_actions). It also warns about edge cases like null label handling and Datadog query pass-through restrictions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema description coverage is 100%, the description adds substantial semantics: the naming convention (short ids for name, human labels in alias), that budgetId expects the budget version ID (not the parent ID), rollingAggregation usage for cumulative budgets, and provider-specific field requirements for externalMetric (e.g., BigQuery needs dateColumn/metricColumn/gapFillingMethod). This goes far beyond the schema's property descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific, expansive purpose: 'Unified query tool for cost data, custom metrics, usage metrics, external (live integration) metrics, period comparisons, formulas, and budgets.' This clearly distinguishes it from sibling tools like search (dimension lookup), list_metrics (metadata discovery), and get (entity retrieval) by enumerating the exact data types it queries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance and alternatives: 'Use externalMetric for exploration when no saved metric matches; prefer saved { type: "metric" } when one exists.' It also states required prerequisite calls (suggest_usage_metrics first, list_metrics with includeExternal, get for budgetVersionId) and explains when to prefer datePreset over from/to.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
retry_report_executionAInspect
Side effect: retries one failed delivery only. DANGER ZONE: state the failed execution and its destination from get_report_execution, then ask the user to confirm before calling. Use after partial failure.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| executionId | Yes | Report execution id to retry. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses that the operation has a side effect, is a 'DANGER ZONE', and should only be used after confirmation. This adds meaningful behavioral context beyond the annotations (readOnlyHint=false, destructiveHint=false). It also clarifies the scope ('one failed delivery only'), which is not apparent from the schema. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise, with a clear front-loaded warning ('Side effect', 'DANGER ZONE') followed by actionable instructions. All sentences earn their place, and there is no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for a side-effectful tool: it states the trigger condition, the required preliminary step, and the need for user confirmation. It does not describe the return value, but no output schema exists and the description does not need to. Slight gap: it does not mention what happens on success or failure, but the core context is sufficiently covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides full descriptions for both parameters (slug and executionId), so schema description coverage is 100%. The description does not add further parameter-level detail, but the schema suffices. Baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: it retries one failed delivery only. This specifies the verb (retry), the resource (failed delivery), and the scope (one only), distinguishing it from sibling tools like run_report_now or transfer_report_execution. The purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance ('Use after partial failure') and a required precondition (state the failed execution and its destination from get_report_execution, then ask the user to confirm). It does not explicitly name alternatives, but the instructions are clear enough for safe invocation. Slight deduction for lacking explicit when-not-to-use alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_report_nowAInspect
Side effect: sends real messages now to every configured destination. DANGER ZONE: show the report name and complete destination list from get, then ask the user to confirm before calling. Returns canonical execution URLs and statuses. Never use this to recover only failed destinations.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| reportId | Yes | Report id to run immediately. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the annotations (readOnlyHint=false, destructiveHint=false) by disclosing the real side effect of sending messages and labeling it a 'DANGER ZONE.' It also reveals the return behavior (canonical execution URLs and statuses) and the need for user confirmation. No contradiction with annotations exists; the description adds crucial behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is highly concise with three purposeful sentences: it states the core side effect, the safety step, and the return value/exclusion. There is zero filler and the 'DANGER ZONE' warning is front-loaded for immediate visibility.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description clearly states what is returned (canonical execution URLs and statuses) and covers prerequisites, confirmation, and exclusions. For a two-parameter tool with no nested objects, this is complete and actionable for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already provides 100% description coverage for all parameters (slug and reportId), so the baseline is 3. The description does not add extra parameter-level semantics beyond the schema; it focuses on usage context rather than parameter syntax.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states the tool 'sends real messages now to every configured destination,' which clearly identifies the action (sends) and resource (report destinations). It distinguishes itself from siblings by noting 'Never use this to recover only failed destinations,' pointing to retry_report_execution as an alternative.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use and when-not-to-use guidance: it instructs to 'show the report name and complete destination list from get, then ask the user to confirm before calling' and explicitly warns against using it to recover only failed destinations. This gives clear operational context and an implicit alternative (retry_report_execution).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchARead-onlyInspect
Unified search across your entire Costory workspace — dimension values, events, alerts, dashboards (with their conditionsCel), dashboard templates, reports, virtual dimensions, and budgets. PRIMARY tool for discovering CEL field names: each dimensions result includes dimension (the exact CEL/groupBy name, e.g. cos_sub_account_id), label, and topMatches. Use type: ["dimensions"] to focus on dimensions only. An empty query (query: "") with type: ["dimensions"] returns every dimension with its top values — use this when you need the full field catalog before building filterCel. With a keyword, results are filtered to matching values (e.g. query: "prod" finds production values across dimensions). Use this when a user mentions a product, team, project, or service name and you need to discover where it appears in the cost data before querying. Returns matching dimension values, related events, alerts, dashboards, dashboardTemplates, reports, virtualDimensions, budgets. Virtual dimension hits include id, name, bqName (immutable query field — set at create, never changes), status, and description. Each dashboard result carries a "conditionsCel" string — the dashboard's CEL filter (empty when none) — so before calling update_dashboard you can decide whether to set "extendDashboardConditions: true" on your new widget. Budget results include id (parent budget id for URLs) and name/year; call get with the budget id to obtain the budgetVersionId needed for query. IMPORTANT: Use short, concise search terms — e.g. if the user says 'my kubernetes dashboard', just search for 'kubernetes', not the full phrase. Optional "type" array restricts results to specific entity buckets (dashboards, reports, alerts, budgets, dimensions, virtual_dimensions, events). FOLLOW-UP: After calling search, use get to fetch full details for dashboards, budgets, reports, virtual dimensions, and cost alerts by ID. For dimension values, use "query" to query data grouped by or filtered on the matched dimensions. When the user wants to add to a dashboard, use the id from the dashboards bucket as input to update_dashboard.
EXAMPLES:
• "List all CEL dimensions" → { query: "", type: ["dimensions"] }
• "Find account-related dimensions" → { query: "account", type: ["dimensions"] }
• "Show me kubernetes costs" → { query: "kubernetes" }
• "Find the data team dashboard" → { query: "data team" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | End date for event search (YYYY-MM-DD). Defaults to today. | |
| from | No | Start date for event search (YYYY-MM-DD). Defaults to 90 days ago. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| type | No | Restrict results to these entity types. Omit for all. | |
| query | Yes | Search term (e.g. 'kubernetes', 'account'). Case-insensitive partial matching. Pass empty string with type: ['dimensions'] to list all CEL field names and top values. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint and destructiveHint annotations, the description discloses result structure (dimension, label, topMatches), the behavior of empty queries with type dimensions, and immutability of bqName. It also reveals conditionsCel on dashboard hits and the need to call get for budgetVersionId, providing significant behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but well-structured: leads with the primary purpose, then covers use cases, parameter behavior, follow-up steps, and examples. Each paragraph serves a distinct purpose, and the examples clarify input shapes. It could be trimmed slightly, but the structure keeps it scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully covers return value expectations for each entity type. It includes edge cases (empty query, type restriction), follow-up tool usage, and practical examples. For a complex multi-entity search tool, this description leaves no critical gap for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema descriptions already cover all 5 parameters, giving a baseline of 3. The description adds meaning beyond schema by explaining empty query + type ['dimensions'] returns the full CEL field catalog, type restricts to entity buckets, and query uses case-insensitive partial matching. This enriches parameter understanding beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool performs 'Unified search across your entire Costory workspace' and enumerates all resource types. It explicitly brands itself as the 'PRIMARY tool for discovering CEL field names,' which distinguishes it from sibling tools like query and get.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Use this when a user mentions a product, team, project, or service name' before querying. It also names alternatives for follow-up: use get for detailed records, query for dimension values, and update_dashboard for adding widgets. The instruction to use short search terms adds actionable usage advice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_documentationARead-onlyInspect
Search Costory product docs (Mintlify) and knowledge base in parallel. Synthesize both: Mintlify is the product source of truth, the KB complements with org-specific or internal detail; if they conflict, trust Mintlify. Returns Mintlify matches (titles, snippets, and full docs URLs (Url: https://docs.costory.io/...)) and KB articles (title, summary, full markdown). Optional limit (1–10, default 5) applies to KB. For a full Mintlify page, use get_documentation_page. When citing a page in chat, use the full Url: value verbatim as the markdown href — do not convert to a relative app path.
EXAMPLES:
• "How do I create a budget alert?" → { query: "budget alert" }
• "Why do costs differ from AWS Cost Explorer?" → { query: "AWS Cost Explorer discrepancy", limit: 3 }
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max number of results to return (1–10, default 5) | |
| query | Yes | Search term to find in documentation (e.g. 'budget alerts', 'dimensions', 'API reference') |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as read-only and non-destructive; the description adds substantial context beyond that: parallel search behavior, Mintlify as source of truth, KB as complementary, conflict-resolution priority, return format specifics, and the critical instruction to use the full URL verbatim when citing. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: purpose, synthesis rule, return structure, limit nuance, alternative tool, citation guidance, and examples. It is well-organized, front-loaded with the core behavior, and uses examples to clarify usage without unnecessary filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description appropriately explains return values: Mintlify matches with titles, snippets, and full URLs, and KB articles with title, summary, and markdown. It also covers the limit behavior, conflict resolution, and citation format. For a search tool with this level of nuance, nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so query and limit are documented. The description enhances this by clarifying that the limit applies only to KB results (not Mintlify matches) and by providing concrete query examples. This adds meaning beyond the schema's generic descriptions, though the schema already carries most of the parameter burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description uses specific verb 'Search' with clear resources: Costory product docs (Mintlify) and knowledge base. It states the search runs in parallel and describes the output, distinguishing it from the sibling get_documentation_page by explicitly naming it for full-page retrieval. This gives an agent a precise understanding of what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit guidance: use get_documentation_page for a full Mintlify page, includes query-to-parameter examples, and explains how to handle conflicts between sources. It lacks a broader comparison to other search-like siblings (e.g., search, query), but the key alternatives are addressed and the intended context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_object_permissionsAInspect
Replace the full grant list on a dashboard, report, budget, or cost alert. One tool for all four kinds — pass resourceKind, the object id, and the complete intended grants array. Omitted principals lose access. Roles are READER, EDITOR, or ADMIN. ORGANIZATION may be Reader or Editor, never Admin. CLERK_ADMIN may use any role and applies only to Clerk organization admins; omit it for No access. Every replacement must retain at least one USER, TEAM, or CLERK_ADMIN Admin grant, so empty grants[] is invalid. Call get_object_permissions first, then send the full list including grants you want to keep. New creates start as creator-only Admin and do not include CLERK_ADMIN. Do not use PRIVATE/PUBLIC or teamId on create/update tools. Resolve people with list_users (userId) and teams with list_teams (teamId) — both list the full organization, not only membership.
EXAMPLES:
• "Share the AWS dashboard read-only with the whole org" (keep yourself as Admin) → { resourceKind: "dashboard", resourceId: "clx9aws", grants: [{ principalType: "USER", userId: "", role: "ADMIN" }, { principalType: "ORGANIZATION", role: "READER" }] }
• "Give the infra team Editor on this budget" (keep yourself as Admin) → { resourceKind: "budget", resourceId: "clx9bud", grants: [{ principalType: "USER", userId: "", role: "ADMIN" }, { principalType: "TEAM", teamId: "team_xyz", role: "EDITOR" }] }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| grants | Yes | Full replacement of direct grants. Omitted principals lose access. Every object must retain a USER, TEAM, or CLERK_ADMIN Admin grant. Each principal may appear once. | |
| resourceId | Yes | Object id from search, get, or create. | |
| resourceKind | Yes | dashboard, report, budget, or costAlert. Budget alerts use the parent budget. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses the real side effects and invariants: omitted principals lose access, empty grants[] is invalid because an Admin grant must remain, and ORGANIZATION/CLERK_ADMIN role restrictions. It also explains the default state for newly created objects (creator-only Admin, no CLERK_ADMIN) and warns about PRIVATE/PUBLIC/teamId misuse. This is materially more than annotation hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description front-loads the core action and scope, then layers constraints, workflow, and examples in a logical order. It is long, but every sentence carries a distinct rule or procedure, and the two examples make the complex grant array concrete.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a write tool with no output schema, the description covers everything required to invoke it correctly: complete grant semantics, required invariants, prerequisite calls, principal resolution, and example payloads. The only omitted item is the return value, which is not needed for a set-permissions operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema already covers the parameters (100%), the description adds meaning the schema does not: 'full replacement', 'omitted principals lose access', role rules per principal type, and exact examples with principalType/userId/teamId/role combinations. It also explains how to resolve principals with list_users/list_teams. This transforms the grant array from a generic array into a guided operation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence names a specific action ('Replace the full grant list') and a bounded resource set ('dashboard, report, budget, or cost alert'), so the agent immediately knows what the tool does and that it spans four object kinds. It also references get_object_permissions, making the read/write pairing clear without requiring the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear workflow: call get_object_permissions first, then send the full grants list, and resolve principals via list_users/list_teams. It does not, however, contain an explicit 'when not to use' or a direct alternative-tool selection, apart from the prerequisite read tool, so it falls just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
suggest_actionsARead-onlyInspect
Suggests interesting actions the user can perform next based on the current investigation context (e.g. save a view, set up an alert, share to Slack, drill down, compare periods). Call this after investigation flows (after query or get) to present follow-up options to the user. EXAMPLES: • Standard post-query call → { hasEvents: false, hasDiff: false } • When events were found and a period comparison was already run → { hasEvents: true, hasDiff: true }
| Name | Required | Description | Default |
|---|---|---|---|
| hasDiff | No | Whether query with compare was already called in this flow | |
| hasEvents | No | Whether list_events returned any matching events |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful context by showing how the input flags (hasEvents, hasDiff) affect the suggestions, using examples. It also implies that the tool is context-dependent, which is beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately concise and well-structured. The first sentence states the purpose, the second gives usage timing, and examples are presented in a clear bullet list. Every sentence contributes information without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple tool with two boolean parameters and no output schema, the description covers purpose, usage timing, and parameter semantics. A minor gap is that it does not explicitly describe the return format (e.g., list of action objects), but the examples and phrase 'present follow-up options' make it sufficiently clear. Overall, it is complete enough for an agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description coverage is 100%, giving baseline 3. The description adds semantic value by providing concrete example parameter combinations (e.g., { hasEvents: false, hasDiff: false } for standard post-query, { hasEvents: true, hasDiff: true } for events+difference), which clarifies how the flags map to real investigation flows.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool 'Suggests interesting actions the user can perform next' with specific examples (save a view, set up an alert, share to Slack, drill down, compare periods). It also distinguishes itself from siblings like suggest_groupby and suggest_usage_metrics by focusing on follow-up actions rather than metrics or group-by suggestions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Call this after investigation flows (after query or get) to present follow-up options to the user.' It gives concrete usage scenarios with the parameter examples, but does not explicitly mention when not to use it or contrast with alternatives, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
suggest_groupbyARead-onlyInspect
Suggest the best dimensions to group costs by, based on an optional filterCel (CEL). Call this when the user asks "what should I look at?" or when you need columns for query or find_cost_change_factors. Prefer datePreset over from/to. Omit compare for current-window ranking. Pass compare ({} auto-derives, or { from, to }) when the user asks what changed — then pass 2–4 of the returned column names (max 8; { column, contains } for token columns, dropping metrics) into find_cost_change_factors. Returns { column } or { column, contains } plus metrics; do not invent columns. filterCel omitted or "" is unfiltered (not AWS-only). filterCel supports == null for unlabelled dimension values (e.g. cos_environment == null).
EXAMPLES:
• "What should I split last month's EC2 cost by?" → { datePreset: "LAST_MONTH", filterCel: "cos_service_name in ["AmazonEC2"]" }
• "Costs of EC2 spiked last month, what should I investigate?" → { datePreset: "LAST_MONTH", compare: {}, filterCel: "cos_service_name in ["AmazonEC2"]" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Current period end (YYYY-MM-DD), inclusive. Omit when using datePreset. | |
| from | No | Current period start (YYYY-MM-DD). Omit when using datePreset. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| compare | No | Optional reference (previous) period. Omit for current-window ranking. `{}` auto-derives from the current window (same helper as query). `{ from, to }` pins it. Prefer passing this when the user asks what changed, before find_cost_change_factors. | |
| filterCel | No | Optional CEL scope. Omit or "" for unfiltered (Billy where_clause TRUE). That is not an AWS-only filter even though columns_where_clause falls back to ["cos_provider"]. | |
| datePreset | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already cover read-only and non-destructive behavior. The description adds meaningful behavioral context: return shape ({ column } or { column, contains } plus metrics), a 'do not invent columns' constraint, clarification that an omitted filter is unfiltered and not AWS-only, and support for == null on unlabelled dimension values. This is substantial beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but dense and front-loaded with the core purpose and usage trigger. It earns most of its length with concrete examples and downstream routing, but some compare/filterCel guidance is duplicated from the schema descriptions and could be tightened.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with six parameters, one nested object, and no output schema, the description is remarkably complete: it covers parameter selection, return shape, filter semantics, examples, and how the result feeds into find_cost_change_factors. An agent has enough context to call it correctly without further inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is high at 83%, but the description still adds value beyond it: it explains when to pass compare, what {} auto-derives, why to prefer datePreset over from/to, and what filterCel null/unfiltered means. The examples further clarify realistic parameter combinations, especially the CEL syntax for service filtering.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Suggest the best dimensions to group costs by.' It distinguishes itself from siblings by explicitly tying its output to query and find_cost_change_factors, and by framing the tool as the one to call when the user asks 'what should I look at?'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit trigger conditions: 'Call this when the user asks "what should I look at?" or when you need columns for query or find_cost_change_factors.' It also provides conditional guidance for compare, datePreset vs from/to, and downstream use, so an agent knows exactly when and how to invoke it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
suggest_usage_metricsARead-onlyInspect
Suggest usage metric units (e.g. k8s_cpu_hours, network_bytes_out) to analyze in the current billing context. Only use when a specific filterCel scope is provided; broad queries will return unhelpful results. filterCel supports == null for unlabelled dimension values (e.g. cos_environment == null). EXAMPLE: "What usage metrics make sense for our GCP compute spend?" → { filterCel: "cos_provider in ["GCP"]" }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| filterCel | No | Optional CEL filter to scope which rows are considered when discovering usage units. Same syntax as the explorer filter. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the tool is known to be safe. The description adds behavioral context: the warning that broad queries return unhelpful results and the detail that filterCel supports == null for unlabelled dimension values, which goes beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact: two sentences plus an example. It is front-loaded with the core purpose, followed by usage guidance and a clarifying example. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has no output schema, but the description covers what it does, when to use it, and includes an example. It does not detail the return format, but for a suggestion tool with no output schema this is acceptable. The annotations and sibling context provide enough completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds meaningful value by providing an example filterCel value ("cos_provider in [\"GCP\"]") and explaining the == null syntax for unlabelled dimension values. This enriches the parameter semantics beyond the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'Suggest' and resource 'usage metric units' with concrete examples (k8s_cpu_hours, network_bytes_out) to analyze in the current billing context. It clearly differentiates from sibling tools like suggest_actions or suggest_groupby by focusing on metric units in billing context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use ('Only use when a specific filterCel scope is provided') and warns against broad queries ('broad queries will return unhelpful results'). It does not name alternatives, but the condition is clear and actionable, with an example.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
transfer_report_executionAInspect
Side effect: sends real messages now. Re-delivers an already-rendered successful execution without recomputing widgets. Destinations use the same shape as create_report: { destinationType: "SLACK" | "TEAMS", channelId } or { destinationType: "EMAIL", email }. Resolve channel ids with list_available_destinations. DANGER ZONE: list every target destination and ask the user to confirm before calling.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| executionId | Yes | Report execution id to re-deliver. | |
| destinations | Yes | Same destination shape as create (1–10 targets). Resolve channels via `list_available_destinations`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint=false, destructiveHint=false), the description explicitly states 'Side effect: sends real messages now' and includes a DANGER ZONE warning to list and confirm destinations. This adds crucial behavioral context about real-world impact and safety, going well beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise and front-loaded, leading with the side-effect warning, then the core action, destination format, and a clear safety directive. Every sentence contributes operational value without waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's mutation nature and no output schema, the description is complete: it covers the action, side effect, destination format, a necessary prerequisite (list_available_destinations), and a mandatory user confirmation step. It leaves no significant gaps for safe invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already has 100% coverage with detailed descriptions for all parameters. The description adds complementary value by noting the destination shape matches create_report and that channel ids must be resolved via list_available_destinations, reinforcing the schema's explainer and providing cross-tool guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Re-delivers an already-rendered successful execution without recomputing widgets.' This is a specific verb+resource pair that distinguishes it from sibling tools like retry_report_execution or run_report_now. The mention of 'sends real messages' also clarifies the real-world side effect, leaving no ambiguity about its purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives solid usage context: it is for re-delivering successful executions, references the destination shape from create_report, and instructs resolving channel ids via list_available_destinations. However, it does not explicitly mention alternatives or when not to use it, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_alertAInspect
Update an existing cost alert. Look up alertId via search / list_alerts / get / create_alert. All fields except alertId (and slug) are optional — omit a field to keep the stored value. Most edits (name, description, tagIds, condition, dedup) need nothing else. tagIds replaces the full list: existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created. Omit keeps stored tags; [] clears them. Explorer (queries + period) and notification dest are wholesale groups: if you send any field in the group, send the full group, written from scratch exactly as for create_alert — the stored queries from get are not a valid input. Inside the explorer group, omit scopeId to keep the alert's saved scope (send null to clear it); it cannot be sent on its own. scopeId is the same id as list_teams, stored on the alert (read it from get) and not merged into the stored queries. Do not send aggBy: the stored grain is always Day. Setting condition on a legacy threshold alert that has no dedup config succeeds but the response may include warning: pass dedup too (or in a follow-up call) to avoid notifying on every firing evaluation. Sharing is not this tool — use get_object_permissions then set_object_permissions with resourceKind "costAlert".
EXAMPLE: rename → { alertId, name: "New name" }
EXAMPLE: dest switch to Slack → { alertId, notificationChannel: "SLACK", slackChannelId: "C01ABC" }
EXAMPLE: condition change → { alertId, condition: "a > 2000" }
EXAMPLE: narrow the monitored series (scope kept) → { alertId, queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_provider in ["AWS"]" }], datePreset: "TRAILING_90_DAYS" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Explicit window end (inclusive, YYYY-MM-DD). Use with from instead of datePreset. | |
| from | No | Explicit window start (YYYY-MM-DD). Use with to instead of datePreset. | |
| name | No | Display name for the alert. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| dedup | No | Deduplication config controlling how often a still-firing group notifies. The window is per groupBy value; delivery stays one message listing newly eligible groups. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days. | |
| emails | No | Email addresses (required if EMAIL) | |
| tagIds | No | Replaces the full tag list. Omit to keep stored tags; pass [] to clear. Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). | |
| alertId | Yes | Alert ID from search, list_alerts, get, or create_alert. | |
| queries | No | Replaces the monitored series wholesale, and `condition` references these `name`s. Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. Write them from scratch: the stored `queries` returned by the `get` tool are a different, read-only shape. | |
| scopeId | No | Team scope id (list_teams), stored on the alert, not merged into query filters. A change still needs full queries plus period. Omit (with queries+period) to keep the saved scope; null clears it. Do not send scopeId alone. | |
| condition | No | Alerts v3 firing rule: a single boolean expression over the query names (`name` field of each query). Supports arithmetic (+ - * /), comparisons (> >= < <= == !=), logical and/or/not, parentheses, and these window functions: rollingSum(a, N, UNIT) (trailing sum over the last N units, UNIT ∈ DAY|WEEK|MONTH, inclusive of today), weekToDateSum(a) (Monday-to-date), monthToDateSum(a) (1st-of-month-to-date), and timeShift(a, N, UNIT) (value shifted back N units; may wrap a window function). Examples: `a > 1000`, `rollingSum(a, 7, DAY) > 1000`, `(a - timeShift(a, 1, DAY)) / timeShift(a, 1, DAY) > 0.2`, `a > 10000 or rollingSum(a, 7, DAY) > 50000`. | |
| datePreset | No | Official date preset (same DatePreset as dashboards/reports, e.g. MTD, LAST_MONTH, TRAILING_30_DAYS). Prefer this over hand-computed from/to when a preset matches. Mutually exclusive with from/to. | |
| description | No | Alert description. | |
| slackChannelId | No | Slack target id (required if SLACK): a channel id (C…) to post to a channel, or a Slack user id (U…) to deliver a direct message to that user. Use list_available_destinations to discover both channels and the signed-in user's DM. | |
| teamsChannelId | No | Teams channel ID (required if TEAMS) | |
| notificationChannel | No | Notification channel |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the safety profile (readOnlyHint=false, destructiveHint=false, openWorldHint=false); the description adds far more — replace-vs-omit semantics for tagIds, wholesale group replacement for queries+period and notification dest, the `[]` clearing sentinel, the legacy-threshold `warning` on condition edits, and the constraint that aggBy must not be sent. That covers destructive/irreversible effects the annotations do not.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core rule in two sentences, then the highest-risk semantics, then examples. Dense and mostly load-bearing, but a few statements (e.g. tagIds behavior) restate the schema verbatim, making it longer than strictly necessary for an agent that already reads the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 16 parameters, one required, no output schema, and heavy cross-field coupling, this is exactly the complexity band where a rich description is required. It covers discovery of alertId, optionality, group replacement, sentinels, the legacy-alert warning, and the out-of-scope sharing workflow — nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds meaning the schema cannot express: group atomicity for explorer/notification fields, omit-vs-null distinctions for scopeId, scopeId being stored separately rather than merged into query filters, and the warning that stored queries from `get` are not valid input. This is genuinely additive, not restatement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Update an existing cost alert') and immediately disambiguates from siblings by naming the lookup tools (search / list_alerts / get / create_alert) that supply alertId. The reader knows this is a partial-update mutation on the cost-alert object, distinct from create_alert and archive_object.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use rules ('omit a field to keep the stored value', 'most edits need nothing else'), explicit when-not ('Sharing is not this tool — use get_object_permissions then set_object_permissions with resourceKind "costAlert"'), and named alternatives for tag and scope discovery (list_tags, list_teams). Four worked examples map intent to payload.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_dashboardAInspect
Update an existing dashboard's shared dashboardContext, widgets, and/or tags. Call get_skill with skillId: "dashboards" first — see skill for inheritance rules. Look up the dashboard id via search. Pass dashboardContext as a partial patch to edit the global filter (conditionsCel), period, groupBy, metricId, currency, or scopeId without recreating the dashboard — omit fields you want to keep; empty conditionsCel clears the filter. The legacy context alias is temporarily accepted but deprecated; never send both. Pass operations to add/replace/remove widgets. Pass tags to replace the full tag list (existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created). Omit keeps stored tags; [] clears. At least one of dashboardContext, operations, or tags is required. Sharing is not an update_dashboard field — use get_object_permissions / set_object_permissions. Response includes inheritedContext so new chart widgets can omit fields matching the dashboard. Chart widgets inherit metricId, groupBy, currency, period, and conditionsCel by default — only pass per-widget overrides. Text widgets: { type: "text", title, textContent }. Do not repeat from/to, datePreset, or groupBy when they match the dashboard context. Set extendDashboardConditions: false only when a chart widget must ignore the dashboard filter. Optional grid fields on add: x/y/w/h (from get). Returns a URL — you MUST include it in your response.
EXAMPLES: see skill dashboards Workflow B (widgets) and Workflow D (context / global filter).
• "Tag the AWS dashboard as infrastructure" (after list_tags returned tag id "tag_abc") → { dashboardId: "clx9aws", tags: ["tag_abc"] }
| Name | Required | Description | Default |
|---|---|---|---|
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tags | No | Complete tag list to set on the dashboard (replaces current tags). Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). Pass [] to clear all tags. Use `get` to read current tags before removing one. Optional; omit to leave tags unchanged. | |
| context | No | Deprecated alias for `dashboardContext`; supported temporarily for backward compatibility. Do not send both fields. | |
| operations | No | List of operations to apply atomically. Each op is "add" (append a new widget), "replace" (overwrite an existing widget in place), or "remove" (delete a widget). For add/replace, widgets inherit dashboard context by default; only pass per-widget overrides. Optional when `dashboardContext` or `tags` is provided. Example: [{"op":"add","widget":{"title":"Monthly cost","queries":[{"type":"cost","name":"a","chartType":"BAR"}],"aggBy":"Month"}}]. | |
| dashboardId | Yes | ID of the DashboardV2 to mutate. Get it from `search` (V2 surface). | |
| dashboardContext | No | Partial patch for dashboard-level shared context (global filter, period, groupBy, metricId, currency, scopeId). Omitted fields keep their current values. Set `conditionsCel` to update/clear the global filter (empty string clears it). When changing period mode, null the unused side (`datePreset` vs `startDate`/`endDate`). Can be used alone or together with `operations` or `tags`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=false, but the description explicitly supports destructive operations: removing widgets via op: "remove", clearing tags with [], and clearing the global filter with an empty conditionsCel. This is a direct contradiction between the stated behavior and the annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and then organized by field, but it is long and repeats some information already present in the very detailed schema. Most sentences carry useful constraints, so it avoids being merely verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite no output schema, the description explains that a URL is returned and must be included, mentions inheritedContext, and covers inheritance defaults, partial patch semantics, and alias deprecation. This is complete for a complex mutation tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter validation not present in the schema, including the requirement that at least one of dashboardContext, operations, or tags be supplied and the instruction never to send both context and dashboardContext.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Update) and resource (dashboard) and enumerates the three patchable areas (dashboardContext, widgets, tags). This immediately distinguishes it from create_dashboard and other siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs the agent to call get_skill first, look up the dashboard id via search, and states the requirement that at least one of dashboardContext, operations, or tags be present. It also routes sharing changes to get_object_permissions / set_object_permissions, naming alternatives rather than leaving them implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_eventAInspect
Update an existing event. Use list_events to find the event id first. All fields except eventId are optional — omit a field to keep the stored value. Metadata is merged into existing values; source is preserved on the event column and should not be supplied. Tags replace the full tag list. Name, date, description, tags, metadata, and scopeId need no widget.
scopeId (same id as list_teams) sets the event scope and leaves the chart unchanged. Omit it to keep the saved scope. null clears it. An id replaces it. If both scopeId and widget.scopeId are sent, scopeId wins.
widget replaces the annotation chart wholesale, or creates one if none exists. If you send widget, send the full chart on the first call, written from scratch exactly as for query: title, queries, and datePreset or from/to. Set chartType on every series (BAR, LINE, AREA, WATERFALL, or TABLE). Omitting it stores LINE and replaces the previous chart type. widget.scopeId alone is invalid. list_events returns chart id and title only, not the stored queries, so do not patch or echo the current chart. Omit widget to leave the chart unchanged. A full widget without widget.scopeId keeps the saved event scope unless top-level scopeId is set. widget.scopeId: null clears the event scope when top-level scopeId is omitted. If the event has multiple charts, pass widgetEventId.
EXAMPLE: "Add a PR link to yesterday's deploy event" → { eventId: "clx9abc", metadata: { link: "https://github.com/acme/app/pull/99" } }
EXAMPLE: "Fix the description on the migration event" → { eventId: "clx9abc", description: "Migrated prod cluster; temporary 2-day cost spike from dual-running nodes." }
EXAMPLE: "Scope the migration event to the platform team" → { eventId: "clx9abc", scopeId: "" }
EXAMPLE: "Clear the event scope" → { eventId: "clx9abc", scopeId: null }
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | New event date (YYYY-MM-DD). | |
| name | No | New event name. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| tags | No | Replace all tags on the event. | |
| labels | No | Deprecated alias for tags. Ignored when tags is sent. | |
| widget | No | Annotation chart for the event. Same shape as the `query` tool (`queries`, `datePreset` or `from`/`to`, `aggBy`, `compare`, `limit`, `scopeId`) plus `title` and optional `description`. Providing a widget creates a visual annotation so the team can see which cost movement the event documents. On create: STRONGLY RECOMMENDED; omit only for truly org-wide events with no cost chart. On update: omit to leave the chart unchanged. If sent, widget is a full replacement (title, queries, and datePreset or from/to required; scopeId alone is invalid). Pass widgetEventId when the event has multiple charts. | |
| eventId | Yes | Event ID from list_events or create_event. | |
| scopeId | No | Event scope id from list_teams. Omit to keep the saved scope. null clears it. Does not change the chart. If widget.scopeId is also sent, this field wins. | |
| category | No | Deprecated. If sent, merged into tags then discarded. | |
| metadata | No | Metadata fields to merge into existing metadata (e.g. link, owner). Existing source is preserved. | |
| description | No | New description. | |
| widgetEventId | No | ID of the widgetEvent to update when the event has multiple annotation charts. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the mutation safety profile (readOnlyHint=false, destructiveHint=false), so the description carries the real behavioral burden and does so: metadata merges while tags replace wholesale, `source` is preserved and must not be supplied, widget replaces the chart entirely rather than patching, and scopeId/widget.scopeId precedence and null semantics are spelled out. It does not mention permissions or error behavior, which keeps it short of 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core verb and field-omission rule, then the two tricky sub-objects, then concrete examples. It is long, but the length is earned by a 12-parameter tool with nested widget semantics. The widget paragraph is dense and partly restates schema rules, which is the only drag on tightness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex mutation with nested objects and no output schema, the description covers prerequisites, partial-update merge-vs-replace semantics, the deprecated fields (labels, category), and the multi-chart addressing case. Nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description genuinely adds meaning beyond the schema: scopeId wins over widget.scopeId, null clears vs omit keeps, 'widget.scopeId alone is invalid', chartType must be set per series, and slug auto-detection is easy to miss. Much of this is corroborated in the schema descriptions, so it is additive rather than unique, landing at 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Update an existing event') and immediately routes the agent to list_events to obtain the required eventId, distinguishing it from create_event in the sibling list. An agent can tell what it does without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly covers when to use it, the required prerequisite (list_events first), optional-field omission semantics, when to send widget vs omit it, and when widgetEventId is needed for multi-chart events. Four worked examples map natural-language requests to argument shapes, covering metadata merge, description edit, scope set, and scope clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_reportAInspect
Apply declarative partial changes to one non-archived report. Call get_skill with skillId: "reports" first when changing DIGEST content, schedule, destinations, or reportContext. Omitted fields are preserved. Pass reportContext as a partial patch to edit the report-wide period, groupBy, metricId, currency, scopeId, or conditionsCel without recreating widgets — omit fields you want to keep; empty conditionsCel clears the filter. The legacy context alias is temporarily accepted but deprecated; never send both. tagIds replaces the full list: existing IDs from list_tags and/or { name, color? } for new tags; color defaults to #6366F1; missing tags are created. Omit keeps stored tags; [] clears them. DANGER ZONE when changing schedule, widget, destinations, resuming to ACTIVE, or using schedule.mode NOW: summarize the merged future configuration and ask the user to confirm before calling. Pure pause plus metadata / reportContext edits can apply immediately. schedule.mode NOW converts to on-demand ACTIVE and sends immediately. Archived restore is forbidden in MCP. destinations replaces the full list (must be non-empty; omit to leave it unchanged). widgets (an array) replaces the report's entire widget list wholesale, with type inside each widget; omit widgets to leave the widgets unchanged. The input field is always widgets (array) — a singular widget key is silently ignored.
EXAMPLE pause: { reportId: "clx…", state: "PAUSED" }
EXAMPLE context patch: { reportId: "clx…", reportContext: { datePreset: "LAST_WEEK", groupBy: "cos_service_name" } }
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional name patch. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| state | No | ACTIVE or PAUSED (MCP-facing status). | |
| tagIds | No | Replaces the full tag list. Omit to keep stored tags; pass [] to clear. Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). | |
| context | No | Deprecated alias for `reportContext`; supported temporarily for backward compatibility. Do not send both fields. | |
| widgets | No | When set, replaces the widget list wholesale; omit to leave unchanged. Same widget objects as `create_report` `widgets[]`. | |
| reportId | Yes | Report id to patch. | |
| schedule | No | Optional schedule replace. NOW / UNSCHEDULED / SCHEDULED (WEEKLY requires `weekday`). | |
| description | No | Optional description patch. | |
| destinations | No | When set, replaces the destination list wholesale; omit to leave unchanged (never empties it). Same destination objects as `create_report` `destinations`. | |
| reportContext | No | Partial patch for report-level shared context (same fields as `create_report` `reportContext`). Omitted fields keep their current values. Empty conditionsCel clears the filter. Can be used alone or together with `widgets`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnlyHint=false, destructiveHint=false, openWorldHint=true. The description adds critical traits: replacement semantics for tagIds/destinations/widgets (full overwrite, omit to keep), schedule.mode NOW firing immediately, silent-ignore of a singular `widget` key, deprecated `context` alias, and danger-zone confirmation requirements — all beyond structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long and dense, but the length is proportionate to an 11-param mutation tool with nested widget/destination arrays. Purpose and the get_skill prerequisite are front-loaded; a few clauses (e.g. tagIds color default) drift toward restating schema detail but remain useful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 11 parameters, nested objects, and no output schema, the description covers the full operational envelope an agent needs: prerequisites, confirmation gates, replacement vs merge semantics, and the two worked examples. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds genuinely new meaning: empty conditionsCel clears the filter, tagIds/destinations replace wholesale, the widgets-vs-widget singular trap, and the deprecated context alias conflict. Only the parameter-level detail could be marginally tighter, but it clearly exceeds the schema's own text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and scope: 'Apply declarative partial changes to one non-archived report.' This distinguishes it from create_report (creation), archive_report (archival), and get/list siblings at a glance.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: 'Call get_skill with skillId: "reports" first when changing DIGEST content, schedule, destinations, or reportContext.' It also names what can apply immediately (pure pause plus metadata/reportContext edits) vs what requires user confirmation, and flags archived restore as forbidden.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_virtual_dimension_draftAInspect
Upsert the virtual dimension draft for an existing virtualDimensionId — updates a pending draft or seeds one from published state when none exists (response may include warning: "Initialized draft from published state"). virtualDimensionId in inputs equals id from list/get/search. Use create_virtual_dimension_draft only for brand-new VDIMs. Call get_skill with skillId: "virtual-dimensions" first — see skill for allocation shapes and workflow. For a new telemetry allocation, discover a live integration metric via list_metrics with includeExternal: true and a specific search term, then persist inline externalMetric { provider, integrationId, metricName, aggregator, groupByFields }. For BigQuery/S3, also set dateColumn and metricColumn from the returned schema (first DATE / first NUMERIC) and gapFillingMethod (default FORWARD_FILL). Catalog-complete providers (Tsuga, Datadog, CloudWatch, Amplitude, Google Cloud Monitoring) do not need those fields. Do not set datasource on new reallocations (echo a leftover stored datasource unchanged unless the user asks to switch). bqName is immutable; use returned bqName for groupBy/filterCel in query. Declarative: rules is optional — omit to change only metadata (name, description, tagNames); when provided, fully replaces the rules array. Omitting rules on a published-only VDIM still persists a draft (metadata-only promotion). Carry forward each existing rule's id and allocation from get; ids are sticky — reorder by moving id with its rule. Do not include leftover in rules. Rejects invalid payloads — nothing persisted unless validation passes. On success: values, draftValidation: { ok: true }, draftPersisted: true. Does not publish or refresh BigQuery. Clerk MCP only.
EXAMPLE: "Add staging to Environment VDIM" — rules is the full desired set → { virtualDimensionId: "<id from create/list>", rules: [{ id: "", name: "Production", conditionCel: "cos_environment in ["prod"]", allocation: { allocationType: "dimensionValue", dimensionValue: "prod" } }, { name: "Staging", conditionCel: "cos_environment in ["staging"]", allocation: { allocationType: "dimensionValue", dimensionValue: "staging" } }] }
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Optional rename for the draft. | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| rules | No | Full desired ordered rules array (declarative, not diffs). Omit to keep the current draft rules unchanged and edit only name/description/tags. Omitting rules on a published-only VDIM still persists a draft (metadata-only promotion path). | |
| tagNames | No | Replace tags when provided (created if missing) | |
| description | No | Optional draft description patch. | |
| virtualDimensionId | Yes | Virtual dimension id (stable public identifier) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses critical runtime behavior: validation failure prevents persistence, success returns values/draftValidation/draftPersisted, rules omission creates metadata-only drafts, rule ids are sticky, and bqName is immutable. It also explicitly states it does not publish or refresh BigQuery.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and contains a useful example, but it is very long and has notable redundancy—BigQuery/S3 requirements and datasource carry-forward guidance are repeated. Tighter organization would improve readability without losing critical details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex write operation with no output schema, the description covers prerequisites, workflow, validation behavior, success indicators, rule-id handling, allocation semantics, and a practical example. No major calling context appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even with full schema coverage, the description adds essential semantics: rules is a full declarative replacement, omission means metadata-only, allocation types are detailed, BigQuery/S3 require dateColumn/metricColumn/gapFillingMethod, mappingType behaviors are explained, and datasource should be carried forward unchanged. This goes well beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly identifies the tool as an update/upsert for an existing virtual dimension draft, and explicitly contrasts it with create_virtual_dimension_draft for brand-new VDIMs. The verb and resource are specific and it is easy to distinguish from related sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance, including using create_virtual_dimension_draft only for brand-new VDIMs, calling get_skill first, using list_metrics for externalMetric discovery, and noting that this tool does not publish or refresh BigQuery. Includes a concrete example and clarifies declarative replacement behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
virtual_dimension_overlap_matrixARead-onlyInspect
Compute a lower-triangular overlap matrix for ordered virtual dimension rules (read-only). virtualDimensionId in inputs equals id from list/get/search. When includeDraft is true (default), operates on the latest pending draft if one exists (draftPersisted: true); otherwise analyzes published rules in memory without creating a draft (draftPersisted: false — not publishable). Set includeDraft false to force published order regardless. Trailing 30 days when from/to are omitted (optional from/to override the window). Returns overlaps for all named rules (excludes leftover). position (from get) and ruleIndex (here) refer to the same 0-based ordinal; ordered rules are [...rules, leftover] with leftover at index rules.length (excluded from this matrix). Returns overlaps[{ ruleId, ruleTargetId, ruleIndex, ruleTargetIndex, overlapCost }] where ruleTargetIndex <= ruleIndex. Diagonal entries (ruleId = ruleTargetId) are each rule's raw membership cost; off-diagonal entries are shared spend shadowed by the earlier rule. costMetric selects the cost column (default cost); valid ids: cost, effective_cost, list_cost, contracted_cost (also accepts contracted_costs), unblended_cost, net_unblended_cost, amortized_cost, net_amortized_cost.
EXAMPLE: "Show rule overlap for Environment VDIM" → { virtualDimensionId: "<virtualDimensionId from create/list>" }
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Optional window end (YYYY-MM-DD); must be set together with `from` | |
| from | No | Optional window start (YYYY-MM-DD); must be set together with `to` | |
| slug | No | Organization slug. Omit to auto-detect from your account (fails if you belong to multiple orgs). | |
| costMetric | No | Cost column id (default cost). Valid: cost, effective_cost, list_cost, contracted_cost, unblended_cost, net_unblended_cost, amortized_cost, net_amortized_cost (also accepts contracted_costs → contracted_cost). | |
| includeDraft | No | Use latest draft rules when true; published rules when false | |
| virtualDimensionId | Yes | Virtual dimension id (stable public identifier) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description richly discloses behavioral details beyond annotations: draft handling with draftPersisted flag, in-memory analysis without creating a draft, trailing 30-day default window, exclusion of leftover rules, index mapping (position vs ruleIndex), and matrix diagonal/off-diagonal semantics. This goes far beyond the readOnlyHint annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Despite being lengthy, the description is front-loaded with the core purpose and structured logically. Every sentence adds critical information (draft behavior, return format, examples, metric options) without redundancy. The example at the end aids comprehension without bloating the text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description fully specifies the return structure (overlaps array with ruleId, ruleTargetId, ruleIndex, ruleTargetIndex, overlapCost) and explains diagonal/off-diagonal semantics. It also covers default time window and costMetric options, making the tool's behavior completely understandable for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Adds substantial meaning beyond schema: correlates virtualDimensionId with 'id' from list/get/search, explains includeDraft's effect on draft persistence, clarifies that from/to default to trailing 30 days, and details the costMetric valid values (including alias). Every parameter's purpose is enriched.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function with a specific verb and resource: 'Compute a lower-triangular overlap matrix for ordered virtual dimension rules.' It also clarifies the read-only nature and includes an example, distinguishing it from sibling tools that manage or retrieve virtual dimensions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context on when to use the tool (computing overlap matrices) and explains parameter behavior like includeDraft and time window defaults. However, it does not explicitly name alternatives or state when not to use this tool, though the unique purpose makes alternatives obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
- Added
archive_object - Changed
create_alert1 field changed- changed
Input schema / properties / scopeId / descriptionPrevious value: -"Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries."New value: +"Team scope id (list_teams). Stored on the alert, not merged into query filters. Omit for no scope."
- Changed
create_event1 field changed- changed
Input schema / properties / widget / descriptionPrevious value: -"Annotation chart for the event. Same shape as the `query` tool (`queries`, `datePreset` or `from`/`to`, `aggBy`, `compare`, `limit`, `scopeId`) plus `title` and optional `description`. Providing a widget creates a visual annotation so the team can see which cost movement the event documents. On create: STRONGLY RECOMMENDED; omit only for truly org-wide events with no cost chart. On update: omit to leave existing annotation charts unchanged; pass widgetEventId when the event has multiple charts."New value: +"Annotation chart for the event. Same shape as the `query` tool (`queries`, `datePreset` or `from`/`to`, `aggBy`, `compare`, `limit`, `scopeId`) plus `title` and optional `description`. Providing a widget creates a visual annotation so the team can see which cost movement the event documents. On create: STRONGLY RECOMMENDED; omit only for truly org-wide events with no cost chart. On update: omit to leave the chart unchanged. If sent, widget is a full replacement (title, queries, and datePreset or from/to required; scopeId alone is invalid). Pass widgetEventId when the event has multiple charts."
- Changed
update_alert1 field changed- changed
Input schema / properties / scopeId / descriptionPrevious value: -"Saved team scope id (from list_teams). Changing it still requires complete queries plus period. Omit (with queries+period) to keep the saved scope; send null to clear. Do not send scopeId alone."New value: +"Team scope id (list_teams), stored on the alert, not merged into query filters. A change still needs full queries plus period. Omit (with queries+period) to keep the saved scope; null clears it. Do not send scopeId alone."
- Changed
update_event2 fields changed- added
Input schema / properties / scopeIdAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Event scope id from list_teams. Omit to keep the saved scope. null clears it. Does not change the chart. If widget.scopeId is also sent, this field wins." +} - changed
Input schema / properties / widget / descriptionPrevious value: -"Annotation chart for the event. Same shape as the `query` tool (`queries`, `datePreset` or `from`/`to`, `aggBy`, `compare`, `limit`, `scopeId`) plus `title` and optional `description`. Providing a widget creates a visual annotation so the team can see which cost movement the event documents. On create: STRONGLY RECOMMENDED; omit only for truly org-wide events with no cost chart. On update: omit to leave existing annotation charts unchanged; pass widgetEventId when the event has multiple charts."New value: +"Annotation chart for the event. Same shape as the `query` tool (`queries`, `datePreset` or `from`/`to`, `aggBy`, `compare`, `limit`, `scopeId`) plus `title` and optional `description`. Providing a widget creates a visual annotation so the team can see which cost movement the event documents. On create: STRONGLY RECOMMENDED; omit only for truly org-wide events with no cost chart. On update: omit to leave the chart unchanged. If sent, widget is a full replacement (title, queries, and datePreset or from/to required; scopeId alone is invalid). Pass widgetEventId when the event has multiple charts."
5 tool updates
- Changed
create_alert1 field changed- added
Input schema / properties / tagIdsAdded value: +{ + "description": "`list_tags` id or `{ name, color? }` (created if missing, default #6366F1). Omit and [] leave it untagged.", + "items": { + "anyOf": [ + { + "description": "Existing tag ID", + "minLength": 1, + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "default": "#6366F1", + "description": "Hex color for the new tag (defaults to #6366F1)", + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + ] + }, + "type": "array" +}
- Changed
create_dashboard1 field changed- added
Input schema / properties / tagsAdded value: +{ + "description": "Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). Omit and [] both leave the dashboard untagged.", + "items": { + "anyOf": [ + { + "description": "Existing tag ID", + "minLength": 1, + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "default": "#6366F1", + "description": "Hex color for the new tag (defaults to #6366F1)", + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + ] + }, + "type": "array" +}
- Changed
create_report2 fields changed- changed
Input schema / properties / schedule / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "mode": { - "const": "NOW", - "type": "string" - } - }, - "required": [ - "mode" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "mode": { - "const": "UNSCHEDULED", - "type": "string" - } - }, - "required": [ - "mode" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "firstRunAt": { - "description": "First send time as ISO-8601 UTC datetime (e.g. 2026-07-16T10:00:00.000Z)", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", - "type": "string" - }, - "mode": { - "const": "SCHEDULED", - "type": "string" - }, - "period": { - "description": "Recurrence: DAILY, WEEKDAYS, WEEKLY, or MONTHLY. WEEKLY requires `weekday`.", - "enum": [ - "DAILY", - "WEEKDAYS", - "WEEKLY", - "MONTHLY" - ], - "type": "string" - }, - "weekday": { - "description": "Day of week for WEEKLY: 0 = Sunday … 6 = Saturday. Required when period is WEEKLY (Zod refine; may appear optional in JSON Schema).", - "maximum": 6, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "mode", - "period" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "mode": { + "const": "NOW", + "type": "string" + } + }, + "required": [ + "mode" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "mode": { + "const": "UNSCHEDULED", + "type": "string" + } + }, + "required": [ + "mode" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "firstRunAt": { + "description": "First send date as ISO-8601 UTC datetime (e.g. 2026-07-16T00:00:00.000Z). The time of day is ignored; the report is due for that whole UTC day.", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", + "type": "string" + }, + "mode": { + "const": "SCHEDULED", + "type": "string" + }, + "period": { + "description": "Recurrence: DAILY, WEEKDAYS, WEEKLY, or MONTHLY. WEEKLY requires `weekday`.", + "enum": [ + "DAILY", + "WEEKDAYS", + "WEEKLY", + "MONTHLY" + ], + "type": "string" + }, + "weekday": { + "description": "Day of week for WEEKLY: 0 = Sunday … 6 = Saturday. Required when period is WEEKLY (Zod refine; may appear optional in JSON Schema).", + "maximum": 6, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "mode", + "period" + ], + "type": "object" + } +] - added
Input schema / properties / tagIdsAdded value: +{ + "description": "Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1). Omit and [] both leave the report untagged.", + "items": { + "anyOf": [ + { + "description": "Existing tag ID", + "minLength": 1, + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "default": "#6366F1", + "description": "Hex color for the new tag (defaults to #6366F1)", + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + ] + }, + "type": "array" +}
- Added
update_alert - Changed
update_report2 fields changed- changed
Input schema / properties / schedule / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "mode": { - "const": "NOW", - "type": "string" - } - }, - "required": [ - "mode" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "mode": { - "const": "UNSCHEDULED", - "type": "string" - } - }, - "required": [ - "mode" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "firstRunAt": { - "anyOf": [ - { - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", - "type": "string" - }, - { - "type": "null" - } - ], - "description": "First send time as ISO-8601 UTC datetime, or null to clear" - }, - "mode": { - "const": "SCHEDULED", - "type": "string" - }, - "period": { - "description": "Recurrence: DAILY, WEEKDAYS, WEEKLY, or MONTHLY. WEEKLY requires `weekday`.", - "enum": [ - "DAILY", - "WEEKDAYS", - "WEEKLY", - "MONTHLY" - ], - "type": "string" - }, - "weekday": { - "description": "Day of week for WEEKLY: 0 = Sunday … 6 = Saturday. Required when period is WEEKLY.", - "maximum": 6, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "mode", - "period" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "mode": { + "const": "NOW", + "type": "string" + } + }, + "required": [ + "mode" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "mode": { + "const": "UNSCHEDULED", + "type": "string" + } + }, + "required": [ + "mode" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "firstRunAt": { + "anyOf": [ + { + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$", + "type": "string" + }, + { + "type": "null" + } + ], + "description": "First send date as ISO-8601 UTC datetime, or null to clear. The time of day is ignored." + }, + "mode": { + "const": "SCHEDULED", + "type": "string" + }, + "period": { + "description": "Recurrence: DAILY, WEEKDAYS, WEEKLY, or MONTHLY. WEEKLY requires `weekday`.", + "enum": [ + "DAILY", + "WEEKDAYS", + "WEEKLY", + "MONTHLY" + ], + "type": "string" + }, + "weekday": { + "description": "Day of week for WEEKLY: 0 = Sunday … 6 = Saturday. Required when period is WEEKLY.", + "maximum": 6, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "mode", + "period" + ], + "type": "object" + } +] - added
Input schema / properties / tagIdsAdded value: +{ + "description": "Replaces the full tag list. Omit to keep stored tags; pass [] to clear. Pass existing tag IDs from `list_tags`, and/or new tag objects `{ name, color? }` (created in the org if missing; color defaults to #6366F1).", + "items": { + "anyOf": [ + { + "description": "Existing tag ID", + "minLength": 1, + "type": "string" + }, + { + "additionalProperties": false, + "properties": { + "color": { + "default": "#6366F1", + "description": "Hex color for the new tag (defaults to #6366F1)", + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + ] + }, + "type": "array" +}
1 tool update
- Changed
query1 field changed- changed
Input schema / properties / queries / items / oneOfPrevious value: -[ - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "currency": { - "default": "USD", - "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", - "enum": [ - "USD", - "EUR", - "GBP", - "CNY" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "default": "cost", - "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", - "enum": [ - "cost", - "effective_cost", - "list_cost", - "contracted_cost", - "unblended_cost", - "net_unblended_cost", - "amortized_cost", - "net_amortized_cost" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "cost", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "metric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "usage", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "formula": { - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "formula", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "formula", - "name" - ], - "type": "object" - }, - { - "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "budgetId": { - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "budget", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - }, - "virtualDimensionValue": { - "type": "string" - } - }, - "required": [ - "type", - "budgetId", - "name" - ], - "type": "object" - }, - { - "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "provider": { - "default": "tsuga", - "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake", - "cloudmonitoring", - "push" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "externalMetric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "integrationId", - "name" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "currency": { + "default": "USD", + "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", + "enum": [ + "USD", + "EUR", + "GBP", + "CNY" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "default": "cost", + "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", + "enum": [ + "cost", + "effective_cost", + "list_cost", + "contracted_cost", + "unblended_cost", + "net_unblended_cost", + "amortized_cost", + "net_amortized_cost" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "cost", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "metric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "usage", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "formula": { + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "formula", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "formula", + "name" + ], + "type": "object" + }, + { + "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "budgetId": { + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "budget", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + }, + "virtualDimensionValue": { + "type": "string" + } + }, + "required": [ + "type", + "budgetId", + "name" + ], + "type": "object" + }, + { + "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required. For Snowflake: identical to BigQuery — metricName is DATABASE.SCHEMA.TABLE; dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names.", + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "provider": { + "default": "tsuga", + "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring", + "push" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "externalMetric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "integrationId", + "name" + ], + "type": "object" + } +]
10 tool updates
- Changed
create_alert1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
create_dashboard3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / dashboardContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "displayOptions": { - "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", - "properties": { - "hideEmptyRows": { - "description": "Hide rows with abs displayed sum < 0.01", - "type": "boolean" - }, - "precision": { - "description": "Fraction digits 0–6", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "showTotal": { - "description": "Show Total column", - "type": "boolean" - }, - "sort": { - "properties": { - "by": { - "description": "total | cost | groupBy | query letter (a)", - "minLength": 1, - "type": "string" - }, - "direction": { - "default": "DESC", - "description": "ASC | DESC (default DESC)", - "enum": [ - "ASC", - "DESC" - ], - "type": "string" - } - }, - "required": [ - "by" - ], - "type": "object" - }, - "topN": { - "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", - "maximum": 100, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } +]
- Changed
create_report3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / reportContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "datePreset": { - "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface (integer 1–100). Applied at every tree level.", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
find_cost_change_factors1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
preview_report_widget3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / reportContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widget / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "datePreset": { - "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface (integer 1–100). Applied at every tree level.", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
preview_virtual_dimension_draft1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
query1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
suggest_groupby1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
update_dashboard3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / dashboardContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / operations / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "op": { - "const": "add", - "type": "string" - }, - "widget": { - "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "displayOptions": { - "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", - "properties": { - "hideEmptyRows": { - "description": "Hide rows with abs displayed sum < 0.01", - "type": "boolean" - }, - "precision": { - "description": "Fraction digits 0–6", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "showTotal": { - "description": "Show Total column", - "type": "boolean" - }, - "sort": { - "properties": { - "by": { - "description": "total | cost | groupBy | query letter (a)", - "minLength": 1, - "type": "string" - }, - "direction": { - "default": "DESC", - "description": "ASC | DESC (default DESC)", - "enum": [ - "ASC", - "DESC" - ], - "type": "string" - } - }, - "required": [ - "by" - ], - "type": "object" - }, - "topN": { - "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", - "maximum": 100, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } - ] - } - }, - "required": [ - "op" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "replace", - "type": "string" - }, - "widget": { - "additionalProperties": {}, - "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "widgetId": { - "description": "ID of the widget to overwrite (keeps its position).", - "type": "string" - } - }, - "required": [ - "op", - "widgetId", - "widget" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "remove", - "type": "string" - }, - "widgetId": { - "description": "ID of the widget to delete.", - "type": "string" - } - }, - "required": [ - "op", - "widgetId" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "op": { + "const": "add", + "type": "string" + }, + "widget": { + "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } + ] + } + }, + "required": [ + "op" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "replace", + "type": "string" + }, + "widget": { + "additionalProperties": {}, + "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "widgetId": { + "description": "ID of the widget to overwrite (keeps its position).", + "type": "string" + } + }, + "required": [ + "op", + "widgetId", + "widget" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "remove", + "type": "string" + }, + "widgetId": { + "description": "ID of the widget to delete.", + "type": "string" + } + }, + "required": [ + "op", + "widgetId" + ], + "type": "object" + } +]
- Changed
update_report3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / reportContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "datePreset": { - "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "CURRENT_MONTH", - "CURRENT_QUARTER", - "CURRENT_YEAR", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_1_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface (integer 1–100). Applied at every tree level.", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
2 tool updates
- Changed
create_alert1 field changed- changed
Input schema / properties / dedup / descriptionPrevious value: -"Deduplication config controlling how often a still-firing alert notifies. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days."New value: +"Deduplication config controlling how often a still-firing group notifies. The window is per groupBy value; delivery stays one message listing newly eligible groups. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days."
- Changed
preview_alert1 field changed- changed
Input schema / properties / dedup / descriptionPrevious value: -"Deduplication config controlling how often a still-firing alert notifies. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days. Optional here: when provided the result also reports how many notifications would actually be sent after dedup."New value: +"Deduplication config controlling how often a still-firing group notifies. The window is per groupBy value; delivery stays one message listing newly eligible groups. Either CALENDAR (kind: CALENDAR, calendarUnit: WEEK | MONTH) = at most once per current ISO week / calendar month, or ROLLING (kind: ROLLING, windowDays: N) = at most once every N days. Optional here: when provided the result also reports how many notifications would actually be sent after dedup."
10 tool updates
- Changed
create_dashboard2 fields changed- removed
Input schema / properties / teamIdRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional owning team id. Independent of visibility; omit or null for no team." -} - removed
Input schema / properties / visibilityRemoved value: -{ - "default": "PRIVATE", - "description": "PRIVATE (default), PUBLIC, or TEMPLATE. Set `teamId` to share with a team — there is no TEAM visibility value.", - "enum": [ - "PRIVATE", - "PUBLIC", - "TEMPLATE" - ], - "type": "string" -}
- Changed
create_event4 fields changed- removed
Input schema / properties / category / defaultRemoved value: -"TECHNICAL" - changed
Input schema / properties / category / descriptionPrevious value: -"BUSINESS = org/budget change, TECHNICAL = deployment/infra change, PROVIDER = cloud provider update"New value: +"Deprecated. If sent, merged into tags then discarded." - removed
Input schema / properties / category / enumRemoved value: -[ - "BUSINESS", - "TECHNICAL", - "PROVIDER" -] - added
Input schema / properties / labelsAdded value: +{ + "description": "Deprecated alias for tags. Ignored when tags is sent.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
create_report2 fields changed- removed
Input schema / properties / teamIdRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional owning team id. Independent of type/visibility; omit or null for no team." -} - removed
Input schema / properties / visibilityRemoved value: -{ - "default": "PRIVATE", - "description": "PRIVATE (default) or PUBLIC (maps to API `type`). Set `teamId` to share with a team — there is no TEAM visibility value. TEMPLATE is dashboard-only.", - "enum": [ - "PRIVATE", - "PUBLIC" - ], - "type": "string" -}
- Added
get_object_permissions - Changed
list_events1 field changed- removed
Input schema / properties / categoryRemoved value: -{ - "description": "Filter by category. Omit to return all categories.", - "enum": [ - "BUSINESS", - "TECHNICAL", - "PROVIDER" - ], - "type": "string" -}
- Changed
list_teams1 field changed- added
Input schema / properties / queryAdded value: +{ + "description": "Optional team-name search. Omit to list every non-archived team in the organization.", + "type": "string" +}
- Added
list_users - Added
set_object_permissions - Changed
update_dashboard3 fields changed- changed
Input schema / properties / dashboardContext / descriptionPrevious value: -"Partial patch for dashboard-level shared context (global filter, period, groupBy, metricId, currency, scopeId). Omitted fields keep their current values. Set `conditionsCel` to update/clear the global filter (empty string clears it). When changing period mode, null the unused side (`datePreset` vs `startDate`/`endDate`). Can be used alone or together with `operations`, `tags`, or `teamId`."New value: +"Partial patch for dashboard-level shared context (global filter, period, groupBy, metricId, currency, scopeId). Omitted fields keep their current values. Set `conditionsCel` to update/clear the global filter (empty string clears it). When changing period mode, null the unused side (`datePreset` vs `startDate`/`endDate`). Can be used alone or together with `operations` or `tags`." - changed
Input schema / properties / operations / descriptionPrevious value: -"List of operations to apply atomically. Each op is \"add\" (append a new widget), \"replace\" (overwrite an existing widget in place), or \"remove\" (delete a widget). For add/replace, widgets inherit dashboard context by default; only pass per-widget overrides. Optional when `dashboardContext`, `tags`, or `teamId` is provided. Example: [{\"op\":\"add\",\"widget\":{\"title\":\"Monthly cost\",\"queries\":[{\"type\":\"cost\",\"name\":\"a\",\"chartType\":\"BAR\"}],\"aggBy\":\"Month\"}}]."New value: +"List of operations to apply atomically. Each op is \"add\" (append a new widget), \"replace\" (overwrite an existing widget in place), or \"remove\" (delete a widget). For add/replace, widgets inherit dashboard context by default; only pass per-widget overrides. Optional when `dashboardContext` or `tags` is provided. Example: [{\"op\":\"add\",\"widget\":{\"title\":\"Monthly cost\",\"queries\":[{\"type\":\"cost\",\"name\":\"a\",\"chartType\":\"BAR\"}],\"aggBy\":\"Month\"}}]." - removed
Input schema / properties / teamIdRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Team to assign the dashboard to (from `list_teams`), or null to detach from any team. Optional; omit to leave team unchanged." -}
- Changed
update_event4 fields changed- changed
Input schema / properties / category / descriptionPrevious value: -"New category."New value: +"Deprecated. If sent, merged into tags then discarded." - removed
Input schema / properties / category / enumRemoved value: -[ - "BUSINESS", - "TECHNICAL", - "PROVIDER" -] - added
Input schema / properties / labelsAdded value: +{ + "description": "Deprecated alias for tags. Ignored when tags is sent.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Input schema / properties / tags / descriptionPrevious value: -"Replace all labels on the event."New value: +"Replace all tags on the event."
12 tool updates
- Changed
create_alert1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
create_dashboard3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / dashboardContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "displayOptions": { - "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", - "properties": { - "hideEmptyRows": { - "description": "Hide rows with abs displayed sum < 0.01", - "type": "boolean" - }, - "precision": { - "description": "Fraction digits 0–6", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "showTotal": { - "description": "Show Total column", - "type": "boolean" - }, - "sort": { - "properties": { - "by": { - "description": "total | cost | groupBy | query letter (a)", - "minLength": 1, - "type": "string" - }, - "direction": { - "default": "DESC", - "description": "ASC | DESC (default DESC)", - "enum": [ - "ASC", - "DESC" - ], - "type": "string" - } - }, - "required": [ - "by" - ], - "type": "object" - }, - "topN": { - "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", - "maximum": 100, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } +]
- Changed
create_report3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / reportContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "datePreset": { - "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", + "type": "number" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
create_virtual_dimension_draft1 field changed- changed
Input schema / properties / rules / items / properties / allocation / oneOfPrevious value: -[ - { - "properties": { - "allocationType": { - "const": "dimensionValue", - "type": "string" - }, - "dimensionValue": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "dimensionValue" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "existingColumn", - "type": "string" - }, - "existingColumn": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "existingColumn" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "splitCost", - "type": "string" - }, - "reAllocationParams": { - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "properties": { - "partitions": { - "items": { - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "weight": { - "type": "number" - } - }, - "required": [ - "label", - "weight" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "const": "custom", - "type": "string" - } - }, - "required": [ - "type", - "partitions" - ], - "type": "object" - } - ] - } - }, - "required": [ - "allocationType", - "reAllocationParams" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "telemetry", - "type": "string" - }, - "datasource": { - "minLength": 1, - "type": "string" - }, - "externalMetric": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "provider": { - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake", - "cloudmonitoring" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "provider", - "integrationId" - ], - "type": "object" - }, - "mappingParams": { - "properties": { - "mapping": { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "mapping" - ], - "type": "object" - }, - "mappingType": { - "enum": [ - "mapping", - "regexMapping", - "identity" - ], - "type": "string" - }, - "regexTransformation": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "mappingType", - "mappingParams" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "allocationType": { + "const": "dimensionValue", + "type": "string" + }, + "dimensionValue": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "dimensionValue" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "existingColumn", + "type": "string" + }, + "existingColumn": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "existingColumn" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "splitCost", + "type": "string" + }, + "reAllocationParams": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "properties": { + "partitions": { + "items": { + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "label", + "weight" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "custom", + "type": "string" + } + }, + "required": [ + "type", + "partitions" + ], + "type": "object" + } + ] + } + }, + "required": [ + "allocationType", + "reAllocationParams" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "telemetry", + "type": "string" + }, + "datasource": { + "minLength": 1, + "type": "string" + }, + "externalMetric": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "provider": { + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring", + "push" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "provider", + "integrationId" + ], + "type": "object" + }, + "mappingParams": { + "properties": { + "mapping": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "mapping" + ], + "type": "object" + }, + "mappingType": { + "enum": [ + "mapping", + "regexMapping", + "identity" + ], + "type": "string" + }, + "regexTransformation": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "mappingType", + "mappingParams" + ], + "type": "object" + } +]
- Changed
find_cost_change_factors1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
preview_report_widget3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / reportContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widget / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "datePreset": { - "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", + "type": "number" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
preview_virtual_dimension_draft1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
query2 fields changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +] - changed
Input schema / properties / queries / items / oneOfPrevious value: -[ - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "currency": { - "default": "USD", - "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", - "enum": [ - "USD", - "EUR", - "GBP", - "CNY" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "default": "cost", - "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", - "enum": [ - "cost", - "effective_cost", - "list_cost", - "contracted_cost", - "unblended_cost", - "net_unblended_cost", - "amortized_cost", - "net_amortized_cost" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "cost", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "metric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "usage", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "formula": { - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "formula", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "formula", - "name" - ], - "type": "object" - }, - { - "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "budgetId": { - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "budget", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - }, - "virtualDimensionValue": { - "type": "string" - } - }, - "required": [ - "type", - "budgetId", - "name" - ], - "type": "object" - }, - { - "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "provider": { - "default": "tsuga", - "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake", - "cloudmonitoring" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "externalMetric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "integrationId", - "name" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "currency": { + "default": "USD", + "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", + "enum": [ + "USD", + "EUR", + "GBP", + "CNY" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "default": "cost", + "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", + "enum": [ + "cost", + "effective_cost", + "list_cost", + "contracted_cost", + "unblended_cost", + "net_unblended_cost", + "amortized_cost", + "net_amortized_cost" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "cost", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "metric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "usage", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "formula": { + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "formula", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "formula", + "name" + ], + "type": "object" + }, + { + "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "budgetId": { + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "budget", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + }, + "virtualDimensionValue": { + "type": "string" + } + }, + "required": [ + "type", + "budgetId", + "name" + ], + "type": "object" + }, + { + "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "provider": { + "default": "tsuga", + "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring", + "push" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "externalMetric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "integrationId", + "name" + ], + "type": "object" + } +]
- Changed
suggest_groupby1 field changed- changed
Input schema / properties / datePreset / enumPrevious value: -[ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" -]New value: +[ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" +]
- Changed
update_dashboard3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / dashboardContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / operations / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "op": { - "const": "add", - "type": "string" - }, - "widget": { - "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "displayOptions": { - "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", - "properties": { - "hideEmptyRows": { - "description": "Hide rows with abs displayed sum < 0.01", - "type": "boolean" - }, - "precision": { - "description": "Fraction digits 0–6", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "showTotal": { - "description": "Show Total column", - "type": "boolean" - }, - "sort": { - "properties": { - "by": { - "description": "total | cost | groupBy | query letter (a)", - "minLength": 1, - "type": "string" - }, - "direction": { - "default": "DESC", - "description": "ASC | DESC (default DESC)", - "enum": [ - "ASC", - "DESC" - ], - "type": "string" - } - }, - "required": [ - "by" - ], - "type": "object" - }, - "topN": { - "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", - "maximum": 100, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } - ] - } - }, - "required": [ - "op" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "replace", - "type": "string" - }, - "widget": { - "additionalProperties": {}, - "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "widgetId": { - "description": "ID of the widget to overwrite (keeps its position).", - "type": "string" - } - }, - "required": [ - "op", - "widgetId", - "widget" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "remove", - "type": "string" - }, - "widgetId": { - "description": "ID of the widget to delete.", - "type": "string" - } - }, - "required": [ - "op", - "widgetId" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "op": { + "const": "add", + "type": "string" + }, + "widget": { + "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } + ] + } + }, + "required": [ + "op" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "replace", + "type": "string" + }, + "widget": { + "additionalProperties": {}, + "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "widgetId": { + "description": "ID of the widget to overwrite (keeps its position).", + "type": "string" + } + }, + "required": [ + "op", + "widgetId", + "widget" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "remove", + "type": "string" + }, + "widgetId": { + "description": "ID of the widget to delete.", + "type": "string" + } + }, + "required": [ + "op", + "widgetId" + ], + "type": "object" + } +]
- Changed
update_report3 fields changed- changed
Input schema / properties / context / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / reportContext / properties / datePreset / anyOfPrevious value: -[ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } +] - changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "datePreset": { - "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "CURRENT_MONTH", + "CURRENT_QUARTER", + "CURRENT_YEAR", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", + "type": "number" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
update_virtual_dimension_draft1 field changed- changed
Input schema / properties / rules / items / properties / allocation / oneOfPrevious value: -[ - { - "properties": { - "allocationType": { - "const": "dimensionValue", - "type": "string" - }, - "dimensionValue": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "dimensionValue" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "existingColumn", - "type": "string" - }, - "existingColumn": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "existingColumn" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "splitCost", - "type": "string" - }, - "reAllocationParams": { - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "properties": { - "partitions": { - "items": { - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "weight": { - "type": "number" - } - }, - "required": [ - "label", - "weight" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "const": "custom", - "type": "string" - } - }, - "required": [ - "type", - "partitions" - ], - "type": "object" - } - ] - } - }, - "required": [ - "allocationType", - "reAllocationParams" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "telemetry", - "type": "string" - }, - "datasource": { - "minLength": 1, - "type": "string" - }, - "externalMetric": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "provider": { - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake", - "cloudmonitoring" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "provider", - "integrationId" - ], - "type": "object" - }, - "mappingParams": { - "properties": { - "mapping": { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "mapping" - ], - "type": "object" - }, - "mappingType": { - "enum": [ - "mapping", - "regexMapping", - "identity" - ], - "type": "string" - }, - "regexTransformation": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "mappingType", - "mappingParams" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "allocationType": { + "const": "dimensionValue", + "type": "string" + }, + "dimensionValue": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "dimensionValue" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "existingColumn", + "type": "string" + }, + "existingColumn": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "existingColumn" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "splitCost", + "type": "string" + }, + "reAllocationParams": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "properties": { + "partitions": { + "items": { + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "label", + "weight" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "custom", + "type": "string" + } + }, + "required": [ + "type", + "partitions" + ], + "type": "object" + } + ] + } + }, + "required": [ + "allocationType", + "reAllocationParams" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "telemetry", + "type": "string" + }, + "datasource": { + "minLength": 1, + "type": "string" + }, + "externalMetric": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "provider": { + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring", + "push" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "provider", + "integrationId" + ], + "type": "object" + }, + "mappingParams": { + "properties": { + "mapping": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "mapping" + ], + "type": "object" + }, + "mappingType": { + "enum": [ + "mapping", + "regexMapping", + "identity" + ], + "type": "string" + }, + "regexTransformation": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "mappingType", + "mappingParams" + ], + "type": "object" + } +]
6 tool updates
- Changed
create_dashboard1 field changed- changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "displayOptions": { - "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", - "properties": { - "hideEmptyRows": { - "description": "Hide rows with abs displayed sum < 0.01", - "type": "boolean" - }, - "precision": { - "description": "Fraction digits 0–6", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "showTotal": { - "description": "Show Total column", - "type": "boolean" - }, - "sort": { - "properties": { - "by": { - "description": "total | cost | groupBy | query letter (a)", - "minLength": 1, - "type": "string" - }, - "direction": { - "default": "DESC", - "description": "ASC | DESC (default DESC)", - "enum": [ - "ASC", - "DESC" - ], - "type": "string" - } - }, - "required": [ - "by" - ], - "type": "object" - }, - "topN": { - "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", - "maximum": 100, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } +]
- Changed
create_report2 fields changed- changed
Input schema / properties / visibility / descriptionPrevious value: -"PRIVATE (default), PUBLIC, or TEMPLATE (maps to API `type`). Set `teamId` to share with a team — there is no TEAM visibility value."New value: +"PRIVATE (default) or PUBLIC (maps to API `type`). Set `teamId` to share with a team — there is no TEAM visibility value. TEMPLATE is dashboard-only." - changed
Input schema / properties / visibility / enumPrevious value: -[ - "PRIVATE", - "PUBLIC", - "TEMPLATE" -]New value: +[ + "PRIVATE", + "PUBLIC" +]
- Changed
create_virtual_dimension_draft1 field changed- changed
Input schema / properties / rules / items / properties / allocation / oneOfPrevious value: -[ - { - "properties": { - "allocationType": { - "const": "dimensionValue", - "type": "string" - }, - "dimensionValue": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "dimensionValue" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "existingColumn", - "type": "string" - }, - "existingColumn": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "existingColumn" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "splitCost", - "type": "string" - }, - "reAllocationParams": { - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "properties": { - "partitions": { - "items": { - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "weight": { - "type": "number" - } - }, - "required": [ - "label", - "weight" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "const": "custom", - "type": "string" - } - }, - "required": [ - "type", - "partitions" - ], - "type": "object" - } - ] - } - }, - "required": [ - "allocationType", - "reAllocationParams" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "telemetry", - "type": "string" - }, - "datasource": { - "minLength": 1, - "type": "string" - }, - "externalMetric": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "provider": { - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "provider", - "integrationId" - ], - "type": "object" - }, - "mappingParams": { - "properties": { - "mapping": { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "mapping" - ], - "type": "object" - }, - "mappingType": { - "enum": [ - "mapping", - "regexMapping", - "identity" - ], - "type": "string" - }, - "regexTransformation": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "mappingType", - "mappingParams" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "allocationType": { + "const": "dimensionValue", + "type": "string" + }, + "dimensionValue": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "dimensionValue" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "existingColumn", + "type": "string" + }, + "existingColumn": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "existingColumn" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "splitCost", + "type": "string" + }, + "reAllocationParams": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "properties": { + "partitions": { + "items": { + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "label", + "weight" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "custom", + "type": "string" + } + }, + "required": [ + "type", + "partitions" + ], + "type": "object" + } + ] + } + }, + "required": [ + "allocationType", + "reAllocationParams" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "telemetry", + "type": "string" + }, + "datasource": { + "minLength": 1, + "type": "string" + }, + "externalMetric": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "provider": { + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "provider", + "integrationId" + ], + "type": "object" + }, + "mappingParams": { + "properties": { + "mapping": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "mapping" + ], + "type": "object" + }, + "mappingType": { + "enum": [ + "mapping", + "regexMapping", + "identity" + ], + "type": "string" + }, + "regexTransformation": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "mappingType", + "mappingParams" + ], + "type": "object" + } +]
- Changed
query1 field changed- changed
Input schema / properties / queries / items / oneOfPrevious value: -[ - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "currency": { - "default": "USD", - "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", - "enum": [ - "USD", - "EUR", - "GBP", - "CNY" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "default": "cost", - "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", - "enum": [ - "cost", - "effective_cost", - "list_cost", - "contracted_cost", - "unblended_cost", - "net_unblended_cost", - "amortized_cost", - "net_amortized_cost" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "cost", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "metric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "usage", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "formula": { - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "formula", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "formula", - "name" - ], - "type": "object" - }, - { - "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "budgetId": { - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "budget", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - }, - "virtualDimensionValue": { - "type": "string" - } - }, - "required": [ - "type", - "budgetId", - "name" - ], - "type": "object" - }, - { - "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "provider": { - "default": "tsuga", - "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "externalMetric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "integrationId", - "name" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "currency": { + "default": "USD", + "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", + "enum": [ + "USD", + "EUR", + "GBP", + "CNY" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "default": "cost", + "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", + "enum": [ + "cost", + "effective_cost", + "list_cost", + "contracted_cost", + "unblended_cost", + "net_unblended_cost", + "amortized_cost", + "net_amortized_cost" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "cost", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "metric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "usage", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "formula": { + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "formula", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "formula", + "name" + ], + "type": "object" + }, + { + "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "budgetId": { + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "budget", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + }, + "virtualDimensionValue": { + "type": "string" + } + }, + "required": [ + "type", + "budgetId", + "name" + ], + "type": "object" + }, + { + "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "provider": { + "default": "tsuga", + "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "externalMetric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "integrationId", + "name" + ], + "type": "object" + } +]
- Changed
update_dashboard1 field changed- changed
Input schema / properties / operations / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "op": { - "const": "add", - "type": "string" - }, - "widget": { - "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "displayOptions": { - "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", - "properties": { - "hideEmptyRows": { - "description": "Hide rows with abs displayed sum < 0.01", - "type": "boolean" - }, - "precision": { - "description": "Fraction digits 0–6", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "showTotal": { - "description": "Show Total column", - "type": "boolean" - }, - "sort": { - "properties": { - "by": { - "description": "total | cost | groupBy | query letter (a)", - "minLength": 1, - "type": "string" - }, - "direction": { - "default": "DESC", - "description": "ASC | DESC (default DESC)", - "enum": [ - "ASC", - "DESC" - ], - "type": "string" - } - }, - "required": [ - "by" - ], - "type": "object" - }, - "topN": { - "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", - "maximum": 100, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } - ] - } - }, - "required": [ - "op" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "replace", - "type": "string" - }, - "widget": { - "additionalProperties": {}, - "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "widgetId": { - "description": "ID of the widget to overwrite (keeps its position).", - "type": "string" - } - }, - "required": [ - "op", - "widgetId", - "widget" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "remove", - "type": "string" - }, - "widgetId": { - "description": "ID of the widget to delete.", - "type": "string" - } - }, - "required": [ - "op", - "widgetId" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "op": { + "const": "add", + "type": "string" + }, + "widget": { + "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups. On a non-comparison TABLE widget, use displayOptions.topN for the row cap and omit this — the two are mutually exclusive there.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } + ] + } + }, + "required": [ + "op" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "replace", + "type": "string" + }, + "widget": { + "additionalProperties": {}, + "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "widgetId": { + "description": "ID of the widget to overwrite (keeps its position).", + "type": "string" + } + }, + "required": [ + "op", + "widgetId", + "widget" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "remove", + "type": "string" + }, + "widgetId": { + "description": "ID of the widget to delete.", + "type": "string" + } + }, + "required": [ + "op", + "widgetId" + ], + "type": "object" + } +]
- Changed
update_virtual_dimension_draft1 field changed- changed
Input schema / properties / rules / items / properties / allocation / oneOfPrevious value: -[ - { - "properties": { - "allocationType": { - "const": "dimensionValue", - "type": "string" - }, - "dimensionValue": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "dimensionValue" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "existingColumn", - "type": "string" - }, - "existingColumn": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "existingColumn" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "splitCost", - "type": "string" - }, - "reAllocationParams": { - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "properties": { - "partitions": { - "items": { - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "weight": { - "type": "number" - } - }, - "required": [ - "label", - "weight" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "const": "custom", - "type": "string" - } - }, - "required": [ - "type", - "partitions" - ], - "type": "object" - } - ] - } - }, - "required": [ - "allocationType", - "reAllocationParams" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "telemetry", - "type": "string" - }, - "datasource": { - "minLength": 1, - "type": "string" - }, - "externalMetric": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "provider": { - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude", - "snowflake" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "provider", - "integrationId" - ], - "type": "object" - }, - "mappingParams": { - "properties": { - "mapping": { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "mapping" - ], - "type": "object" - }, - "mappingType": { - "enum": [ - "mapping", - "regexMapping", - "identity" - ], - "type": "string" - }, - "regexTransformation": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "mappingType", - "mappingParams" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "allocationType": { + "const": "dimensionValue", + "type": "string" + }, + "dimensionValue": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "dimensionValue" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "existingColumn", + "type": "string" + }, + "existingColumn": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "existingColumn" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "splitCost", + "type": "string" + }, + "reAllocationParams": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "properties": { + "partitions": { + "items": { + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "label", + "weight" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "custom", + "type": "string" + } + }, + "required": [ + "type", + "partitions" + ], + "type": "object" + } + ] + } + }, + "required": [ + "allocationType", + "reAllocationParams" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "telemetry", + "type": "string" + }, + "datasource": { + "minLength": 1, + "type": "string" + }, + "externalMetric": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "provider": { + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake", + "cloudmonitoring" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "provider", + "integrationId" + ], + "type": "object" + }, + "mappingParams": { + "properties": { + "mapping": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "mapping" + ], + "type": "object" + }, + "mappingType": { + "enum": [ + "mapping", + "regexMapping", + "identity" + ], + "type": "string" + }, + "regexTransformation": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "mappingType", + "mappingParams" + ], + "type": "object" + } +]
3 tool updates
- Changed
create_report1 field changed- changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", + "type": "number" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
preview_report_widget1 field changed- changed
Input schema / properties / widget / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", + "type": "number" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
- Changed
update_report1 field changed- changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "dashboardId": { - "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "DASHBOARD_PDF", - "type": "string" - } - }, - "required": [ - "type", - "dashboardId" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "type": { - "const": "GRAPH_SNAPSHOT", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "datePreset": { - "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "flopN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "from": { - "description": "Widget-level start date override. Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "", - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topN": { - "default": 5, - "maximum": 50, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "TOP_FLOP", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "contentMarkdown": { - "maxLength": 10000, - "minLength": 1, - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "title": { - "default": "", - "type": "string" - }, - "type": { - "const": "TEXT", - "type": "string" - } - }, - "required": [ - "type", - "contentMarkdown" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "additionalGroupBy": { - "default": [], - "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "aggBy": { - "default": "Month", - "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", - "enum": [ - "Week", - "Month" - ], - "type": "string" - }, - "compare": { - "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", - "properties": { - "from": { - "description": "Comparison period start date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "to": { - "description": "Comparison period end date.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - }, - "datePreset": { - "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - "description": { - "default": "", - "type": "string" - }, - "display": { - "default": "tree", - "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", - "enum": [ - "tree", - "table", - "summary" - ], - "type": "string" - }, - "enableAiInvestigation": { - "default": false, - "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", - "type": "boolean" - }, - "from": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "minAbsoluteDiff": { - "default": 100, - "minimum": 0, - "type": "number" - }, - "minRelativeDiff": { - "default": 5, - "minimum": 0, - "type": "number" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "default": "Cost changes", - "type": "string" - }, - "to": { - "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", - "format": "date", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", - "type": "string" - }, - "topLargestAbsoluteChange": { - "default": 20, - "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", - "type": "number" - }, - "type": { - "const": "DIGEST", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "dashboardId": { + "description": "Id of the dashboard to render as a PDF (from `search`/`get`).", + "minLength": 1, + "type": "string" + }, + "datePreset": { + "description": "Optional period override for the PDF. Omit to inherit the report (and destination) period.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Optional custom range start (YYYY-MM-DD). Use with to instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Optional custom range end (inclusive, YYYY-MM-DD). Use with from instead of datePreset.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "DASHBOARD_PDF", + "type": "string" + } + }, + "required": [ + "type", + "dashboardId" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "type": { + "const": "GRAPH_SNAPSHOT", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "datePreset": { + "description": "Widget-level period override (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Use either datePreset OR explicit from/to, not both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "flopN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "from": { + "description": "Widget-level start date override. Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "", + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Omit to inherit report context period.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topN": { + "default": 5, + "maximum": 50, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "TOP_FLOP", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "contentMarkdown": { + "maxLength": 10000, + "minLength": 1, + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "title": { + "default": "", + "type": "string" + }, + "type": { + "const": "TEXT", + "type": "string" + } + }, + "required": [ + "type", + "contentMarkdown" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "additionalGroupBy": { + "default": [], + "description": "Deeper DIGEST hierarchy levels AFTER the root groupBy, in order. Root axis is `reportContext.groupBy` (preferred) or `queries[0].groupBy` when overriding. Example: reportContext.groupBy=\"cos_environment\" + additionalGroupBy=[\"cos_sub_account_id\",\"cos_service_name\"] → tree path environment → project → service.", + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "aggBy": { + "default": "Month", + "description": "DIGEST time grain: Week or Month (default Month). Never Period (use TOP_FLOP for whole-period movers).", + "enum": [ + "Week", + "Month" + ], + "type": "string" + }, + "compare": { + "description": "Required only with explicit from/to when not using datePreset or reportContext. Prefer datePreset / reportContext.", + "properties": { + "from": { + "description": "Comparison period start date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "description": "Comparison period end date.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "datePreset": { + "description": "Widget-level period override for scheduled DIGESTs (same DatePreset as dashboards, e.g. LAST_WEEK). Omit to inherit `reportContext.datePreset` / startDate/endDate. Mutually exclusive with explicit from/to/compare — do not set both.", + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + "description": { + "default": "", + "type": "string" + }, + "display": { + "default": "tree", + "description": "DIGEST presentation: \"tree\" (default, change tree only — faster), \"summary\" (LLM executive narrative — slower; opt-in AI summary), or \"table\" (flat table). Set \"summary\" when the user wants the AI write-up.", + "enum": [ + "tree", + "table", + "summary" + ], + "type": "string" + }, + "enableAiInvestigation": { + "default": false, + "description": "When true, deepest-leaf cost movers get per-node AI investigation before delivery (async analysis_agent_run — noticeably slower). Default false. Independent of display: \"summary\" (executive narrative). Prefer display: \"summary\" alone for a written overview; enable this only when the user wants deep node analysis.", + "type": "boolean" + }, + "from": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "minAbsoluteDiff": { + "default": 100, + "minimum": 0, + "type": "number" + }, + "minRelativeDiff": { + "default": 5, + "minimum": 0, + "type": "number" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On report widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `reportContext`. DIGEST: exactly one `{ type: \"cost\", ... }`. Call get_skill skillId: \"reports\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "default": "Cost changes", + "type": "string" + }, + "to": { + "description": "Only for one-off custom ranges (YYYY-MM-DD). Omit to inherit report context. Do not set when datePreset is set.", + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "topLargestAbsoluteChange": { + "default": 20, + "description": "How many largest-absolute-change nodes to surface. Allowed values: 5, 10, 15, or 20.", + "type": "number" + }, + "type": { + "const": "DIGEST", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } +]
3 tool updates
- Added
find_cost_change_factors - Changed
query1 field changed- added
Input schema / properties / analyzeAdded value: +{ + "additionalProperties": false, + "description": "Optional analyses. Set changePoint true (or { ignoreWeekends }) to run change-point detection once per query after a timeseries result. Incompatible with compare.", + "properties": { + "changePoint": { + "anyOf": [ + { + "type": "boolean" + }, + { + "additionalProperties": false, + "properties": { + "ignoreWeekends": { + "description": "Drop Saturday/Sunday before detection. Maps to Billy `no_weekends`.", + "type": "boolean" + } + }, + "type": "object" + } + ] + } + }, + "type": "object" +}
- Changed
suggest_groupby10 fields changed- added
Input schema / properties / compareAdded value: +{ + "additionalProperties": false, + "description": "Optional reference (previous) period. Omit for current-window ranking. `{}` auto-derives from the current window (same helper as query). `{ from, to }` pins it. Prefer passing this when the user asks what changed, before find_cost_change_factors.", + "properties": { + "from": { + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + }, + "to": { + "format": "date", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$", + "type": "string" + } + }, + "type": "object" +} - added
Input schema / properties / datePresetAdded value: +{ + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" +} - changed
Input schema / properties / filterCel / descriptionPrevious value: -"Optional CEL filter to scope the suggestion (same as query)."New value: +"Optional CEL scope. Omit or \"\" for unfiltered (Billy where_clause TRUE). That is not an AWS-only filter even though columns_where_clause falls back to [\"cos_provider\"]." - changed
Input schema / properties / from / descriptionPrevious value: -"Start date (YYYY-MM-DD)"New value: +"Current period start (YYYY-MM-DD). Omit when using datePreset." - added
Input schema / properties / from / formatAdded value: +"date" - added
Input schema / properties / from / patternAdded value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" - changed
Input schema / properties / to / descriptionPrevious value: -"End date (YYYY-MM-DD)"New value: +"Current period end (YYYY-MM-DD), inclusive. Omit when using datePreset." - added
Input schema / properties / to / formatAdded value: +"date" - added
Input schema / properties / to / patternAdded value: +"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$" - removed
Input schema / requiredRemoved value: -[ - "from", - "to" -]
5 tool updates
- Changed
create_dashboard1 field changed- changed
Input schema / properties / widgets / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } +]
- Changed
create_virtual_dimension_draft1 field changed- changed
Input schema / properties / rules / items / properties / allocation / oneOfPrevious value: -[ - { - "properties": { - "allocationType": { - "const": "dimensionValue", - "type": "string" - }, - "dimensionValue": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "dimensionValue" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "existingColumn", - "type": "string" - }, - "existingColumn": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "existingColumn" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "splitCost", - "type": "string" - }, - "reAllocationParams": { - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "properties": { - "partitions": { - "items": { - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "weight": { - "type": "number" - } - }, - "required": [ - "label", - "weight" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "const": "custom", - "type": "string" - } - }, - "required": [ - "type", - "partitions" - ], - "type": "object" - } - ] - } - }, - "required": [ - "allocationType", - "reAllocationParams" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "telemetry", - "type": "string" - }, - "datasource": { - "minLength": 1, - "type": "string" - }, - "externalMetric": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "provider": { - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "provider", - "integrationId" - ], - "type": "object" - }, - "mappingParams": { - "properties": { - "mapping": { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "mapping" - ], - "type": "object" - }, - "mappingType": { - "enum": [ - "mapping", - "regexMapping", - "identity" - ], - "type": "string" - }, - "regexTransformation": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "mappingType", - "mappingParams" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "allocationType": { + "const": "dimensionValue", + "type": "string" + }, + "dimensionValue": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "dimensionValue" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "existingColumn", + "type": "string" + }, + "existingColumn": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "existingColumn" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "splitCost", + "type": "string" + }, + "reAllocationParams": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "properties": { + "partitions": { + "items": { + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "label", + "weight" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "custom", + "type": "string" + } + }, + "required": [ + "type", + "partitions" + ], + "type": "object" + } + ] + } + }, + "required": [ + "allocationType", + "reAllocationParams" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "telemetry", + "type": "string" + }, + "datasource": { + "minLength": 1, + "type": "string" + }, + "externalMetric": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "provider": { + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "provider", + "integrationId" + ], + "type": "object" + }, + "mappingParams": { + "properties": { + "mapping": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "mapping" + ], + "type": "object" + }, + "mappingType": { + "enum": [ + "mapping", + "regexMapping", + "identity" + ], + "type": "string" + }, + "regexTransformation": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "mappingType", + "mappingParams" + ], + "type": "object" + } +]
- Changed
query1 field changed- changed
Input schema / properties / queries / items / oneOfPrevious value: -[ - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "currency": { - "default": "USD", - "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", - "enum": [ - "USD", - "EUR", - "GBP", - "CNY" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "default": "cost", - "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", - "enum": [ - "cost", - "effective_cost", - "list_cost", - "contracted_cost", - "unblended_cost", - "net_unblended_cost", - "amortized_cost", - "net_amortized_cost" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "cost", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "metric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "filterCel": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." - }, - "groupBy": { - "anyOf": [ - { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - } - ] - }, - { - "type": "null" - } - ] - }, - "metricId": { - "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "usage", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "metricId", - "name" - ], - "type": "object" - }, - { - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "formula": { - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "type": { - "const": "formula", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "formula", - "name" - ], - "type": "object" - }, - { - "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", - "properties": { - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "budgetId": { - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "budget", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - }, - "virtualDimensionValue": { - "type": "string" - } - }, - "required": [ - "type", - "budgetId", - "name" - ], - "type": "object" - }, - { - "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "alias": { - "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", - "maxLength": 50, - "minLength": 1, - "type": "string" - }, - "chartType": { - "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", - "enum": [ - "BAR", - "LINE", - "AREA", - "WATERFALL", - "TABLE" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "name": { - "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", - "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "type": "string" - }, - "provider": { - "default": "tsuga", - "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - }, - "rollingAggregation": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "window": { - "properties": { - "preset": { - "enum": [ - "MONTH", - "WEEK" - ], - "type": "string" - }, - "relativeDays": { - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - } - }, - "type": "object" - } - }, - "required": [ - "aggregator", - "window" - ], - "type": "object" - }, - "type": { - "const": "externalMetric", - "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", - "type": "string" - } - }, - "required": [ - "type", - "integrationId", - "name" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "currency": { + "default": "USD", + "description": "Currency for cost values (USD, EUR, GBP). Defaults to \"USD\".", + "enum": [ + "USD", + "EUR", + "GBP", + "CNY" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "default": "cost", + "description": "Cost column to query (e.g. \"cost\", \"effective_cost\", \"amortized_cost\"). Defaults to \"cost\".", + "enum": [ + "cost", + "effective_cost", + "list_cost", + "contracted_cost", + "unblended_cost", + "net_unblended_cost", + "amortized_cost", + "net_amortized_cost" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "cost", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Saved Costory metric id from list_metrics metrics[].id. This is the query identity — not list_metrics `datasourceId` (that filter only returns groupBy dimensions for telemetry).", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "metric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "filterCel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional scope filter as CEL (Common Expression Language). Examples: cos_service_name in [\"AmazonEC2\"]; cos_provider in [\"AWS\"] && cos_environment in [\"prod\"]. Label dimensions use the cos_ prefix; native provider fields omit it. Unlabelled/untagged resources have null values; use == null to match them (e.g. cos_environment == null) and != null to exclude them. Omit or pass empty string for no filter." + }, + "groupBy": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + } + ] + }, + { + "type": "null" + } + ] + }, + "metricId": { + "description": "Usage metric id (from suggest_usage_metrics). Distinct from cost metricId and from list_metrics datasourceId.", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "usage", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "metricId", + "name" + ], + "type": "object" + }, + { + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "formula": { + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "type": { + "const": "formula", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "formula", + "name" + ], + "type": "object" + }, + { + "description": "Plot a budget. Use aggBy Month for one value per calendar month. For a daily running total within each month (to see which day the budget was reached), use aggBy Day with rollingAggregation { aggregator: SUM, window: { preset: MONTH } }.", + "properties": { + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "budgetId": { + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "budget", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + }, + "virtualDimensionValue": { + "type": "string" + } + }, + "required": [ + "type", + "budgetId", + "name" + ], + "type": "object" + }, + { + "description": "Plot a live external metric from an integration. integrationId comes from list_metrics(includeExternal: true). For Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. For Datadog: metricName is the Datadog metric name; groupByFields are metric tags; conditions is an optional Datadog filter/query string. When query is set it is the Datadog metrics query string (pass-through to queryMetrics); metricName / aggregator / conditions / groupByFields are leftover UI state and are not a second writer. .rollup is required and the interval must be ≥ 24h (daily / weekly / monthly or seconds ≥ 86400). Costory will not fill an empty weekly series. For CloudWatch: metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names; conditions is an optional dimension filter. For Amplitude: metricName is one of active_users, new_users, active_users_weekly, active_users_monthly; aggregator must be SUM; groupByFields is at most one built-in user property from list_metrics attributes (version, country, city, region, DMA, language, platform, os, device, device_type, start_version, paying); conditions is an optional Amplitude segment string (s parameter); Hour aggBy is unsupported; weekly/monthly metrics snap Amplitude period-end dates to the week/month start. For BigQuery: metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. For S3: identical to BigQuery — metricName is the fully-qualified id of a Costory-managed external table over the customer's mirrored Parquet; dateColumn, metricColumn, and gapFillingMethod are required.", + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "alias": { + "description": "Optional human-readable label (e.g. \"Cost by environment\", \"AWS spend\"). Prefer short identifiers in name; put descriptive text here.", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "chartType": { + "description": "Explorer chart for this series: BAR, LINE, AREA, WATERFALL, or TABLE. Omit to use LINE.", + "enum": [ + "BAR", + "LINE", + "AREA", + "WATERFALL", + "TABLE" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "name": { + "description": "Required series identifier referenced by formulas (e.g. \"a\", \"b\", \"a__r6\"). Prefer short identifiers; put human labels in alias — never spaces or descriptive phrases in name.", + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", + "type": "string" + }, + "provider": { + "default": "tsuga", + "description": "External metric provider. Defaults to \"tsuga\" when omitted for backward compatibility with existing Tsuga MCP clients.", + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + }, + "rollingAggregation": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "window": { + "properties": { + "preset": { + "enum": [ + "MONTH", + "WEEK" + ], + "type": "string" + }, + "relativeDays": { + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + } + }, + "required": [ + "aggregator", + "window" + ], + "type": "object" + }, + "type": { + "const": "externalMetric", + "description": "Required series discriminator — always set explicitly, never omit. One of: \"cost\" | \"metric\" | \"usage\" | \"formula\" | \"budget\" | \"externalMetric\".", + "type": "string" + } + }, + "required": [ + "type", + "integrationId", + "name" + ], + "type": "object" + } +]
- Changed
update_dashboard1 field changed- changed
Input schema / properties / operations / items / oneOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "op": { - "const": "add", - "type": "string" - }, - "widget": { - "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "additionalProperties": false, - "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", - "properties": { - "description": { - "default": "", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "textContent": { - "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "const": "text", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "type", - "textContent", - "title" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "aggBy": { - "default": "Month", - "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", - "enum": [ - "Hour", - "Day", - "Week", - "Month", - "Period" - ], - "type": "string" - }, - "compare": { - "additionalProperties": false, - "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", - "properties": { - "chartType": { - "default": "WATERFALL", - "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", - "enum": [ - "WATERFALL", - "TABLE", - "KPI_BREAKDOWN" - ], - "type": "string" - }, - "enabled": { - "default": true, - "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", - "type": "boolean" - }, - "from": { - "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "to": { - "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - } - }, - "type": "object" - }, - "datePreset": { - "anyOf": [ - { - "enum": [ - "TRAILING_90_DAYS", - "TRAILING_30_DAYS", - "TRAILING_45_DAYS", - "TRAILING_7_DAYS", - "TRAILING_3_DAYS", - "TRAILING_14_WEEKS", - "MTD", - "QTD", - "YTD", - "LAST_WEEK", - "LAST_MONTH", - "LAST_6_MONTHS", - "LAST_12_MONTHS", - "LAST_4_YEARS", - "LAST_3_MONTHS", - "LAST_INVOICE_MONTH" - ], - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." - }, - "description": { - "default": "", - "type": "string" - }, - "extendDashboardConditions": { - "default": true, - "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", - "type": "boolean" - }, - "from": { - "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "h": { - "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 9007199254740991, - "type": "integer" - }, - "limit": { - "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "queries": { - "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", - "items": { - "additionalProperties": {}, - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "type": "array" - }, - "scopeId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." - }, - "title": { - "type": "string" - }, - "to": { - "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", - "pattern": "^\\d{4}-\\d{2}-\\d{2}$", - "type": "string" - }, - "type": { - "const": "chart", - "default": "chart", - "type": "string" - }, - "w": { - "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", - "exclusiveMinimum": 0, - "maximum": 12, - "type": "integer" - }, - "x": { - "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", - "maximum": 11, - "minimum": 0, - "type": "integer" - }, - "y": { - "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "title" - ], - "type": "object" - } - ] - } - }, - "required": [ - "op" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "replace", - "type": "string" - }, - "widget": { - "additionalProperties": {}, - "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "widgetId": { - "description": "ID of the widget to overwrite (keeps its position).", - "type": "string" - } - }, - "required": [ - "op", - "widgetId", - "widget" - ], - "type": "object" - }, - { - "additionalProperties": false, - "properties": { - "op": { - "const": "remove", - "type": "string" - }, - "widgetId": { - "description": "ID of the widget to delete.", - "type": "string" - } - }, - "required": [ - "op", - "widgetId" - ], - "type": "object" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "op": { + "const": "add", + "type": "string" + }, + "widget": { + "description": "Widget definition to append. Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "additionalProperties": false, + "description": "Text block widget (notes, headings, commentary). No queries or period — does not inherit dashboard metric/groupBy.", + "properties": { + "description": { + "default": "", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "textContent": { + "description": "Markdown or plain text body. Stored as `{ type: \"text\", textContent }` — same as the UI text widget.", + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "const": "text", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "type", + "textContent", + "title" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "aggBy": { + "default": "Month", + "description": "Time grain for the series: Hour, Day, Week, Month, or Period (default Month).", + "enum": [ + "Hour", + "Day", + "Week", + "Month", + "Period" + ], + "type": "string" + }, + "compare": { + "additionalProperties": false, + "description": "Add a comparison period to show cost evolution side-by-side. Omit `from`/`to` to compare against the preceding period automatically; set `chartType` to choose how it renders.", + "properties": { + "chartType": { + "default": "WATERFALL", + "description": "How the comparison renders: WATERFALL (default, contribution to change), TABLE (before/after rows), or KPI_BREAKDOWN (per-group KPI cards). Note KPI_BREAKDOWN and TABLE widgets cannot be exported as PNG via get_widget_png.", + "enum": [ + "WATERFALL", + "TABLE", + "KPI_BREAKDOWN" + ], + "type": "string" + }, + "enabled": { + "default": true, + "description": "Defaults to true — supplying `compare` at all turns the comparison on. Pass false to keep the block while disabling it (useful when echoing back a payload from `get`).", + "type": "boolean" + }, + "from": { + "description": "Comparison period start date. Omit (together with `to`) to compare against the preceding period, derived from the main period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "to": { + "description": "Comparison period end date. Required together with `from`; omit both to auto-derive.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + } + }, + "type": "object" + }, + "datePreset": { + "anyOf": [ + { + "enum": [ + "TRAILING_90_DAYS", + "TRAILING_30_DAYS", + "TRAILING_45_DAYS", + "TRAILING_7_DAYS", + "TRAILING_3_DAYS", + "TRAILING_14_WEEKS", + "MTD", + "QTD", + "YTD", + "LAST_WEEK", + "LAST_MONTH", + "LAST_6_MONTHS", + "LAST_12_MONTHS", + "LAST_4_YEARS", + "LAST_3_MONTHS", + "LAST_INVOICE_MONTH" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Widget-level date preset override. Omit to inherit dashboardContext.datePreset or startDate/endDate; use only when this widget differs from the dashboard period." + }, + "description": { + "default": "", + "type": "string" + }, + "displayOptions": { + "description": "TABLE-only. sort {by, direction}, hideEmptyRows, topN (omit limit), showTotal, precision. get_skill dashboards.", + "properties": { + "hideEmptyRows": { + "description": "Hide rows with abs displayed sum < 0.01", + "type": "boolean" + }, + "precision": { + "description": "Fraction digits 0–6", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "showTotal": { + "description": "Show Total column", + "type": "boolean" + }, + "sort": { + "properties": { + "by": { + "description": "total | cost | groupBy | query letter (a)", + "minLength": 1, + "type": "string" + }, + "direction": { + "default": "DESC", + "description": "ASC | DESC (default DESC)", + "enum": [ + "ASC", + "DESC" + ], + "type": "string" + } + }, + "required": [ + "by" + ], + "type": "object" + }, + "topN": { + "description": "Ranked rows 1–100 after hide-empty + sort. Omit limit.", + "maximum": 100, + "minimum": 1, + "type": "integer" + } + }, + "type": "object" + }, + "extendDashboardConditions": { + "default": true, + "description": "Whether this widget AND-merges the dashboard's `context.conditionsCel` with its own `filterCel`. Defaults to true — omit to inherit the dashboard filter. Set false only when the widget must ignore dashboard-wide conditions.", + "type": "boolean" + }, + "from": { + "description": "Widget-level date override (inclusive), e.g. '2025-01-01'. Omit when the widget uses the dashboard context period.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "h": { + "description": "Grid height in rows. When provided, the widget keeps this exact height instead of the auto-computed default. Obtain the value from the `h` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + }, + "limit": { + "description": "Max groups/rows per series. Omit to use the default (100). Increase up to 1000 when you need more than 100 breakdown groups.", + "maximum": 1000, + "minimum": 1, + "type": "integer" + }, + "queries": { + "description": "Same series objects as the `query` tool `queries` array (cost / metric / usage / externalMetric / formula / budget). Each requires `type` (never omit) and a `name` (prefer short ids like a/b/c); put human labels in `alias`. On dashboard chart widgets prefer sparse `{ type: \"cost\", name: \"a\", alias?, filterCel? }` and inherit metricId / currency / groupBy / period from `dashboardContext`. Call get_skill skillId: \"dashboards\" for examples.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "scopeId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries." + }, + "title": { + "type": "string" + }, + "to": { + "description": "Widget-level end date override (inclusive). Required together with `from` only when overriding the dashboard period; omit when inheriting context.", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" + }, + "type": { + "const": "chart", + "default": "chart", + "type": "string" + }, + "w": { + "description": "Grid width in columns (1–12). When provided, the widget keeps this exact width instead of the auto-computed default. Obtain the value from the `w` field returned by `get`.", + "exclusiveMinimum": 0, + "maximum": 12, + "type": "integer" + }, + "x": { + "description": "Grid column index (0-based, 0 = left edge, max 11). When provided together with `y` (on a create_dashboard widget or an update_dashboard `add`) the widget is placed at the exact position instead of being auto-packed. Use `x`/`y` from `get` to copy a widget at its original position, or set manually (e.g. top-left: x=0 y=0, top-right of a half-width widget: x=6 y=0). Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing. When `w` is also set, `x + w` must be <= 12.", + "maximum": 11, + "minimum": 0, + "type": "integer" + }, + "y": { + "description": "Grid row index (0-based, 0 = top). Rows grow downward; combine with `x` to pin a widget to a specific spot. Both `x` and `y` must be supplied together — if only one is given the position falls back to auto-packing.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "title" + ], + "type": "object" + } + ] + } + }, + "required": [ + "op" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "replace", + "type": "string" + }, + "widget": { + "additionalProperties": {}, + "description": "Same widget object as `widget` on op: \"add\" (text or chart). Chart `queries` match the `query` tool shape; pass only overrides relative to dashboard context.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "widgetId": { + "description": "ID of the widget to overwrite (keeps its position).", + "type": "string" + } + }, + "required": [ + "op", + "widgetId", + "widget" + ], + "type": "object" + }, + { + "additionalProperties": false, + "properties": { + "op": { + "const": "remove", + "type": "string" + }, + "widgetId": { + "description": "ID of the widget to delete.", + "type": "string" + } + }, + "required": [ + "op", + "widgetId" + ], + "type": "object" + } +]
- Changed
update_virtual_dimension_draft1 field changed- changed
Input schema / properties / rules / items / properties / allocation / oneOfPrevious value: -[ - { - "properties": { - "allocationType": { - "const": "dimensionValue", - "type": "string" - }, - "dimensionValue": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "dimensionValue" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "existingColumn", - "type": "string" - }, - "existingColumn": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "existingColumn" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "splitCost", - "type": "string" - }, - "reAllocationParams": { - "discriminator": { - "propertyName": "type" - }, - "oneOf": [ - { - "properties": { - "partitions": { - "items": { - "properties": { - "label": { - "minLength": 1, - "type": "string" - }, - "weight": { - "type": "number" - } - }, - "required": [ - "label", - "weight" - ], - "type": "object" - }, - "type": "array" - }, - "type": { - "const": "custom", - "type": "string" - } - }, - "required": [ - "type", - "partitions" - ], - "type": "object" - } - ] - } - }, - "required": [ - "allocationType", - "reAllocationParams" - ], - "type": "object" - }, - { - "properties": { - "allocationType": { - "const": "telemetry", - "type": "string" - }, - "datasource": { - "minLength": 1, - "type": "string" - }, - "externalMetric": { - "properties": { - "aggregator": { - "enum": [ - "SUM", - "AVG", - "MAX", - "MIN" - ], - "type": "string" - }, - "conditions": { - "type": "string" - }, - "dateColumn": { - "minLength": 1, - "type": "string" - }, - "gapFillingMethod": { - "enum": [ - "ZERO", - "FORWARD_FILL", - "LINEAR_INTERPOLATION", - "SPREAD" - ], - "type": "string" - }, - "groupByFields": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "maxItems": 7, - "type": "array" - }, - "integrationId": { - "description": "External metric integration ID from list_metrics(includeExternal: true).", - "minLength": 1, - "type": "string" - }, - "metricColumn": { - "minLength": 1, - "type": "string" - }, - "metricName": { - "default": "", - "type": "string" - }, - "provider": { - "enum": [ - "tsuga", - "bigquery", - "datadog", - "cloudwatch", - "s3", - "amplitude" - ], - "type": "string" - }, - "query": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "provider", - "integrationId" - ], - "type": "object" - }, - "mappingParams": { - "properties": { - "mapping": { - "additionalProperties": { - "type": "string" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" - } - }, - "required": [ - "mapping" - ], - "type": "object" - }, - "mappingType": { - "enum": [ - "mapping", - "regexMapping", - "identity" - ], - "type": "string" - }, - "regexTransformation": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "allocationType", - "mappingType", - "mappingParams" - ], - "type": "object" - } -]New value: +[ + { + "properties": { + "allocationType": { + "const": "dimensionValue", + "type": "string" + }, + "dimensionValue": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "dimensionValue" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "existingColumn", + "type": "string" + }, + "existingColumn": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "existingColumn" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "splitCost", + "type": "string" + }, + "reAllocationParams": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "properties": { + "partitions": { + "items": { + "properties": { + "label": { + "minLength": 1, + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "label", + "weight" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "const": "custom", + "type": "string" + } + }, + "required": [ + "type", + "partitions" + ], + "type": "object" + } + ] + } + }, + "required": [ + "allocationType", + "reAllocationParams" + ], + "type": "object" + }, + { + "properties": { + "allocationType": { + "const": "telemetry", + "type": "string" + }, + "datasource": { + "minLength": 1, + "type": "string" + }, + "externalMetric": { + "properties": { + "aggregator": { + "enum": [ + "SUM", + "AVG", + "MAX", + "MIN" + ], + "type": "string" + }, + "conditions": { + "type": "string" + }, + "dateColumn": { + "minLength": 1, + "type": "string" + }, + "gapFillingMethod": { + "enum": [ + "ZERO", + "FORWARD_FILL", + "LINEAR_INTERPOLATION", + "SPREAD" + ], + "type": "string" + }, + "groupByFields": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 7, + "type": "array" + }, + "integrationId": { + "description": "External metric integration ID from list_metrics(includeExternal: true).", + "minLength": 1, + "type": "string" + }, + "metricColumn": { + "minLength": 1, + "type": "string" + }, + "metricName": { + "default": "", + "type": "string" + }, + "provider": { + "enum": [ + "tsuga", + "bigquery", + "datadog", + "cloudwatch", + "s3", + "amplitude", + "snowflake" + ], + "type": "string" + }, + "query": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "provider", + "integrationId" + ], + "type": "object" + }, + "mappingParams": { + "properties": { + "mapping": { + "additionalProperties": { + "type": "string" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "mapping" + ], + "type": "object" + }, + "mappingType": { + "enum": [ + "mapping", + "regexMapping", + "identity" + ], + "type": "string" + }, + "regexTransformation": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "allocationType", + "mappingType", + "mappingParams" + ], + "type": "object" + } +]
44 tool updates
- First observed
archive_report - First observed
create_alert - First observed
create_dashboard - First observed
create_event - First observed
create_report - First observed
create_virtual_dimension_draft - First observed
delete_tag - First observed
discard_virtual_dimension_draft - First observed
get - First observed
get_context - First observed
get_dashboard_widget_data - First observed
get_dashboard_widget_image - First observed
get_documentation_page - First observed
get_report_execution - First observed
get_report_execution_widget - First observed
get_skill - First observed
list_alerts - First observed
list_available_destinations - First observed
list_events - First observed
list_metrics - First observed
list_organizations - First observed
list_reports - First observed
list_skills - First observed
list_tags - First observed
list_teams - First observed
list_virtual_dimensions - First observed
preview_alert - First observed
preview_report_widget - First observed
preview_virtual_dimension_draft - First observed
publish_virtual_dimension - First observed
query - First observed
retry_report_execution - First observed
run_report_now - First observed
search - First observed
search_documentation - First observed
suggest_actions - First observed
suggest_groupby - First observed
suggest_usage_metrics - First observed
transfer_report_execution - First observed
update_dashboard - First observed
update_event - First observed
update_report - First observed
update_virtual_dimension_draft - First observed
virtual_dimension_overlap_matrix
Related MCP Connectors
Ask about AWS, Azure, GCP, OpenAI, Anthropic and Snowflake costs, budgets, and anomalies.
Query cloud, AI and SaaS spend across 25+ providers: costs, budgets, anomalies, unit economics.
Cloud cost + FinOps knowledge for AI agents: AWS/Azure/GCP optimisation, AI spend, waste playbooks.
Cloud cost visibility and savings recommendations grounded in your actual AWS, GCP and Azure bill.
Related MCP Servers
- AlicenseAqualityAmaintenanceKubernetes cluster cost analysis and optimization. Query costs, find savings opportunities, get rightsizing recommendations, and analyze pod spend across AKS, EKS, and GKE — directly from Claude612Apache 2.0
- AlicenseAqualityCmaintenanceEnables analyzing AWS cloud costs through natural language queries, providing cost summaries, anomaly detection, idle resource identification, rightsizing recommendations, and tagging compliance via Claude.1027 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to pre-flight estimate query costs, enforce hard budgets, reconcile actual billed spend, and suggest cheaper rewrites across BigQuery, Snowflake, and Databricks.MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to query multi-cloud cost and usage data through a uniform read-only toolset, including cost summaries, trends, budgets, forecasts, and optimization recommendations.MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.