Repository navigation
fix(billing-cost-management-mcp-server): document the cost-explorer Tags filter format and hint on tag keys that cannot match - #4941
Merged
finklen-amazon merged 2 commits intoOct 30, 2026
Conversation
finklen-amazon
requested review from
a team,
Comusus,
Rahul-1404,
anniewn,
bhatia-di,
callnmm,
chittev,
johnwangwyx,
junaidgirkar,
mattkelsey,
shsrams,
somsubhro,
wangyuhere and
yasmeenh11
as code owners
October 29, 2026 00:13
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4941 +/- ##
=======================================
Coverage 93.76% 93.76%
=======================================
Files 1080 1080
Lines 94176 94230 +54
Branches 15312 15327 +15
=======================================
+ Hits 88303 88357 +54
Misses 3498 3498
Partials 2375 2375 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
anniewn
previously approved these changes
Oct 29, 2026
…ags filter format and hint on tag keys that cannot match getCostAndUsage and getDimensionValues add the user: prefix to user-defined tag keys themselves, so a Tags filter on "user:Environment" matches no cost: with Values it returns $0, and with ABSENT it returns every cost, both with no error. A TAG group_by label such as "Environment$Dev" copied into Values also matches nothing. The description gains a "Tags leaves" section for getCostAndUsage and getDimensionValues filters: no user: prefix (tags AWS applies keep aws:), keys and values are case-sensitive, the group_by label is not a filter value, Values [""] matches the same costs as ABSENT, and a wrong key is silent. getCostAndUsage and getDimensionValues add a hint to a successful response when a Tags key starts with user:, a key or value is a group_by label, or both. The hint allows for a key that really starts with user:. Error responses keep the recovery hint the error mapping adds, and a filter the hint check cannot read still goes to the API's own validation. The forecast and resource-level operations get no hint: a forecast filtered on a user: key still returned a forecast, so the hint would be wrong there, and the resource-level operation was not checked.
finklen-amazon
force-pushed
the
fix/bcm-ce-dimension-tag-docs
branch
from
October 30, 2026 01:47
b0dfe67 to
b97db0e
Compare
…-tag-docs # Conflicts: # src/billing-cost-management-mcp-server/tests/tools/test_cost_explorer_tools.py
anniewn
approved these changes
Oct 30, 2026
yasmeenh11
approved these changes
Oct 30, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Oct 30, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Oct 30, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Oct 30, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Oct 30, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Oct 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes
Summary
Changes
The
cost-explorerdescription showed a Tags filter example but never said what a tag key or value must look like. Three common mistakes are accepted with no error and return a wrong answer:user:key prefix. Billing exports name user-defined tagsuser:<key>, but Cost Explorer adds that prefix to a filter key itself. So{"Tags": {"Key": "user:Environment", "Values": ["Dev"]}}matches nothing and returns $0. WithABSENTthe same key matches every cost and returns the whole bill.Environment$Dev(andEnvironment$for costs without the key). Filtering onEnvironment$Devmatches nothing.Description: a new "Tags leaves" section, for getCostAndUsage and getDimensionValues filters, states:
user:prefix; tags AWS applies itself keepaws:;ABSENTcovers the<key>$group;"Values": [""]matches the same costs asABSENT(an empty list is rejected);Values, every cost withABSENT; copy the key from getTagsOrValues.Response hint: docs alone do not stop a model that copied
user:Environmentfrom an export, so getCostAndUsage and getDimensionValues add ahintto a successful response when a Tags key starts withuser:, or a key or value is a group_by label. The hint names the fix (Use 'Environment',Filter on 'Dev' instead, orUse ABSENT for the Environment$ group). It does not reject the request: a resource can carry a tag key that really starts withuser:, and the hint says so.Scope and safety of the hint:
user:Environmentstill returned a forecast, so "matches nothing" would be wrong there. The resource-level operation was not checked live.User experience
Before: a model filters on
user:EnvironmentwithABSENT, gets the whole bill back as "untagged spend", and reports it. Or it filters onuser:Environment=Dev, or onEnvironment$Dev, gets $0, and reports no spend.After: the description gives the key and value rules. If the model still sends one of those filters, the response says why it matched nothing (or everything) and what to send instead.
Testing
Run from
src/billing-cost-management-mcp-server:uv run --frozen pytest: 2720 passed.ruff check,ruff format --check,pyrighton the changed files: clean (0 errors).tests/tools/test_cost_explorer_tools.py: 41. 36 fail onmain; the other 5 are regression guards that pass there:user:keys (any case) insideAnd/Or/Not, group_by labels as value or key (withABSENT, only the key is suggested), the<key>$label, and auser:key with a label key or value;tests/tools/passes on each merge.Live, read-only, against a real account. The description was read as served over stdio (
list_tools), and calls went through the server (getCostAndUsage, one month, MONTHLY):user:prefix.Environment$<value>andEnvironment$labels.Environment=[Dev]returns non-zero cost, equal to the group line.user:Environment=[Dev]returns $0.EnvironmentwithABSENTequals theEnvironment$group line.user:EnvironmentwithABSENTequals the unfiltered total.Environment=[""]equalsEnvironmentwithABSENT.Environment=[Environment$Dev]returns $0.CASE_INSENSITIVEis rejected with ValidationException ("Allowed MatchOptions:CASE_SENSITIVE, EQUALS, ABSENT").Not(user:Environment ABSENT)returns $0.aws:createdByfilter with its prefix returns cost.user:key returns no values, and the response carries the hint.main: the 7 description checks and the 2 hint checks. The final rewording of the hint passes the 24 checks that replay recorded responses; the unit tests cover the new wording.Merge conflict
Second update: merged
maininto the branch (merge commit, no force-push) after #4962 (reject an empty filter) and #4964 (reject group_by where Cost Explorer has no GroupBy) landed.tests/tools/test_cost_explorer_tools.py: fix(billing-cost-management-mcp-server): reject group_by where Cost Explorer has no GroupBy and document filter limits #4964 and this PR both appended tests at the end of the file. Kept both blocks, fix(billing-cost-management-mcp-server): reject group_by where Cost Explorer has no GroupBy and document filter limits #4964's first.tools/cost_explorer_tools.pymerged without conflicts. fix(billing-cost-management-mcp-server): reject group_by where Cost Explorer has no GroupBy and document filter limits #4964's group_by refusal and fix(billing-cost-management-mcp-server): reject an empty filter instead of dropping it #4962's empty-filter rejection return errors, so the tag hint (successful responses only) never touches them.The fix bytes are unchanged: every line this PR adds or removes relative to
mainis identical to the previous head. On the merged head:tests/tools/and the full suite pass, except onerecommendation_detailstest that fails the same way onmain.ruffandpyrightare clean.cost_explorer_tools.pyis covered.Earlier update: rebased onto
mainafter #4936 (refusebilling_view_arnwhere AWS ignores it). #4936 added a check just before thegetDimensionValuescall this PR wraps, and appended tests at the same spot. Both were kept, and the fix bytes were unchanged.Checklist
If your change doesn't seem to apply, please leave them unchecked.
Is this a breaking change? (Y/N) N
RFC issue number: N/A
Checklist:
Acknowledgment
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.