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.
- ValidatedStrict schema, missing inputs recorded
- ComputedRatios, DuPont, FCF by MCP tools
- ScoredL1 to L4 analyst agents, each with a handoff
- IntegratedComposite score plus 23 veto rules
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
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.
Key capabilities
What I builtMCP 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
Four stages, with tools between the model and the math.
Python · MCP (FastMCP) · Claude tool use · JSON Schema · YAML · pytest
- 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
- 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 onHow it was built
Rules first, then toolsChallenges
Problem, then fixWhat the engine guarantees.
Impact
What it changes for an analystSame 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.
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.
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