Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

### Fixes

- **Past months' costs**: your AI assistant no longer reports past months as having no
cost data.

---

## October 26, 2026
Expand Down
11 changes: 11 additions & 0 deletions stacklet/mcp/assetdb/sql_info.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,23 @@ Many AssetDB tables are extremely large. Always use LIMIT and indexed column fil
| `cost_source` | Lookup table for cost sources | Very Small | Sources of ingested cost data |
| `account_id` | Lookup table for accounts | Small | Bridge between cost tables and account/provider detail |
| `resource_id` | Lookup table for resources | Medium | Bridge between cost tables and resource detail |
| `cloud_service` | Lookup table for services | Small | Bridge between cost tables and service/provider names |
| `cloud_region` | Lookup table for regions | Small | Bridge between cost tables and region names |

### **Special Considerations**

- Most table columns are commented. When investigating table structure, these are valuable.
- Foreign key constraints do not tell the whole story; there are many implicit foreign relations.

### **Cost Data**

- Do not filter on `current` to pick a month; filter on `date`. `current` marks each account's (`account_cost_monthly`) or resource's (`resource_cost_monthly`) own latest month, not the latest calendar month, and a resource that has stopped costing has no current row. Rows are never superseded, so filtering `current = true` silently drops past months, and any account or resource whose latest data is older.
- A month's row may be partial while its data is still arriving: compare `source_date` or `last_date` with the month end.
- Filter on `account_id.provider`, through the cost row's `account`, not by matching service names: every cost table has `account`, and a provider's services need not contain its name. Some deployments have non-cloud providers (e.g. `anthropic`, in preview, whose services are model names); `SELECT DISTINCT provider FROM account_id` lists them.
- `account_cost_monthly.account`, `.service` and `.region` are integer ids; join them to `account_id`, `cloud_service` and `cloud_region` on `id`. `resource_cost_monthly` has only `account` and `resource_id`, with no service or region.
- `resource_focus_cost` and `resource_cost_monthly` have `tags`, an array of `resource_tags.id`s: unnest and join them to see whose cost it is, even for resources that aren't in `resources`. They are the tags the cost source reported, so for cloud resources they can be empty (AWS legacy CUR) or have `user_`-prefixed keys (AWS CUR 2.0: `team` is `user_team`); `resources._tags` has the inventory tags. Only `resource_focus_cost` has `sku`: join it to `sku.id` for `sku_id`, which encodes the pricing dimensions.
- Anthropic (preview): the cost row's `resource_id` joins `resource_id.id`, whose `r_id` is a user (`user_…`, Claude Code usage included), an API key (`apikey_…`) or a service account (untagged). Users are tagged `anthropic:user_email`/`anthropic:user_name` (or `anthropic:claude_code_user` for users who've left), keys `anthropic:api_key_name`/`anthropic:owner_*`. `sku_id` is `model:token_type:context_window:service_tier:inference_geo:speed` (e.g. `…:200k-1M:…` for long context), or `web_search`. Some cost (Console usage, code execution, small shares) has no principal, so is only in account-level cost; resource rows sum to less.

### **Safe Querying Process**

1. **Query table sizes:**
Expand Down
Loading