MCP · one of four interfaces

The interface changes. The analysis does not.

EVA is reached four ways: the operator environment, the HTTP API, EVA Chat and the Model Context Protocol. MCP is an interface mechanism, not a second analytical implementation. For the same request, the computation, the model identity and the refusal state are the same whichever door it arrives through.

Real stdio subprocess33 approved EVA tools1,856 fields compared0 mismatches
CallerExternal clientIssues a request through the protocol.
→
InteroperabilityMCPGoverned 33-tool allowlist over local stdio.
→
BoundaryEVA tool dispatcherThe same validated service boundary used by EVA Chat.
→
AuthorityEVAPerforms the analytical computation and returns the evidence-bearing result.
MCP adds no analytical implementation. The adapter dispatches approved calls through the same validated boundary the operator environment uses, and wraps the returned EVA payload in a governance envelope.
Live verification

What was actually demonstrated.

Every figure below was produced by a run performed through a real MCP server subprocess and reproduced identically over the HTTP API, from the canonical payload.

Tool surface33Approved tools returned by live tools/list.
TransportstdioA real MCP server subprocess, not an in-process call.
Equivalence1,856Fields compared between direct EVA and MCP execution.
Mismatches0Not one field differed, wall-clock timestamps included. No exclusions required.
Actual EVA call over MCP

A mortgage payoff event, executed rather than narrated.

The demonstration used the US housing ecosystem and fired a borrower home-sale payoff event through the live MCP server, on the route named beneath the result.

Question represented by the call
What happens to the mortgage chain when the borrower sells the home and pays off the loan?

Tool: run_event_impact_with_inputs

MCP tool arguments
{ "ecosystem_id": "us_housing_v9", "event_code": "HOU_BORR_HOME_SALE_PAYOFF", "fire_date": "2026-05-05", "valuation_date": "2026-05-05", "inputs": { … canonical loan and conditions … } }
Loan, value change−$383,540.05
Servicing right, value change−$12,180.61
MBS pass-through, asset value change$0.00
Investor position extinguished−$427,728.52

The asset value change is zero because the lender receives par on the firing date; the investor position is a separate measure and is extinguished.

The loan and servicing calculations were returned with model/version identity, input/output hashes, run identity and source lineage in the EVA result.

One analysis, four ways in

The same request. Two interfaces. One identical result.

A borrower sells the house and pays off the loan. The canonical request was issued over the HTTP API and through a real MCP server subprocess. Both returned byte-identical results and the same run identifier. The panels below show that recorded run as each interface presents it.

Ecosystemus_housing_v9
EventHOU_BORR_HOME_SALE_PAYOFF
Fired2026-05-05
Assets4
Stakeholder rows27
Total asset delta−$395,720.66
Interface 1

Operator environment

The analyst fires the event and reads the stakeholder table.

StakeholderImpact
INVESTORMBS_PASS_THROUGH−427,728.52
BORROWERSHORT_LIEN+351,089.42
BORROWEROWNER_PROPERTY+175,111.02
REAL_ESTATE_AGENTCOMMISSION+28,000.02
HOMEOWNERS_INSURERPREMIUM_STREAM−23,026.38
GSE_FNMAGFEE_STRIP−16,225.32
SERVICERMSR_BASE−8,935.55

21 of 27 stakeholder positions valued · 6 not applicable · showing 7

Interface 2

EVA Chat

The same firing requested in conversation. The numbers are reported, not generated.

Fire the home sale payoff on us_housing_v9 and show me the asset deltas.

The engine fired HOU_BORR_HOME_SALE_PAYOFF on 2026-05-05 and returned the full impact set.

HOU_LOAN_CONV_30 $383,540 → $0, a change of −$383,540, VALUED.
HOU_MSR $12,181 → $0, a change of −$12,181, VALUED.
HOU_MBS_PT $427,729 unchanged. HOU_PROPERTY $560,000 unchanged.

The loan and MSR are extinguished by the full prepayment; the MBS pass-through and property values are unchanged.

Interface 3

HTTP API

The payload the operator environment is rendering — model identity, provenance and hashes intact.

POST /api/v1/firing/fire-event

"asset_id": "HOU_LOAN_CONV_30",
"valuation_model": "LoanLedgerCashflowModel",
"modifier_binding": "PrepaymentFullModifier",
"status": "VALUED",
"baseline_value": 383540.04688121786,
"impacted_value": 0.0,
"delta": -383540.04688121786,
"delta_pct": -1.0,
"model_version": "0.6.0",
"implementation_id":
  "mortgagekit.valuation.loan_pv
   .LoanCashflowDCFModel:v0.6",
"inputs_hash_impacted": "dfd990d12193c28e…",
"reason": "full prepayment: lender
  receives par on firing date"
Interface 4

MCP

A tool call from an external client over a real stdio subprocess. The result arrives inside a governance envelope.

tools/call → run_event_impact_with_inputs

"eva_governance": {
  "source": "EVA",
  "authority": "All numbers in
    eva_result are computed by EVA's
    engine. Numbers not present were
    not computed.",
  "narration_rules": "Report fields
    verbatim. Do not invent, estimate,
    or requalify."
},
"eva_result": {
  "engine": "engine10",
  "run_id": "engine10::d4922ecf…"
}
One run identifier · HTTP and MCPengine10::d4922ecf4d39294ffe7920d0a218a50c
1,856fields compared between direct EVA and MCP
0mismatches, wall-clock timestamps included
4 of 4per-asset deltas identical to the float
identicalinput and output hashes on every asset

The interfaces differ in what they render. They do not differ in what was computed. The operator environment draws a table, chat writes a sentence, the API returns the payload and MCP wraps it in a governance envelope — and the analysis beneath them is the same analysis.

A second result, from the chat surface. Chat sent a reduced request and returned a different run identifier. Firing the HTTP route with those same reduced arguments reproduces the chat identifier exactly — engine10::4f4e251e… on both. The run identifier is a function of the request, not of the interface. Change the request and the identity changes; change only the door and it does not.

Every figure, field name, model identifier and hash fragment above is taken verbatim from the recorded run. The operator-environment and chat panels are composed renderings of that run rather than captured screens.

Bounded tool surface

The tool surface is fixed. A caller cannot widen it.

The EVA MCP surface registers a fixed allowlist of 33 tools. Requests outside that surface are returned as structured refusals rather than being improvised by the adapter.

TOOL_NOT_ALLOWED

An excluded tool stays excluded.

A request for run_decision_policy was refused by name inside the governed response envelope.

status: ERROR
error_code: TOOL_NOT_ALLOWED
"run_decision_policy" is not in the EVA MCP allow-list
UNKNOWN_EVENT

Invalid analytical input remains an EVA refusal.

A bogus event code sent through an allowed tool produced EVA's own catalog error, carried across MCP without being reinterpreted.

status: ERROR
error_code: UNKNOWN_EVENT
EVT_DOES_NOT_EXIST_XYZ
Tool inventory

Thirty-three approved ways into EVA.

The surface covers analytical execution, explanation, discovery and glossary access. The adapter does not add a second analytical implementation.

Event analysis

run_event_impact
run_event_impact_with_inputs

Deterministic event consequence execution.

Decision & reaction

run_decision_impact
compute_reaction

Bounded decision consequence and payoff/minimax analysis.

Explain & discover

explain_run
list_ecosystems
list_personas
list_events
define_term

Read stored runs and discover the available analytical vocabulary.

The boundary in one sentence

The interface carries the request. EVA computes the result.

MCP is one of four ways in. The analytical result remains an EVA result, with its model identity, run identity, evidence and refusal state intact — identical to the result the same request returns through any other interface.

Verified path

external client
↓
MCP
↓
EVA tool dispatcher
↓
EVA
↓
evidence-bearing result