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
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP
- URL
Available Tools
45 toolsarchive_reportADestructiveInspect
Archive a report (soft delete; no MCP restore). DANGER ZONE: confirm with the user that they want to archive this report before calling.
| 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?
The description adds valuable context beyond the annotations: it specifies that this is a soft delete, that there is no MCP restore, and that user confirmation is mandatory. This complements the destructiveHint annotation without contradicting it.
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 and structured effectively: a clear statement of purpose followed by a critical safety warning. Every sentence provides necessary information with no redundancy or fluff.
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 simplicity, the destructive annotation, and complete schema coverage, the description is sufficiently complete. It explains the operation's nature and safety requirements without missing essential 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?
The input schema fully documents both parameters (slug and reportId) with clear descriptions and formats. The description adds no additional parameter insights, so it relies on the schema, which is sufficient. Baseline 3 is appropriate given high schema coverage.
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 ('Archive a report') and the resource ('report'), and adds clarifying details ('soft delete; no MCP restore') that distinguish it from other destructive tools like delete_tag. 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 clear context for use by flagging a 'DANGER ZONE' and requiring user confirmation before calling. It does not explicitly mention alternatives or when not to use it, but the safety instruction effectively guides proper usage.
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. MCP is create-only — there is no update_alert; edit in the UI via the returned URL. Accepts the same query config as query (prefer datePreset over hand-computed from/to). 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. 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 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. | |
| 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) | |
| 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 | Optional saved team scope id (from list_teams). Merges the scope whereClause into cost/usage queries. | |
| 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?
The description discloses key behavioral traits beyond annotations: the tool is create-only (no update), window math is evaluated daily in BigQuery (so no evaluation period is chosen), dedup controls re-notification frequency, and the returned URL must be included in the response. Annotations only indicate create/write nature, so this added context is highly valuable.
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 detailed but every sentence earns its place. It is front-loaded with the core purpose, followed by critical limitations, parameter guidance, condition examples, and a complete example. No redundant or vague phrases; the density is 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?
Given the tool's complexity (16 params, nested objects, no output schema), the description covers all critical aspects: condition syntax, dedup behavior, period semantics, notification channels, the return URL requirement, and a full example. It leaves no major ambiguity 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%, but the description adds significant meaning beyond the schema: it explains the relationship between period and evaluation, the condition expression syntax with examples, dedup semantics (CALENDAR vs ROLLING), and how to structure queries. The example maps a natural-language request to concrete parameter values, making parameter usage far clearer.
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: 'Create a cost alert that monitors one or more queries and notifies when a condition fires.' It clearly distinguishes from sibling tools by noting 'MCP is create-only — there is no update_alert; edit in the UI via the returned URL.' This gives a precise scope and differentiates it from other create_* 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 provides explicit usage context and alternatives: 'MCP is create-only — there is no update_alert; edit in the UI via the returned URL', and 'Use list_available_destinations for SLACK/TEAMS channel IDs.' It also explains when to prefer datePreset over hand-computed from/to, and gives a full example. This goes well beyond basic 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.
create_dashboardAInspect
Create a dashboard with one or more widgets. 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. 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). | |
| teamId | No | Optional owning team id. Independent of visibility; omit or null for no team. | |
| 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"}]. | |
| visibility | No | PRIVATE (default), PUBLIC, or TEMPLATE. Set `teamId` to share with a team — there is no TEAM visibility value. | PRIVATE |
| 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?
Annotations are minimal (readOnly false, openWorld false, destructive false), so the description carries the burden. It clearly discloses the mutation, the deprecated context alias, the auto-pack grid behavior, and the required response behavior: 'Returns a URL — you MUST include it in your 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 dense but well-structured: main action up front, then get_skill prerequisite, dashboardContext rules, widget types, and return contract. For a tool with nested objects and many rules, the length is justified and 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?
Given the tool's complexity, the description covers the skill prerequisite, required context fields, inheritance defaults, text/chart/compare widget handling, grid layout, and return URL. With no output schema, mentioning the returned URL and requiring it in the response is important and complete.
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 synthesizing dashboardContext semantics, the deprecated context alias, widget-type differences, and compare behavior. It does not merely repeat schema text even though the schema already covers many details.
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 dashboard with one or more widgets.' It clearly distinguishes this tool from siblings like update_dashboard and create_report by focusing on dashboard creation with widgets.
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 with skillId 'dashboards' first, and provides concrete inheritance rules ('do not repeat from/to, datePreset, groupBy, metricId, currency, or conditionsCel when they match dashboardContext'). It also warns against sending both context and dashboardContext and states when the period may be omitted.
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) — metadata.source is reserved/system-managed and should not be set by agents.
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", category: "TECHNICAL", 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. | |
| 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 existing annotation charts unchanged; pass widgetEventId when the event has multiple charts. | |
| category | No | BUSINESS = org/budget change, TECHNICAL = deployment/infra change, PROVIDER = cloud provider update | TECHNICAL |
| 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 (readOnlyHint:false, destructiveHint:false) already indicate a write operation, and the description consistently says 'Create.' It adds extra behavioral context: the widget creates a visual annotation tied to cost movement, metadata.source is reserved/system-managed and must not be set by agents, and tag values should be discovered via list_tags rather than invented. This significantly enriches understanding of side effects and constraints.
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 a clear purpose, then logically progresses to usage guidance, parameter tips, and a compact example. Every sentence adds value: the widget guidance, tag discovery, metadata.source warning, and example all earn their place. While longer than average, it is appropriately sized for a tool with 8 parameters and nested objects.
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 (8 params, nested objects, no output schema), the description is remarkably complete. It covers when to use, how to construct a widget, tag conventions, metadata constraints, and provides a complete JSON example. It leaves little ambiguity about what the agent should provide in various scenarios, making it fully actionable.
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 goes far beyond baseline by providing a full worked example, explaining the widget object shares shape with the query tool, clarifying the use of tags with list_tags, and warning about metadata.source. This adds substantial semantic meaning to the parameters, especially for nested objects like widget and metadata.
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 'Create a new event to annotate a cost change,' which is a specific verb+resource+scope statement. It clearly distinguishes from sibling create tools like create_alert, create_dashboard, and create_report by focusing on cost-change annotation. The additional context about displaying events on cost charts reinforces the tool's unique role.
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 this when the user wants to document why costs changed — e.g. a deployment, migration, pricing update, or business decision.' It also provides conditional guidance: 'Omit widget only for purely informational org-wide events,' and recommends calling list_tags to discover existing tags. This constitutes clear context and exclusions, well beyond a vague 'use this for events.'
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. 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.
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). Returns report URL and nextRunAt; NOW also returns execution URLs and per-destination status.
EXAMPLE monthly DIGEST: { visibility: "PRIVATE", 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: { visibility: "PRIVATE", 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). | |
| teamId | No | Optional owning team id. Independent of type/visibility; omit or null for no team. | |
| 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`). | |
| visibility | No | 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. | PRIVATE |
| 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?
Adds substantial side-effect disclosure beyond the spartan annotations: NOW 'sends real messages now' and SCHEDULED 'authorizes future channel delivery,' both flagged as DANGER ZONE. Also discloses the deprecated context alias, inheritance behavior, and return payload (report URL, nextRunAt, plus execution URLs and per-destination status for NOW). No contradiction — destructiveHint=false concerns data destruction, while the description transparently surfaces irreversible delivery side effects the annotation omits.
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?
Unusually long, but earns its length given 5 widget types, 3 schedule modes, nested inheritance, and a deprecation. Purpose and DANGER ZONE warnings are front-loaded, and both JSON examples are complete valid payloads rather than filler. Not a 5 because the density is overwhelming and would benefit from clearer visual hierarchy (headers or bullet separation between the schedule modes and widget types).
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 10-parameter tool with nested objects and no output schema, the description covers the input model, deprecations, return values, and required confirmation steps, then externalizes remaining depth to get_skill('reports'). The schema carries parameter detail at 100%, and the examples anchor the abstract rules. Nothing an agent needs to construct a valid call 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 goes far beyond it: it dictates what belongs in reportContext ('period required when query widgets are present: prefer datePreset'), the inherit-don't-repeat rule for widget overrides, and cross-field constraints ('never send both' context/reportContext; WEEKLY requires weekday). The two complete example payloads give the agent concrete valid shapes for DIGEST and GRAPH_SNAPSHOT+TOP_FLOP that the schema alone cannot 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?
Opens with a specific verb+resource+structure: 'Create a report with shared reportContext and one or more widgets plus destinations.' The schedule-mode breakdown (NOW vs UNSCHEDULED vs SCHEDULED) and widget-type enumeration make the tool's scope unambiguous against siblings like update_report, archive_report, and run_report_now.
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 explicit gates: 'Call get_skill with skillId: "reports" first' and a mandatory confirmation step before NOW/SCHEDULED modes ('ask the user to confirm before calling'). Mode selection semantics are well-specified, but it never explicitly names siblings like preview_report_widget or run_report_now as alternatives for iterating on drafts or running existing reports ad hoc.
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?
Beyond the annotations, the description discloses key behaviors: nothing is persisted unless validation passes, bqName is immutable after creation, leftoverRule is auto-added, and the tool does not publish or refresh BigQuery. It also describes the success response shape, which is valuable because no output schema is 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 long, but the tool is complex and the content is dense with operational constraints. It is front-loaded with the core purpose and provides a concrete example at the end. Some redundancy exists with the allocation schema description, but overall the length is justified.
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 complexity, absence of an output schema, and rich parameter semantics, the description is remarkably complete: it covers preconditions, data discovery, validation behavior, success response fields, next steps, and post-publish usage. An agent has enough context to invoke this tool correctly and avoid common pitfalls.
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 goes further by explaining how parameters relate to real-world workflow: virtualDimensionId semantics, externalMetric discovery via list_metrics, required fields per provider, rule id/values being generated, and the bqName mapping. The worked example also clarifies how to structure a rules payload.
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: 'Create a new virtual dimension draft without publishing.' This clearly distinguishes the tool from siblings like publish_virtual_dimension, update_virtual_dimension_draft, and discard_virtual_dimension_draft. It also states the draft lifecycle constraint up front.
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 workflow guidance: call get_skill first, use list_metrics with includeExternal: true for telemetry metrics, set dateColumn/metricColumn for BigQuery/S3, and use preview_virtual_dimension_draft next. It also names exclusions such as 'Do not set datasource' and 'Do not include catch-all/leftover in rules,' making the correct usage clear.
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 mark this read-only and non-destructive, and the description adds substantial behavioral context beyond that: filterCel omitted means unfiltered rather than AWS-only, nestingEdges warns against summing a contributor with its ancestors/descendants, and aggregationMethod should usually be omitted. These are exactly the kind of non-obvious behaviors an agent needs to know.
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 information-dense and front-loaded with the core purpose, followed by usage rules and illustrative examples. There is some redundancy with the schema (e.g., compare {} auto-derivation and aggregationMethod guidance), but the examples and edge-case warnings justify the length.
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 an 8-parameter tool with nested objects and no output schema, the description covers the key invocation decisions: date windowing, compare behavior, column selection limits, filterCel semantics, nestingEdges math, and aggregationMethod. It does not describe the return format, but the examples and parameter clarifications make correct invocation achievable. Minor gaps remain around datePreset versus explicit from/to interaction.
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 (88%), so the baseline is 3, but the description adds meaning through concrete examples and explains important cross-parameter behavior: compare can be {} for auto-derivation, filterCel's unfiltered semantics are clarified, columns can be strings or { column, contains } objects, and independent contributors may be summed. This goes beyond the raw 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 clear, specific statement: 'Find which dimension values drove a cost change between two periods.' It frames this as a before/after analysis, distinguishes it from related investigation tools by pointing to suggest_groupby / search / get_context as precursors, and provides concrete examples that remove 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 explicitly states when to use the tool: for before/after cost-change analysis, with compare required and {} auto-deriving the previous window. It also gives workflow guidance ('Prefer suggest_groupby / search / get_context first') and limits column selection to 2–4 dimensions (max 8), all of which helps an agent decide 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.
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).
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?
Beyond the readOnlyHint and destructiveHint annotations, the description discloses substantial behavior: it includes a type discriminator, budget alignment fields, cost alert firingHistory, report run-health, virtual dimension immutability (bqName), and dashboard widget structure. It also warns that values are derived/output-only and leftoverRule must not be placed in rules. 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 appropriately structured: a front-loaded purpose statement followed by labeled per-resource sections (Budgets, Cost alerts, Reports, Virtual dimensions, Dashboards) and a closing usage tip. Every sentence carries specific, actionable information, and the formatting makes scanning easy.
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 must explain return values and it does so thoroughly: type discriminator, budget fields, cost alert firingHistory, report health, virtual dimension internals, and dashboard widget details. It also covers follow-up actions (get_dashboard_widget_data, update_dashboard, get_report_execution) and domain-specific references (howToQueryAlignedCost), making the tool self-contained.
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 describes both parameters fully (100% coverage), but the description adds crucial usage semantics, such as 'For budgets, pass parent budget id or budget version id' and clarifies that the id field can refer to multiple resource types distinguished by the response's type field. This goes beyond the schema's literal parameter 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 description begins with a specific verb and enumerates exact resources: 'Fetch a V2 dashboard, budget, cost alert, report, or virtual dimension by ID.' It includes a type discriminator and clearly branches out per resource, distinguishing this tool from siblings like get_dashboard_widget_data and get_report_execution.
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 instructs to 'Use "search" (or list tools) first to discover IDs' and provides per-resource guidance: budgets require parent id or version id, cost alerts require id from search or list_alerts, and reports may lead to get_report_execution for delivery content. It also suggests get_skill for dashboards and virtual-dimensions, offering clear alternatives.
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_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, ask the user directly 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?
While the description gives rich behavioral context (return shape, 'connected' boolean, email free-form nature), the annotation readOnlyHint=false contradicts the read-only implication of 'List every destination.' This is a clear contradiction between the described behavior and the structured metadata, so the score is 1 per rubric.
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 every sentence earns its place: purpose, return format, usage instruction, and an example. It is not overly verbose and front-loads the core purpose.
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 explains the return object (slack/teams/email structure, channel arrays, 'connected' boolean) and how to use the results to resolve channelId. This is complete for a listing tool, covering all necessary aspects.
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 covers the single optional 'slug' parameter 100%, including its auto-detect behavior. The tool description adds no extra details about the parameter, 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?
The description uses the specific verb 'List' targeting the resource 'every destination the organization can deliver reports or alerts to,' which clearly distinguishes it from siblings like create_report and create_alert. It also outlines the return structure keyed by provider, reinforcing the tool's 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?
It explicitly instructs to call this tool before create_report or create_alert to resolve channelId values, and for email destinations to ask the user directly. This provides clear when-to-use guidance and a concrete example (the '#infra-costs' case), leaving little 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). 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", category: "TECHNICAL" }
| 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 | |
| category | No | Filter by category. Omit to return all categories. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description focuses on additional behavior: events are returned sorted by date descending, include metadata and widgetEvents, and widgetEventId can be used with update_event. This adds meaningful context beyond the annotations without contradicting them.
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 opens with the core action, then gives a use case, return details, and two illustrative examples. Every sentence earns its place, though the examples add length. It is appropriately front-loaded and structured for quick comprehension.
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 5 parameters and no output schema, the description covers the return format (sorted, metadata, widgetEvents), parameter nuances via examples, and the relationship to update_event. It omits pagination/limits, but for a list tool with this scope, the description is largely complete.
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 clear descriptions for each parameter. The description adds value through examples showing realistic parameter combinations (e.g., category 'TECHNICAL' for deployments) and clarifies the functional intent of from/to and category. This goes beyond the schema's basic 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 description states a specific verb-resource pair ('List events') with a clear scope ('deployments, business changes, provider updates') and date range. It distinguishes itself from siblings like create_event and update_event by focusing on listing existing events, and even references update_event for further actions.
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: 'Use this to correlate cost changes with real-world events' and 'After query you may optionally call this'. It also provides concrete examples with parameter mappings. It does not explicitly say when NOT to use it, but the workflow context and sibling reference (update_event) imply alternatives.
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 update_dashboard 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, so the safety profile is known. The description adds behavioral context by specifying the returned aggregate count per tag and listing the resource types (dashboards, views, explorers, alerts), which goes beyond what annotations or schema convey.
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 sentences, both earning their place: the first states the core function, the second gives concrete usage guidance referencing specific sibling tools. No filler, redundancy, or vague 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?
Given the simple read-only nature, the single optional parameter is well-covered by the schema, and the description explains the return value (counts) and concrete use cases. This is sufficient for an agent to select and invoke the tool correctly without needing an output schema.
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 fully documents the only parameter (slug) with a clear description and auto-detect behavior, so schema coverage is 100%. The tool description does not add any parameter-specific information, making the baseline 3 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 ('List') and resource ('all tags in the organization'), and specifies the returned data (counts of tagged resources). This clearly distinguishes it from sibling tag-related tools like delete_tag and makes the tool's purpose immediately 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 explicitly instructs when to use the tool: before passing tags to create_event, update_event, or update_dashboard. This provides clear context for the agent, though it does not list when not to use it or name alternative tools, so it stops short of a full when-not exclusion.
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 teams the current user belongs to, including each team's scopes. 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). Use when the user asks about a team's costs or wants to filter by team scope.
| 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 and destructiveHint=false, so the safety profile is known. The description adds valuable behavioral context by revealing the structure of the returned data (each scope has an id and filterCel) and how to use those values in other operations, exceeding the baseline expectations.
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 sentences, each earning its place: the first defines the core action, the second explains the returned scope structure and its usage, and the third gives a clear usage trigger. No fluff or 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?
The tool is simple (one optional parameter, read-only, no output schema), and the description covers the output format and use cases well. It doesn't mention error conditions or empty results, but these are minor gaps for this type of 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 description coverage is 100% for the single optional parameter 'slug', and the schema already explains auto-detection and multi-org failure. The description adds no additional parameter semantics, 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?
The description uses a specific verb ('List') and resource ('teams the current user belongs to'), and clearly distinguishes itself from sibling list tools by focusing on teams and their scopes. It also explains the key output elements (scopes with id and filterCel), making the purpose 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?
Explicitly states when to use the tool: 'Use when the user asks about a team's costs or wants to filter by team scope.' This provides clear context without explicitly listing alternatives or exclusions, but the guidance is direct and actionable.
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). Returns the evaluation window, firingDays (distinct days the condition held), firingRows (per-group fires), notificationsCount (fires that survive the dedup window) and a sample of firing dates. 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 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. | |
| 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?
The annotations already mark the tool as read-only and non-destructive, but the description adds deeper behavioral context: it replays the condition over `lookbackDays` and returns specific computed metrics (`firingDays`, `firingRows`, `notificationsCount`). It also clarifies that dedup is optional here and affects only the reported notification count, which is useful behavior not captured in 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 a single paragraph followed by a concrete example; every sentence provides distinct information, from the backtest purpose to the returned fields and the explicit usage note. While the example is lengthy, it earns its place by demonstrating a realistic parameter combination, making the structure efficient 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?
This is a new tool with no output schema, yet the description fully specifies the return payload (evaluation window, `firingDays`, `firingRows`, `notificationsCount`, and sample dates). It also explains prerequisites (same queries/condition/dedup as create_alert), the default lookback, and provides an example, giving an agent everything needed 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 input schema already provides 100% parameter documentation with detailed descriptions, so the baseline is 3. The description enhances this by explaining how the parameters interact: it states `condition` is replayed against `lookbackDays`, and that `dedup` can be omitted or included to see notification impact. The included example maps a real query/condition/dedup to the parameter structure, which adds practical semantic value 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 'Backtest a cost-alert condition BEFORE creating it', clearly identifying the verb (backtest), resource (cost-alert condition), and temporal context. It distinguishes itself from create_alert by explicitly referencing the same queries/condition/dedup without the notification channel, which separates it from the sibling 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?
Explicitly states 'Use this to sanity-check a condition/threshold (and tune dedup) before calling create_alert', giving direct when-to-use guidance. It also clarifies what is not needed (notification channel) and references create_alert as the alternative, so an agent knows when not to invoke this tool.
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 one of 5, 10, 15, 20). 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 destructiveHint=false, and the description reinforces this while adding rich behavioral detail: token-bounded results, per-type return shapes (GRAPH_SNAPSHOT has no image, DASHBOARD_PDF/TEXT return validity pointer), deprecated `context` alias with 'never send both', DIGEST AI opt-ins with performance implications, and threshold defaults. This far exceeds what annotations alone convey.
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 tightly organized with labeled sections ('By type:', 'DIGEST hierarchy:', 'DIGEST threshold defaults:', 'DIGEST AI (opt-in):') and the core purpose is front-loaded. Two worked examples at the end earn their place. It is dense but not padded; slightly long only because the tool genuinely covers multiple widget variants.
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, yet the description fully compensates by enumerating per-type return contents (resolvedPeriod, comparison totals, bounded rows, series sample, validity pointer). It also covers edge cases like deprecated context alias, inheritance rules, DIGEST hierarchy and thresholds, and AI behavior—everything an agent needs to invoke and interpret this complex tool correctly is present.
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 schema already documents parameters individually. The description goes well beyond the schema by providing concrete JSON examples, clarifying inheritance semantics (widgets omit fields to inherit from reportContext), documenting the deprecated `context` alias, and constraining values like topLargestAbsoluteChange (5/10/15/20) and aggBy (Month/Week for DIGEST, never Period). This materially improves correct parameter 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?
Opens with a specific verb and resource: 'Read-only preview of ONE report widget'. It explicitly contrasts with create_report by saying 'pass the same reportContext + widget object you will later hand to create_report' and 'never creates or delivers', so an agent can distinguish it from sibling creation/delivery tools without opening schemas.
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 clearly frames when to use it: preview a single widget before handing it to create_report, and states what it will not do ('never creates or delivers'). It does not explicitly enumerate exclusions for alternatives like get_dashboard_widget_data, but the preview-vs-create contrast is specific enough for safe routing.
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?
Annotations declare readOnlyHint=true, destructiveHint=false; the description adds critical behavioral nuance: it operates on the latest pending draft if one exists (draftPersisted: true) or in-memory published rules without creating a draft (draftPersisted: false, not publishable). It also warns to read the summary field first and clarifies side effects (or lack thereof). This significantly exceeds what annotations alone convey.
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, every sentence carries operational value. It opens with the core purpose, flows through draft-state behavior, parameter relationships, mode-specific outputs, and ends with concrete examples. The structure is logical and dense with no filler—an appropriate size for an 11-parameter 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 11 parameters, no output schema, and complex mode-dependent behavior, the description covers everything an agent needs: draft semantics, output shapes (totals.namedRulesSharePercent, topValues, dailyTotals), parameter constraints, and sibling tool relationships. It also gives examples covering typical use cases. This is complete for even a high-complexity 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?
Although schema coverage is 100%, the description adds deep relational semantics: virtualDimensionId source, position/ruleIndex equivalence, ruleId vs. ruleIndex preference, leftover targeting, mutual exclusivity of datePreset/from/to, currency alignment, costMetric valid values, and how to discover groupBy via search. This goes far beyond the schema's per-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 first sentence states a specific verb+resource: 'Preview per-rule costs or a groupBy breakdown (read-only BigQuery).' It clearly distinguishes between modes and explicitly contrasts with create/update/publish siblings via the draft vs. published state discussion. This is a textbook clear purpose statement.
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 it ('Call after a successful create/update') and when to use alternatives ('To publish changes ... call update_virtual_dimension_draft ... or create_virtual_dimension_draft ... preview alone does not persist'). It also explains the mode choice (costs vs. breakdown) and parameter selection, with examples. This is exemplary usage 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/destructiveHint annotations, the description discloses nuanced behavior: budgetId 'must be the budget version ID', 'Costory will not fill an empty weekly series', Datadog query strings are pass-through while metricName/aggregator/conditions are ignored, and compare auto-derives the preceding period. No statement contradicts 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 front-loaded with a minimum valid call, naming conventions, and organized examples, which helps scanning. However, it is very long, duplicates provider details already present in the input schema, and includes a tangential 'Poll computeStatus until COMPLETED after publish' line that does not clearly belong to this tool, so not 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?
Given the tool's complexity — 11 parameters, nested oneOf schemas, six query types, and five external providers — the description is remarkably complete. It covers required discovery calls, provider-specific field shapes, CEL null semantics, rolling aggregation, formulas, budgets, change-point analysis, limit defaults, and even follow-up tools like list_events and suggest_actions. The lack of an output schema is a minor gap because invocation guidance is thoroughly 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?
Even though schema coverage is 100%, the description adds critical semantics not inferable from the schema: metricId for custom metrics comes from list_metrics and is not the datasourceId, usage metricIds come from suggest_usage_metrics, groupBy is the SPLIT dimension while filterCel is SCOPE, label dimensions use the cos_ prefix, and null matching requires == null. These additions materially improve correct invocation.
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 concrete 'Unified query tool for cost data, custom metrics, usage metrics, external (live integration) metrics, period comparisons, formulas, and budgets' and provides a minimum valid call. This clearly identifies a specific verb, resource, and scope, and differentiates it from sibling tools like get, search, and suggest_actions.
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 routing guidance: 'call suggest_usage_metrics first to discover valid metricIds', 'call search with type: ["dimensions"]' before guessing CEL fields, and 'call get with that id to obtain budgetVersionId' for budgets. It also states clear preferences such as 'prefer datePreset over hand-computed from/to' and 'prefer saved { type: "metric" } when one exists', making when-to-use and when-not-to-use unusually clear.
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.
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?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description doesn't need to cover safety. It adds valuable behavior: return shape ('{ column } or { column, contains } plus metrics'), filterCel semantics ('omitted or "" is unfiltered (not AWS-only)', 'supports == null'), and a guard against inventing columns. However, it doesn't specify edge behavior like empty results or error conditions.
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 and front-loaded with purpose, then usage rules, then parameter nuances, then examples. It is longer than average, but each sentence conveys necessary operational detail and the EXAMPLES block clarifies real usage. The structure is logical, though the first paragraph could be slightly 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?
For a tool with 6 parameters, a nested compare object, no output schema, and many sibling tools, the description covers triggers, parameter preferences, return shape, and downstream integration with find_cost_change_factors. Minor gaps remain: the exact output structure isn't fully enumerated and failure/empty-result behavior is unstated, but overall the 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 coverage is 83%, so the baseline lift is already high, but the description adds substantial semantic meaning beyond the schema: datePreset is preferred over from/to, compare `{}` auto-derives context, filterCel empty is unfiltered, and slug auto-detects while failing for multi-org accounts. These details help an agent construct correct arguments.
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: 'Suggest the best dimensions to group costs by, based on an optional filterCel (CEL).' It also states exactly when to call it ('when the user asks "what should I look at?" or when you need columns for query or find_cost_change_factors'), clearly distinguishing it from sibling tools like find_cost_change_factors and query.
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 trigger conditions and decision rules: 'Prefer datePreset over from/to', 'Omit compare for current-window ranking', 'Pass compare ... when the user asks what changed', and downstream instructions to pass 2–4 returned columns into find_cost_change_factors. It also warns 'do not invent columns', which is crucial for correct invocation.
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_dashboardAInspect
Update an existing dashboard's shared dashboardContext, widgets, tags, and/or team. 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; [] clears). Pass teamId (from list_teams) to assign a team, or teamId: null to detach. At least one of dashboardContext, operations, tags, or teamId is required. 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"] }
• "Move the K8s dashboard to the infra team" (after list_teams returned id "team_xyz") → { dashboardId: "clx9k8s", teamId: "team_xyz" }
• "Remove the dashboard from its team" → { dashboardId: "clx9k8s", teamId: null }
| 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. | |
| teamId | No | Team to assign the dashboard to (from `list_teams`), or null to detach from any team. Optional; omit to leave team 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`, `tags`, or `teamId` 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`, `tags`, or `teamId`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations carry little here, so the description carries the burden, and it delivers: partial-patch semantics (omitted fields keep values), explicit clear behavior for conditionsCel, tags [], and teamId null, the deprecated context alias with the warning never to send both, and the required at least one of dashboardContext/operations/tags/teamId. It also discloses the response contract by saying the response includes inheritedContext and returns a URL the agent MUST include.
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 carries a distinct rule or prerequisite, and the structure front-loads the core purpose before layering details. The embedded examples are high-leverage: three small payloads communicate the common usage patterns more efficiently than additional prose.
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 seven params and deeply nested widget schemas, the description covers prerequisites, conditional requirements, inheritance rules, partial-update semantics, clearing behavior, and the return contract without needing to restate the schema. It also routes the agent to get_skill for workflow examples, closing the gap left by having no output schema.
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 substantial meaning beyond the schema: dashboardContext is a partial patch, tags fully replace the list, teamId null detaches, operations are add/replace/remove, and chart widgets inherit dashboard context so agents should pass only overrides. This guidance directly prevents common misinvocations like repeating inherited fields or sending 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?
The opening sentence names the exact verb (update), the resource (existing dashboard), and the affected surfaces (dashboardContext, widgets, tags, team), clearly distinguishing it from create_dashboard and get. The phrase 'without recreating the dashboard' reinforces that this tool modifies an existing dashboard rather than building a new one.
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 concrete preconditions: call get_skill with skillId 'dashboards' first, look up dashboardId via search, source tags from list_tags, teams from list_teams, and grid positions from get. It also signals the alternative to creation via 'without recreating the dashboard' and includes representative examples that show exactly when to use the tool and how to construct payloads.
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 — provide only what you want to change. Metadata is merged into existing values except metadata.source, which is preserved internally and should not be supplied. Tags replace the full label list. Widget updates the annotation chart when the event has one; creates one if none exists. 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." }
| 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 labels on the event. | |
| 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 existing annotation charts unchanged; pass widgetEventId when the event has multiple charts. | |
| eventId | Yes | Event ID from list_events or create_event. | |
| category | No | New category. | |
| 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?
The description goes beyond annotations by detailing merge semantics for metadata (including the preserved `metadata.source`), full replacement behavior for tags, and widget update/create logic. These are critical behavioral traits an agent needs to know before invoking the 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?
The description is front-loaded with the primary action, followed by targeted guidance and caveats. Two concrete examples illustrate common use cases without bloat. 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 10-parameter mutation tool with nested objects and no output schema, the description covers all critical facets: event lookup, optional fields, metadata behavior, widget behavior, multi-chart handling, and label replacement. It is a complete operational guide.
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 description coverage, the description adds significant semantic value: metadata merge, tag replacement, widget creation vs. update, and the widgetEventId disambiguation are all explained. The widget parameter's pointer to the `query` tool shape is especially helpful.
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 'Update an existing event' — a specific verb+resource statement that clearly distinguishes this from sibling tools like create_event and list_events. It unambiguously states the tool's function and 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?
It explicitly instructs to use list_events to find the event ID first and explains that only fields needing changes should be provided. This gives clear when-to-use guidance and sets expectations for partial updates.
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. 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). | |
| 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 convey readOnly=false, openWorld=true, and destructive=false; the description adds substantial behavioral detail beyond that: wholesale replacement semantics for destinations and widgets, 'a singular `widget` key is silently ignored,' the deprecated `context` alias with 'never send both,' and 'schedule.mode NOW converts to on-demand ACTIVE and sends immediately.' 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?
Although long, the description is front-loaded with purpose, then prerequisites, then safety-critical DANGER ZONE guidance, then parameter semantics, and ends with concrete examples. Every sentence carries distinct operational value, and the length is 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?
For a 10-parameter mutation tool with nested objects and no output schema, the description covers what is needed to invoke it correctly: prerequisites, confirmation requirements, forbidden operations, replacement-versus-patch semantics, and examples. The lack of a return-value note is a minor omission given the depth of invocation 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%, which sets a baseline of 3, but the description adds meaning the schema lacks: reportContext is a partial patch, 'empty conditionsCel clears the filter,' destinations 'must be non-empty; omit to leave it unchanged,' and widgets are always an array whereas the singular `widget` key is ignored. These are operationally crucial clarifications beyond the JSON 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 precise verb and resource: 'Apply declarative partial changes to one non-archived report.' It further distinguishes itself by noting 'Omitted fields are preserved' and 'Archived restore is forbidden in MCP,' which separates it from create/archive siblings without needing to open their schemas.
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: 'Call get_skill with skillId: "reports" first when changing DIGEST content, schedule, destinations, or reportContext.' It also provides clear when-not/confirmation instructions: a DANGER ZONE for schedule/widget/destination changes requiring user confirmation, while 'Pure pause plus metadata / reportContext edits can apply immediately.'
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?
The description discloses far more than annotations provide: upsert side effects, a possible initialization warning, declarative rules replacement semantics, sticky rule ids, immutable bqName, validation failure behavior ('nothing persisted unless validation passes'), success response shape, and the fact that it does not publish or refresh BigQuery. This is exceptionally transparent for a mutation 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?
The description is long but every sentence carries operational value, covering workflow, edge cases, and constraints. The purpose statement is front-loaded, followed by usage routing and only then detailed parameter semantics. The example concretely demonstrates the expected payload shape 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 tool's complexity, sparse annotations, and lack of an output schema, the description is remarkably complete. It explains prerequisites, success and warning responses, validation behavior, immutable fields, rule-id stickiness, and the scope of what the tool does and does not do. An agent has enough context to invoke it correctly in both simple and advanced scenarios.
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 parameter meaning beyond the schema: how virtualDimensionId maps to returned ids, when to omit rules, how to configure externalMetric with required discovery steps, BigQuery/S3 required fields, catalog-complete provider exceptions, and the datasource leftover echo rule. It transforms raw schema fields into actionable usage knowledge.
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: it 'Upserts the virtual dimension draft for an existing virtualDimensionId' and explains both the update and seed-from-published behaviors. It explicitly distinguishes itself from create_virtual_dimension_draft, so an agent can select it correctly without ambiguity.
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: use create_virtual_dimension_draft only for brand-new VDIMs, and call get_skill first for allocation shapes and workflow. It also provides a concrete discovery workflow for telemetry metrics via list_metrics and states what to omit or preserve, leaving no doubt about when this tool is appropriate.
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. Dates show when Glama detected each change.
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
Frequently Asked Questions
Claiming proves that you control a remote MCP connector. It does not move, proxy, or interrupt the server.
Open the connector listing, choose Claim ownership, and sign in to Glama.
Complete one verification method:
GitHub identity — fastest for official registry listings. For a namespace such as
io.github.alice/server, link the matching GitHub user, then choose Claim with GitHub. An organization namespace such asio.github.acme/serveralso needs that organization to have installed the Glama AI GitHub App and approved its permissions, because GitHub discloses organization membership only to apps it has installed. Use HTTP or DNS when it has not.HTTP challenge — works when you can deploy a public file. Generate a token, publish the exact JSON Glama shows at
/.well-known/glama.jsonon the same origin as the connector, then choose Check HTTP challenge.DNS challenge — works when you control DNS but cannot change the server. Generate a token, create the exact TXT record Glama shows, wait for it to propagate, then choose Check DNS challenge.
After verification, Glama sends a confirmation email and gives you access to listing details, thumbnails, health checks, and analytics. Keep the HTTP file or DNS record in place: Glama periodically checks it and ownership remains verified while the token is discoverable.
The HTTP ownership file has this structure:
{
"$schema": "https://glama.ai/mcp/schemas/connector.json",
"claim": "glama_claim_..."
}Claim tokens are opaque, stable, and bound to the signed-in Glama account. They contain no email address or other personal information. If Glama can no longer discover a verified HTTP or DNS token, it starts a seven-day grace period before removing claim-based access. Restore the same token during that period to keep ownership verified. Never publish an email address, Glama session token, GitHub token, or connector credential as ownership proof.
If verification fails, confirm that you copied the current token exactly. The HTTP file must be public, return valid JSON with a successful HTTP response, and stay on the connector's origin. DNS changes may need more time to propagate. A claim cannot transfer to a different origin or hostname: if the connector target changes, Glama starts the grace period and the new target must be claimed separately after the previous claim is released.
For a connector linked to the official MCP Registry, registry updates continue to replace its name, description, and URL by default. After claiming, open Manage connector and enable Use Glama listing details as the source of truth if edits made on Glama should be preserved. Categories and thumbnails are always managed on Glama; registry linkage and technical connection settings continue to sync.
Control your server's listing on Glama, including description and metadata
Access analytics and receive server usage reports
Get monitoring and health status updates for your server
Feature your server to boost visibility and reach more users
To improve your MCP server's ranking:
Claim ownership of the server listing
Complete the server profile with an accurate description and thumbnail
Provide a test profile so Glama can connect to and evaluate the server
Keep tool definitions clear and complete to earn a high Tool Definition Quality Score (TDQS)
Route real usage through the Glama Gateway; more recorded successful server uses also improve the ranking
For users:
Full audit trail – every tool call is logged with inputs and outputs for compliance and debugging
Granular tool control – enable or disable individual tools per connector to limit what your AI agents can do
Centralized credential management – store and rotate API keys and OAuth tokens in one place
Change alerts – get notified when a connector changes its schema, adds or removes tools, or updates tool definitions, so nothing breaks silently
For server owners:
Proven adoption – public usage metrics on your listing show real-world traction and build trust with prospective users
Tool-level analytics – see which tools are being used most, helping you prioritize development and documentation
Direct user feedback – users can report issues and suggest improvements through the listing, giving you a channel you would not have otherwise
The connector status is unhealthy when Glama is unable to successfully connect to the server. This can happen for several reasons:
The server is experiencing an outage
The URL of the server is wrong
Credentials required to access the server are missing or invalid
If you are the owner of this MCP connector and would like to make modifications to the listing, including providing test credentials for accessing the server, please contact support@glama.ai.
Discussions
No comments yet. Be the first to start the discussion!
Related MCP Connectors
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.
Connects AI assistants to CloudQuell multi-cloud and AI cost, savings, anomaly, and budget data.
Related MCP Servers
- AlicenseAqualityBmaintenanceKubernetes 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
- AlicenseAqualityBmaintenanceEnables analyzing AWS cloud costs through natural language queries, providing cost summaries, anomaly detection, idle resource identification, rightsizing recommendations, and tagging compliance via Claude.1041MIT
- 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
- AlicenseNot gradedqualityDmaintenanceEnables natural language analysis of AWS cloud costs, waste audits, and budget insights across multiple profiles and regions.MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.
TDQS
Tools are organized by resource (alerts, dashboards, reports, events, virtual dimensions) with distinct actions, so most are clearly separable. The main confusion risks are the three report-delivery side-effect tools (run_report_now, retry_report_execution, transfer_report_execution) and the generic get that spans five resource types, though detailed descriptions mitigate these.
The dominant verb_noun pattern (create_*, list_*, update_*, preview_*, get_*) is consistent and predictable across the set. Deviations like bare verbs query/search/get and the noun-only virtual_dimension_overlap_matrix are readable but break the otherwise uniform convention.
44 tools is heavy and exceeds the comfortable range, but the server covers a genuinely broad FinOps platform spanning querying, dashboards, reports, alerts, events, virtual dimensions, docs, skills, and suggestions. Each tool has a distinct job, though the sheer count makes agent navigation harder.
Core workflows are well covered: query → dashboard/report/alert/event, plus a full virtual-dimension draft lifecycle. Notable gaps include alerts being create-only with no update/delete, no deletes for dashboards/events/published virtual dimensions, and budget management limited to query/get with no create/update.