Ask a coding agent for the vega of a pricer and it does one of two things. It reads the code, recognises a formula, and states the textbook answer. Or it writes a small script that bumps the volatility and prices twice. Both are reasonable, and both answer a slightly different question from the one you asked.
Reading the source is an answer about the code
A Greek is a property of one execution at one input point: the constants that were in force, the branches taken, the values that reached each line. Source code describes all executions at once, and the parts that decide this one are often somewhere else: a configuration file, a default argument, a floor applied three calls away.
Here is a pricer an agent might be asked about:
# market_config.py
VOL_FLOOR = 0.25 # risk policy: never price below 25% vol
# desk_pricer.py
import aadc
from market_config import VOL_FLOOR
def call_price(S, K, r, vol, T):
vol = max(vol, VOL_FLOOR)
d1 = (aadc.math.log(S / K) + (r + 0.5 * vol * vol) * T) / (vol * aadc.math.sqrt(T))
d2 = d1 - vol * aadc.math.sqrt(T)
return S * aadc.math.cdf_normal(d1) - K * aadc.math.exp(-r * T) * aadc.math.cdf_normal(d2)
f = aadc.Functions()
f.start_recording(trace=True, trace_inputs_as_random=False)
spot, vol = aadc.idouble(100.0), aadc.idouble(0.20)
a_spot, a_vol = spot.mark_as_input(), vol.mark_as_input()
price = call_price(spot, 105.0, 0.03, vol, 1.0)
out = price.mark_as_output()
f.stop_recording()
ws = f.create_workspace()
ws.set_val(a_spot, 100.0); ws.set_val(a_vol, 0.20)
ws.forward(); ws.set_diff(out, 1.0); ws.reverse()
print("price", float(ws.val(out)), "delta", float(ws.diff(a_spot)), "vega", float(ws.diff(a_vol)))
An agent that reads call_price, recognises Black-Scholes, and evaluates the vega formula at the stated 20% volatility gets about 39.89 (39.893498130685984 for this contract). The pricer’s vega at this input is 0.0: the 20% was floored to 25% before it reached the formula. The source contains the floor; whether it binds depends on a value in another file and on the input.
Bumping is an answer about two runs
A bump does see the floor, because it runs the code. But it inherits the bump’s problems: a bump size to choose, noise in Monte Carlo, and no information about why the number is what it is. Here a central bump of 0.01 in volatility returns 0.0 and says nothing about the floor; a bump of 0.1 straddles it and returns 9.949274992978765, which is neither the vega at 20% nor the vega at 25%. The agent then has to explain a number it cannot trace.
Giving the agent the engine
aadc-mcp is a local MCP server. The agent calls tools instead of reading files, and the tools return what the AADC engine recorded when the pricer ran. These are the answers the tools returned on the script above, trimmed to the relevant lines:
open_session("desk_pricer.py") runs the script and keeps the recording process alive. The script printed price 9.121799457724094 delta 0.5198747929060461 vega 0.0.
ask_session(id, "branches order=margin") lists the decisions frozen at record time, closest to flipping first:
entry count kind op margin(first) where
#3 1 FROZEN < -0.04999999999999999 desk_pricer.py:5 in call_price ibool->bool x1
The floor bound, at desk_pricer.py:5, and the volatility was 0.05 away from the threshold. That is the explanation for the zero vega, with its location, and the agent did not have to infer it.
replay_session(id, "#0=101", "#25") moves the spot to 101 on the live recording. The recorded decision survives and the value is exact: 9.64961045969929, the same digits as pricing again.
replay_session(id, "#1=0.30", "#25") moves the volatility to 30%. The engine re-checks the floor, finds it flips, and withholds the value: FLIPPED #3 ... margin -0.04999999999999999 -> 0.04999999999999999 desk_pricer.py:5. The agent has nothing to quote and says so, instead of producing a plausible number from the wrong branch. (Pricing again at 30% gives 11.11165445631984.)
Every number and every file:line in those answers is the engine’s output. The agent’s job is to choose the questions and narrate the answers.
When this helps, and when it does not
The engine answers questions about one execution: what ran, what it depended on, what it would give at nearby inputs on the same path. It does not tell you the code is correct, and a record cannot say “this is the only path”. For that, the agent still has to read the code, and now it reads it with the record’s constants, decisions and margins in hand.
For an unmodified QuantLib script, trace_script records the run with no code changes and the same questions apply; the walkthrough in Ask your coding agent where your Greeks come from shows a recorded session on a European option.
Try it
python3 -m venv aadc-env && . aadc-env/bin/activate
pip install aadc-mcp
aadc-mcp --check
claude mcp add aadc -- "$VIRTUAL_ENV/bin/aadc-mcp"
The last line registers the server with Claude Code; other MCP clients take the same command in their own configuration. Then ask your agent to run the get_started prompt. The server also ships the validate_pricer and add_sensitivities prompts and the how-tos it reads.
AADC runs here as Community Edition: free for non-commercial and academic use; commercial use, including evaluation inside a company, needs a licence from MatLogica.
Implemented using AADC, a commercial adjoint AD compiler (matlogica.com).