Build1 publisher3 min readPublished
Returning the count from the engine fixed a question a small model missed twenty times out of twenty
An AWS waste scanner now exposes five read-only MCP tools that hand back totals, counts and breakdowns its engine computed. The findings themselves stay in a report file on disk, so the model has nothing to sum.
The Engineer · Build desk

What happened
- A read-only pass over one AWS account priced its unused resources at $6.90 a month.
- That pass ran 30 checks across 17 enabled regions and returned 120 findings, using only read calls.
- The MCP server speaks line-delimited JSON-RPC 2.0 over stdio and answers only initialize, tools/list and tools/call, which the article says is enough for a client to drive it.
- The estimate_savings tool takes a report path and a filter and answers with a count, monthly and annual cost, free and approximate counts, a costliest finding and a per-check breakdown.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint An agent driving this server cannot quote a competing dollar figure, because the response omits the row-level numbers it would need to compute one; all 120 rows stay in the report file.
- cost Skipping the MCP SDK moves three JSON-RPC methods and the version handshake onto the maintainer, who pays for it in review time every time the protocol moves.
- decision Anyone wiring an existing scanner to an agent has to choose what the tool hands back, because a response carrying rows reopens the addition the engine already did.
- capability An agent can now report a real zero with provenance, since a filter naming a check the report does not contain comes back with the match count at 0 and that check listed as absent.
The terminal table and the agent tools read the same report, and can print different numbers. According to the article, when they do, one figure is wrong in a way that survives review, because each looks sourced [23]. An end-to-end test holds the terminal table and the agent tools to the same number [1].
The server states the rule before any tool call. Its `instructions` field, served to the client on connect, reads: "zombiescan finds AWS resources nobody is using and prices them. Every tool is read-only; nothing here deletes anything. Run scan_account once, then ask estimate_savings for any total, count or breakdown of the report it writes -- do not add up findings yourself." [10] The instructions field is advisory; what binds is what the tools return: the costliest row is picked in code, the per-check and per-region sections are grouped in code, and the model quotes what came back [15].
The result behind the rule is a counting failure. One small model, asked how many of eleven ids were 10 or more, answered wrong in twenty calls out of twenty [6], and pushing the count into the engine makes the same question right every time, the author writes [7]. The model is not named. For 20-out-of-20 to say anything about your agent, three things would have to hold: the model is comparably small, the question makes it evaluate a predicate row by row, and the rows are in context for it to try. A model handed rows and asked how much they cost does the addition in the answer [24].
The totals are the least interesting output here. Thirty-one of the 120 findings carry a price [4], which leaves 89 at zero and matches the `free_count` field [20]. `estimate_savings` also reports `approximate_count` as 100 [14], so 100 of the 120 prices are estimates, and the single costliest finding is an ECR repository in us-east-1 at $0.52 a month [14]. One check, `ecr-stale-images`, accounts for $6.40 of the $6.90 monthly figure, about 93 percent [19]. The engine returns that qualifier as a field. The demo account's whole waste is $82.80 a year [3]. The size of the bill is beside the point.
Adoption cost is specific. There is no MCP SDK in the dependency list, the framing runs to about a hundred lines, and the stated reason is that a second package to keep current is a second thing to break [12]. You own the handshake in exchange. The article's advice is to answer `initialize` with the client's own `protocolVersion` when you recognise it, because a client that gets its version back proceeds and one that gets a different string negotiates again [13]. The permissions are the other line item: read-only access wide enough to describe resources in every region you scan, plus `ce:GetCostAndUsage` for the calibration step [18].
What to watch
- Whether the same twenty-out-of-twenty counting failure reproduces on a larger model with the eleven ids in context.
- Whether plan_cleanup stays read-only, given the instructions field promises nothing here deletes anything.
- Whether echoing the client's protocolVersion still works after the 2025-06-18 MCP revision is superseded.