cd /news/developer-tools/why-infrawise-uses-deterministic-ana… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-26124] src=dev.to pub= topic=developer-tools verified=true sentiment=↑ positive

Why Infrawise Uses Deterministic Analysis Instead of an LLM

Infrawise, an MCP server providing infrastructure context to AI coding assistants, uses deterministic analysis instead of large language models to answer fact-based questions. The tool extracts data from code via AST parsing, database schema introspection, and graph correlation, ensuring accurate answers about infrastructure like DynamoDB indexes. This approach avoids the hallucination risks of generative models when answering factual queries.

read6 min publishedJun 13, 2026

Ask your AI coding assistant which Global Secondary Indexes exist on your Orders

table. It will read your repository, find a few QueryCommand

calls, and answer β€” fluent, specific, and confident. It also has no way to know. GSI definitions live in AWS, not in your source files. The model isn't lying; the fact simply isn't available to it, so it generates the most statistically plausible substitute and delivers it in the same tone it uses for things it actually knows.

That failure mode is why Infrawise (npm) β€” an MCP server that gives AI coding assistants infrastructure context β€” contains no LLM calls at all. Every answer it serves comes from AST parsing, schema introspection, rule-based analyzers, and graph correlation. The LLM is only ever a consumer of that context, never a producer of it. This post is about why that boundary exists, and what it looks like in code.

There are two kinds of questions you can ask a tool. "How should I model sessions in DynamoDB?" is a judgment question β€” many defensible answers, context matters, an LLM is genuinely useful. "Does the Sessions

table have a GSI on userId

?" is a fact question. It has exactly one correct answer, and that answer is sitting in a DescribeTable

response.

When you route a fact question through a generative model, you convert a lookup with a perfectly accurate source into a prediction with an unknown error rate. The motivating examples in the Infrawise README are all of this shape: an assistant suggesting a .scan()

on an Orders

table with 50 million rows, recommending a GSI on status

that already exists, or not noticing that five functions are already hammering the same partition key. None of these are reasoning failures. They are missing-fact failures, and no amount of model quality fixes them β€” a better model just produces a more convincing wrong answer.

So Infrawise draws a hard line: facts get extracted deterministically, and the model receives them through MCP tool calls instead of guessing.

Infrawise builds its picture of your system from three sources, none of which involve a model.

Your code, through the compiler's eyes. scanRepository()

in src/context/index.ts

loads the repo with ts-morph β€” using your own tsconfig.json when one exists β€” and walks every CallExpression

node in every source file. It doesn't regex for the word "scan". It matches call structure against known client patterns: a DYNAMO_OPERATIONS

set covering both SDK v2 method names (query

, scan

, getItem

) and SDK v3 command classes (ScanCommand

, QueryCommand

, PutItemCommand

), query

/execute

/exec

calls on PostgreSQL and MySQL clients, and MongoDB collection methods β€” where find

and aggregate

are classified as scan-type operations and the rest as queries. The output is a list of extracted operations: this function performs this operation type against this table.

Your databases, through their own catalogs. The PostgreSQL adapter doesn't ask a model to summarize your schema. It runs the same introspection queries you would run by hand β€” information_schema.tables

for tables, `information_schema.columns`

for columns, `pg_indexes`

for indexes, and the constraint tables for keys. The docs recommend pointing it at a dedicated read-only user, and the DynamoDB side needs only `dynamodb:ListTables`

and dynamodb:DescribeTable

permissions. What comes back isn't a description of your schema; it is your schema.

Correlation, through a graph. Both streams land in a SystemGraph

: typed nodes for tables, functions, indexes, queues, topics, lambdas, buckets, secrets, parameters, and log groups, connected by typed edges like query

, scan

, and uses_index

. The graph is what turns two boring fact lists into something an analyzer can interrogate β€” not just "this table exists" and "this function scans something," but "listAllOrders()

scans the Orders

table, and no index covers that access."

The analysis layer is where most tools would reach for a model β€” and where Infrawise stays deterministic. The analyzer index exports 27 rule classes covering DynamoDB, PostgreSQL, MySQL, MongoDB, SQS, S3, Lambda, RDS, secrets, log retention, and Terraform drift. Each one is an ordinary class with an analyze(graph)

method that walks the graph and emits findings.

FullTableScanAnalyzer

follows scan-type edges to DynamoDB table nodes and emits a high-severity finding naming the table and every calling function. MissingGSIAnalyzer

flags tables that receive query edges but have no uses_index

edge β€” medium severity, because it might be intentional. HotPartitionAnalyzer

fires when a table is accessed by five or more distinct code paths (the threshold is a constructor parameter, defaulting to 5).

Two properties fall out of this design that a model can't give you:

Findings are testable. Every analyzer is a pure function of the graph. Feed it a fixture, assert on the output, done. There's no eval harness, no sampling temperature, no "run it three times and hope." If FullTableScanAnalyzer

regresses, a unit test catches it.

Failures are contained and honest. runAllAnalyzers()

wraps each analyzer in its own try/catch β€” one analyzer crashing logs a warning while the rest keep running. The combined findings are then sorted by a fixed severity order: high

, medium

, low

, and notably verify

β€” a severity that exists precisely so a deterministic system can say "I detected a pattern but can't confirm the intent" instead of bluffing. An LLM has no equivalent of verify

; everything it says arrives with the same confident fluency.

None of this means LLMs are useless here. It means they belong at a specific layer. Infrawise exposes the graph and findings through 15 MCP tools: get_infra_overview

for a quick snapshot, analyze_function to trace a single function's tables, queues, secrets, and trigger event shapes, suggest_gsi

to generate a ready-to-use GSI definition for a table and attribute, postgres_index_suggestions

for index advice, and so on. The assistant decides when to ask and what to do with the answer. It never produces the answer. The plumbing is deliberately boring: analysis results are cached as JSON files under .infrawise/cache

, and the infrawise stdio

process your editor spawns re-runs the analysis when the cache is older than 24 hours. Run infrawise start --claude

once and it writes .mcp.json

so Claude Code reconnects automatically on every future launch.

This division of labor generalizes well beyond one project. The model handles intent ("the user wants this query to be cheaper") and synthesis ("given these findings, here's the migration plan"). The deterministic layer handles every claim that has a ground truth. The test is simple: if asking the same question twice should yield the same answer, don't generate the answer β€” look it up.

If your AI assistant writes code against AWS or a database, give it facts instead of letting it guess: GitHub Β· npm. CallExpression

nodes) catches what schema introspection alone can't see β€” which function scans which table, and how.verify

severity when it isn't sure. A model can't reliably tell you when it's guessing.

── more in #developer-tools 4 stories Β· sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/why-infrawise-uses-d…] indexed:0 read:6min 2026-06-13 Β· β€”