The address
https://mcp.zerotrustphysics.com/mcp
Open to any agent. There is no account and no sign-in.
Connect an agent
Claude
Open Customize, then Connectors, then Add custom connector. Enter the address. Where it asks about sign-in, choose No sign-in. Select Add. In a chat, open the + menu, choose Connectors, and turn it on.
Claude Code
claude mcp add --transport http zero-trust-physics https://mcp.zerotrustphysics.com/mcp
Any other agent
Add the address as a remote Model Context Protocol server with no authentication.
By hand
The address speaks the Model Context Protocol over HTTP: one POST, one JSON answer. Each request stands alone. There is no session and no stream.
curl -s https://mcp.zerotrustphysics.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
A script that uses Python’s built-in web client must name itself in a User-Agent header. Without one the network in front of the address answers 403.
Something to paste
Connect to https://mcp.zerotrustphysics.com/mcp and read what is there. Then
tell me what it is, and why it might matter to me.
The tools
Read what is served
- list_cards
- Every model that steps through time, each with one sentence and one word for how it did against its measurements.
- list_solvers
- Every field solver, the same way.
- describe_card
- One model in full: inputs, units, ranges, the names of its stops, how it was tested, and a worked request. With
"form": "short"it is one page. - describe_solver
- The same for one solver.
- find_model
- From a plain description of a real thing to the models that fit, with what each was tested on and how it did.
- cite
- How to cite the verifier, the paper, and the people whose measurements a model stands on, as text and BibTeX.
Ask
- verify_step
- One step of one model, with its cryptographic proof.
- run_trajectory
- A run under a changing load, with its cryptographic proof.
- solve
- One solution of a field solver, with its cryptographic proof.
- run_dataset
- A planned set of runs, or of solves, under one proof.
- validate_series
- A run, or a set of solves, compared with measurements you send. The answer is the error in numbers.
- find_flip
- The place where an outcome changes, with the run on each side and the proof of each.
Confirm a proof
- verify_tape
- Confirms a run’s proof. Send the request as it was sent and the proof it came back with.
- verify_solution
- The same for a solution.
- verify_dataset
- The same for a dataset.
- verify_receipt
- Confirms that a copy of an answer is byte for byte what was sent.
In an answer the proof of a run is named run_seal, of a solution solution_seal, and of a dataset dataset_seal. Each is 64 characters.
One question and its answer
An agent asks how hard a battery pack can be pushed for ten minutes. This is the run at 71.44 amps.
curl -s https://mcp.zerotrustphysics.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-o answer.json \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"run_trajectory","arguments":{"domain":"energy","card":"battery_sag","initial":{"soc":1.0},"dt_s":0.1,"segments":[{"steps":6000,"load":{"current_a":71.44}}],"record_every":6000}}}'
The answer is one block of text inside result.content. Its main parts:
"outcome": "completed"
"steps_landed": 6000
"final_state": { "t_s": 6.000000000000679e2, "soc": 4.587878787877244e-1,
"terminal_v": 3.600029381817893e1,
"cutoff_margin_v": 2.9381817893181505e-4, ... }
"run_seal": "6bd7e54b83c71da84eda74f3314416d619bbbce112186aa548085cb5a4ba0d34"
The pack holds for the ten minutes and ends 0.0003 volts above its cutoff.
The same question at 71.45 amps stops 0.2 seconds before the end:
"outcome": "refused"
"reject": "UndervoltageCutoff"
"reject_detail": "Physical Failure: the pack reached the 36.0 V cutoff
(UndervoltageCutoff). The run is sealed to step 5998. The state at step 5999
was refused and is not sealed."
"steps_landed": 5998
"run_seal": "68c3bc0e9f1469d239ec4e00795d03eaf58063d704c7535ad6f533222672a83e"
The pack is a generic 12-cell model, here to show what an answer looks like. It has not been compared with a measurement.
When a model stops
A model answers inside the range it was tested for. At the edge it stops and says why. One Panasonic cell was tested at one current. Asked for 5 amps, it answers:
Physical Failure: 'held_current_a' 5 A is above 2.90065 A, the inclusive
maximum of the cell_18650pf card (CurrentNotCompared). That state is not sealed.
A stop before anything is computed is one sentence and has no proof. A run that starts and stops part way keeps its proof up to the last step that landed.
Confirm a proof
By asking again
Send verify_tape the request and its run_seal:
{"request": { ...the run_trajectory request as sent... },
"run_seal": "6bd7e54b83c71da84eda74f3314416d619bbbce112186aa548085cb5a4ba0d34"}
The answer begins "holds": true. With one character of the proof changed it begins "holds": false, "cause": "run_seal".
With no engine
Three small programs confirm a proof from a saved answer. They need Python and nothing else.
git clone https://github.com/johnkruze/zero-trust-physics
python3 zero-trust-physics/verify_run.py answer.json
run_seal: holds 6bd7e54b83c71da84eda74f3314416d619bbbce112186aa548085cb5a4ba0d34
result: every proof holds
Keep the answer byte for byte. One added line break at the end of the file changes its last proof line.
The engine’s own source, to build it and get the same proofs: https://github.com/johnkruze/zero-trust-physics-engine
Limits
The address states these when an agent connects.
- Requests
- 300 a minute from one address, then HTTP 429 with Retry-After. 16 in flight at once and 4 from one address, then 429. A request body up to 4,194,304 bytes, then 413.
- A run
- Up to 1,000,000 steps and 10,000 recorded states.
- A dataset
- Up to 300 runs, 1,000,000 steps in all and 10,000 recorded states. It comes back in the answer and is stored nowhere.
- A set of solves
- Up to 64, and fewer of a slow solver: 5 of
step_flow_armaly, 15 oftokamak_dt_beam_target, 15 ofablator_slab, 30 ofatom_dft. - One thing at a time
- One address computes one thing at a time. A solve, a dataset and a run of more than 10,000 steps are long work, and one piece of long work runs at a time. When the address is busy the answer is “Too many concurrent physics tasks.” Nothing waits. Send it again.
- Time
- An answer is waited for 60 seconds. Long work is stopped there. Send a smaller request.
- A search
find_flipruns up to 64 runs, 1,000,000 steps in all or 64 solves, and starts no new run after 20 seconds.- In Claude
- Claude reads a tool’s answer up to about 150,000 characters. Ask for few recorded states: a large
record_every, or a readout.
If something goes wrong
- “Too many concurrent physics tasks.”
- The address was busy. Send it again.
- HTTP 429
- Wait the seconds named, then send again.
- HTTP 403 from a Python script
- Give the script a User-Agent header.
- HTTP 413
- The request is over 4,194,304 bytes.
- An answer that stops and says why
- The sentence names the limit. The model’s description gives its range.
More help: Support
Where the rest is
- The site
- https://zerotrustphysics.com
- For an agent
- https://zerotrustphysics.com/llms.txt
- Each real thing
- https://zerotrustphysics.com/things/
- Programs and source
- https://github.com/johnkruze/zero-trust-physics
John Kruze, Protocol Company · 13 October 2026