Skip to content

Commit 2cf9cfa

Browse files
authored
feat(billing-cost-management-mcp-server): offload large sp-explorer offering results to session SQL (#4713)
describe_savings_plans_offerings and describe_savings_plans_offering_rates returned the whole offering catalog inline, which can exceed 60k tokens for a single call. Route both through convert_response_if_needed so a result above the shared threshold is stored as one row per offering or rate in a session SQLite table, queryable with the session-sql tool. Small results are returned inline unchanged, and pagination metadata (has_more, next_token) is carried on the offloaded response. Register both operation names with the generic records converter, add inline, mocked-offload, and real-offload tests, and note the behavior in the tool description, README, and CHANGELOG.
1 parent eb17525 commit 2cf9cfa

5 files changed

Lines changed: 364 additions & 6 deletions

File tree

‎src/billing-cost-management-mcp-server/CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424
- Added an `order_by` passthrough to the Cost Optimization Hub `list_recommendations` operation, forwarding the COH `orderBy` structure (`{"dimension": ..., "order": "Asc"|"Desc"}`) to sort recommendations server-side by any supported dimension.
2525
- Added a `ranking_mode='performance'` option to the Cost Optimization Hub `list_efficiency_metrics` operation for "most efficient / top / worst performer" rankings of accounts or Regions. It fetches the highest-spend groups first, drops idle/no-score groups, then ranks by efficiency score — either the Pareto "vital few" (highest-spend groups making up ~80% of total spend) when the full set is retrieved, or the fetched top spenders (with a `ranking_focus` disclosure that only the top spenders were analyzed) when the fetch hits its page cap. Requires `group_by`; the `order_by` direction selects best-first vs. worst-first.
2626
- Added `material_spend` and `material_spend_fraction` to the Cost Optimization Hub `list_efficiency_metrics` `ranking_focus` disclosure (`ranking_mode='performance'`), reporting the kept groups' combined spend and their actual cumulative share of total spend so callers state the real share instead of the ~80% selection cutoff. The fraction is `null` when the fetch hits its page cap (total spend then covers only the fetched top spenders, not the whole org).
27+
- Added session SQL offload to the `sp-explorer` tool's offering operations (`DescribeSavingsPlansOfferings`, `DescribeSavingsPlansOfferingRates`): a result above the shared size threshold is stored as one row per offering or rate in a session SQLite table (queried with the `session-sql` tool) instead of returned inline, while a small result is returned inline unchanged
2728
- Added a `list_enrollment_statuses` operation to the `cost-optimization-hub` tool (`ListEnrollmentStatuses`), returning the calling account's Cost Optimization Hub enrollment status (`Active`/`Inactive`) with created and last-updated timestamps, or that of the account given in the optional `account_id`. An account with no enrollment record (never enrolled) is reported as `Inactive` instead of an empty list.
2829
- Added a `get_preferences` operation to the `cost-optimization-hub` tool (`GetPreferences`), returning the calling account's savings estimation mode, member account discount visibility, and preferred commitment (term and payment option).
2930

‎src/billing-cost-management-mcp-server/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ MCP server for accessing AWS Billing and Cost Management capabilities.
2727
- **Reserved Instance planning**: Analyze RI coverage and receive purchase recommendations
2828
- **Savings Plans performance**: Analyze how much eligible spend existing plans cover and how much of their commitment is consumed over a lookback window
2929
- **Savings Plans inventory**: Describe the plans an account owns with their state, term, payment option, commitment, and expiry, including the queued, returned, and payment-failed plans that Cost Explorer does not report
30-
- **Savings Plans rates and offerings**: Look up the rates locked in on plans already owned, and the offerings available to purchase with their rates, to compare terms and payment options against real numbers
30+
- **Savings Plans rates and offerings**: Look up the rates locked in on plans already owned, and the offerings available to purchase with their rates, to compare terms and payment options against real numbers. Large offering and offering-rate results are offloaded to session SQL (queryable with the `session-sql` tool) to save tokens
3131
- **Savings Plans recommendations**: Get personalized purchase recommendations based on usage patterns, the hourly data-points behind a recommendation, and the history of when recommendations were generated
3232
- **Savings Plans purchase analysis**: Run Purchase Analyzer what-if analyses — maximum savings, a specific commitment, or a target average coverage — and retrieve the projected cost, coverage, and utilization once an analysis completes
3333

‎src/billing-cost-management-mcp-server/awslabs/billing_cost_management_mcp_server/tools/sp_explorer_tools.py‎

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
paginate_aws_response,
2222
)
2323
from ..utilities.logging_utils import get_context_logger
24+
from ..utilities.sql_utils import convert_response_if_needed
2425
from fastmcp import Context, FastMCP
2526
from typing import Any, Dict, List, Optional
2627

@@ -59,7 +60,13 @@
5960
Tags come back on each plan in describe_savings_plans, so there is no separate tag operation.
6061
6162
IMPORTANT: the vocabulary here differs from Cost Explorer's, so values carried over from a Cost
62-
Explorer response or recommendation will be rejected. See the individual parameter descriptions.""",
63+
Explorer response or recommendation will be rejected. See the individual parameter descriptions.
64+
65+
The offering catalog is large, so a big describe_savings_plans_offerings or
66+
describe_savings_plans_offering_rates result is automatically offloaded to session SQL to save
67+
tokens: the response carries data_stored=True and a table_name (one row per offering or rate)
68+
instead of an inline searchResults list, and the rows are queried with the session-sql tool. A
69+
small result is returned inline unchanged.""",
6370
)
6471
async def sp_explorer(
6572
ctx: Context,
@@ -439,11 +446,19 @@ async def describe_savings_plans_offerings(
439446
max_pages=max_pages,
440447
)
441448

442-
return format_response(
443-
'success',
449+
# A large offering catalog is offloaded to session SQL (one row per
450+
# offering) instead of returned inline; a small result passes through
451+
# unchanged. An offloaded table is queried with the session-sql tool.
452+
converted = await convert_response_if_needed(
453+
ctx,
444454
{'searchResults': all_offerings, 'pagination': pagination_metadata},
455+
'sp_explorer_describe_savings_plans_offerings',
456+
pagination_token_key='nextToken',
457+
pagination=pagination_metadata,
445458
)
446459

460+
return format_response('success', converted)
461+
447462
except Exception as e:
448463
# Use shared error handler for consistent error reporting
449464
return await handle_aws_error(ctx, e, 'describe_savings_plans_offerings', 'Savings Plans')
@@ -529,11 +544,19 @@ async def describe_savings_plans_offering_rates(
529544
max_pages=max_pages,
530545
)
531546

532-
return format_response(
533-
'success',
547+
# A large offering-rate result is offloaded to session SQL (one row per
548+
# rate) instead of returned inline; a small result passes through
549+
# unchanged. An offloaded table is queried with the session-sql tool.
550+
converted = await convert_response_if_needed(
551+
ctx,
534552
{'searchResults': all_rates, 'pagination': pagination_metadata},
553+
'sp_explorer_describe_savings_plans_offering_rates',
554+
pagination_token_key='nextToken',
555+
pagination=pagination_metadata,
535556
)
536557

558+
return format_response('success', converted)
559+
537560
except Exception as e:
538561
# Use shared error handler for consistent error reporting
539562
return await handle_aws_error(

‎src/billing-cost-management-mcp-server/awslabs/billing_cost_management_mcp_server/utilities/sql_utils.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ def _get_specialized_converter(operation_name: str) -> Optional[str]:
404404
'cost_optimization_hub_list_efficiency_metrics': 'coh_efficiency_metrics',
405405
'budget_actions': 'records',
406406
'budget_notifications': 'records',
407+
'sp_explorer_describe_savings_plans_offerings': 'records',
408+
'sp_explorer_describe_savings_plans_offering_rates': 'records',
407409
}
408410

409411
if operation_name in converters:

0 commit comments

Comments
 (0)