Skip to the case study
Case study01 · OverviewEquity research · 2026

Stock verdicts the LLM can't fudge.

A fundamental analysis engine for Pakistan Stock Exchange companies. Specialist analyst agents read a company through a fixed five-layer framework and report to a supervisor, but every ratio, score and veto is computed by deterministic Python tools served over an MCP server. The LLM calls the tools and writes the prose. Python decides the verdict.

IndustryEquity research & fintech
RoleAI Engineer
MarketPakistan Stock Exchange

Math in Python

No figure is computed by the model, so the same inputs give the same verdict

18 MCP tools

One registry served over MCP and exportable as Claude tool-use specs

Auditable verdicts

Vetoes only cap a score, and every cap names the rule that fired

Case study02 · Solution & capabilities2 / 4
02
The solution

Let the model interpret, never calculate.

Language models are unreliable at arithmetic and drift from run to run, which is a poor fit for a buy or sell call. The engine splits the job: tools own every number and every rule, analyst agents own the reading of those numbers, and a supervisor assembles the result. Missing data is recorded as missing, never estimated.

1Validate the inputsRaw filing labels map to canonical keys and fail loudly if a field is wrong
2Compute through MCP toolsAnalyst agents call the tools for every ratio, then score and interpret
3Supervisor integratesEach agent reports back; the scorecard weights the layers and applies the vetoes

Key capabilities

What I built

MCP server with 18 tools

A FastMCP server over stdio exposes the whole compute layer to any MCP client.

  • One registry feeds MCP and Claude Messages-API tool specs
  • Tool descriptions carry guards, such as validate before compute

Tool calling with a safety contract

The output schema has no way to mark a number as computed by the model.

  • Every metric is tagged computed by tool or missing
  • The input gate flags each layer ready, and tool descriptions forbid computing before then

Multi-agent orchestration

A supervisor agent coordinates specialist analysts, one per layer of the framework.

  • Business, income, balance sheet and cash flow agents report back
  • Each hands off a confidence score and open questions

Veto engine with 23 rules

Hard-coded conditions cap a verdict that the composite score alone would allow.

  • Most restrictive cap wins, and a veto never upgrades
  • When layers disagree, cash flow wins

Never fabricate

Missing inputs stay missing, and the scoring degrades instead of guessing.

  • Absent layers re-normalize the composite weights
  • Four reconciliation ties check that statements agree

Schema-locked output

The final answer is one JSON block, rendered by a fixed dashboard.

  • JSON Schema 2020-12, no extra keys allowed
  • The dashboard changes by one injected data line only
Case study03 · Architecture & build3 / 4
03
Architecture

Four stages, with tools between the model and the math.

Python · MCP (FastMCP) · Claude tool use · JSON Schema · YAML · pytest

Architecture diagram in four stages. Ingestion: a PSX website, a broker API and a third-party API feed source adapters for JSON, CSV and XLSX, then clean and normalize, then validate inputs. Compute service: an MCP server and tools registry with compute_income, compute_balance, compute_cashflow, reconcile_all, derive_veto_signals and integrate_scorecard, called by income statement, balance sheet and cash flow agents, then reconcile and bundle. Analyst engine: layers L1 business, L2 income statement, L3 balance sheet, L4 cash flow and L5 integrated scorecard, ending in a final verdict from STRONG BUY to SELL. Persist and notify: a SQLite and Parquet database and a notification service that alerts the user when a run completes.

Open the diagram full size ↗

Built and tested
  • Stages 2 and 3: the MCP compute service and the L1 to L5 analyst engine
  • The input validation gate from stage 1, with a PSX line-item synonym map
  • Schema-locked output and the fixed dashboard
  • 82 passing tests, including a determinism check and a full veto truth table
Designed next
  • Batch processing: one Message Batches job per ticker, five sequential layer calls, stocks in parallel
  • Polling, retry and quarantine of results that fail the schema
  • Source adapters, SQLite and Parquet storage, and run-complete alerts

Technology stack

What it runs on
AI & orchestrationHow the model reaches the math
ClaudeMCP server (FastMCP)stdio transportTool callingSupervisor + analyst agentsAnthropic Message Batches (designed)
Compute coreDeterministic, no model in the loop
Python 3.11+Income, balance and cash flow modulesReconciliation checksScorecard and veto engine
Contracts & configWhat the model may and may not send
JSON Schema 2020-12jsonschemaYAML (weights, benchmarks)PSX line-item synonym map
Output & qualityA fixed view, and tests behind it
HTML + vanilla JS dashboardInline SVG chartspytest (82 tests)SQLite + Parquet (designed)

How it was built

Rules first, then tools
1FrameLayers, weights, vetoes2ComputePure Python, tested3Expose18 tools over MCP4OrchestrateAnalysts to supervisor

Challenges

Problem, then fix
The model passed interest_expense and the tools silently returned nullsA validate_inputs gate with a strict schema, unknown-key detection, sign coercion and a per-layer ready flag, and the tool descriptions forbid computing until a layer is ready
LLMs drift on arithmeticEvery ratio, CAGR, DuPont and free cash flow figure runs in Python, and the output schema can only mark a value as computed by tool or missing
Layers disagree, and a good composite can hide a bad cash flowCash flow wins ties, vetoes only cap, and the most restrictive of the score band and all caps sets the verdict
Most veto signals are judgment calls, not numbersThree of the 23 conditions are derived from the numbers. The analyst agents supply the rest as explicit booleans, and a veto fires only on an explicit true
Missing data invites a guessMissing inputs stay None, unevaluable reconciliation checks are skipped, and the composite re-normalizes its weights across the layers that exist
The report had to look the same every runThe final answer is a single schema-locked JSON block, rendered by a fixed dashboard where only one data line is injected
Case study04 · Results & impact4 / 4
04
Results

What the engine guarantees.

18MCP tools in one registry
23veto conditions encoded in Python
82passing tests, including a determinism check
5analyst layers, from business to scorecard

Impact

What it changes for an analyst
Reproducible

Same inputs, same verdict

The scorecard is plain Python, and a test runs it twice on identical input and expects identical output. The model only writes the explanation.

Auditable

Every number has a source

Each metric is tagged as computed by a tool or missing, and every verdict cap points to the rule that caused it.

Ready to scale

Built for a batch run

Stocks are independent, so the same tools and schemas are ready to fan out as one batch job per ticker, which is the next step.

Let the model interpret and let Python decide: tools over MCP for every number, analyst agents reporting to a supervisor, and a verdict you can audit.

Muhammad Abdullah · AI EngineerStart a similar project