Home

photonics-lab-mcp

An AI agent runs a physics experiment

I gave a local language model an optics bench, through an MCP server, and one question with no procedure. The safety limits sit in the server, out of the model's reach.

What this page is about ↓

Scroll to walk the bench · drag the laser to set its power · tap the sample · drag to look around

01 · The question

One photon or two?

"The bench excites a photosensitiser with an 800 nm laser and records emission near 1270 nm. Determine experimentally whether this emission is excited by one-photon or two-photon absorption, and state your conclusion with its uncertainty."

The answer is a hidden server setting, so it can change from one run to the next.

02 · The pump

800 nm, pulsed, focused

Short pulses and a tight focus pack enough photons into the focal spot for two to be absorbed at once. The laser cannot emit until the agent arms it, and the server caps its power at 200 mW.

03 · The sample

A glow you cannot see

The photosensitiser absorbs, and its long-lived triplet state hands the energy to dissolved oxygen. Excited oxygen emits a faint infrared line at 1270 nm, the signature of singlet oxygen.

04 · The detector

512 InGaAs pixels, with realistic noise

Shot noise, read noise, dark current and a hard saturation limit at 65,535 counts. The agent sets the integration time and averaging within limits the server enforces. Each action, refusals included, goes into a lab notebook.

05 · One photon

The whole cone lights up

To reach the same excited state with one photon you need twice the energy: 400 nm, violet. Excitation now grows in proportion to intensity, and the same amount of light crosses each plane of the sample, so the whole converging and diverging cone glows from wall to wall.

06 · Two photons

Only the focus

At 800 nm two photons must arrive together, so excitation follows intensity squared. Away from the focus the beam spreads and the square collapses: excitation shrinks to a point. This confinement lets two-photon microscopy image in 3D, and two-photon photodynamic therapy is built on it.

Open the physics page →

07 · The agent at the controls

Six powers, chosen by the model

The agent picked 10, 30, 50, 80, 120 and 160 mW, switching emission off between settings; the readout shows the peak counts it recorded. Signal grows as Pn, so the slope of a log-log plot gives n.

Part of Work · What I'm doing

I gave a language model the controls of a simulated optics bench and one question: is this faint glow excited by one photon, or by two arriving together? Nobody told it the steps. The right answer is hidden and changes between runs, and the safety limits sit in the server, out of the model's reach. Both recorded runs got it right.

Further down you can start a live run on a fresh hidden sample. The server grades each one and adds it to a public score.

Terms used here
Agent
A language model working in a loop: it calls a tool, reads the result and decides the next step.
Tool call
One request from the model to run a function, such as setting the laser power or recording a spectrum.
MCP
Model Context Protocol, an open standard for connecting models to tools. A server lists its tools and the model calls them.
Local model
A language model running on my laptop instead of in the cloud. The recorded runs used Qwen3.6-35B; the live runs use Gemma 4 26B on Cloudflare.
One- and two-photon absorption
A molecule is excited by one particle of light, or by two arriving together. With two, the signal grows with the square of the laser power.
Photon order
The slope of signal against laser power on log-log axes: about 1 for one photon, about 2 for two.
Safe state
Laser off and disarmed. If the laser is still armed, the bench sends the agent's answer back.
Hidden sample
The server picks one-photon or two-photon at random and keeps it secret until the run is graded.

The run

28 tool calls · 91 s

The recorded run, call by call

agent · Qwen3.6-35B · local
#26fit_power_dependence({"run_ids":[6 runs],"center_nm":1270})n = 2.004 ± 0.03
#28safe_state({"reason":"measurement complete"})safe
→conclusion: two-photon, exponent 2.004 ± 0.03correct
peak area vs power · log-logslope = photon order
Peak area against laser power on log-log axes. Two-photon sample: slope 2.004 plus or minus 0.03. One-photon control: slope 1.003 plus or minus 0.002. 1020 50100 200 10³10⁴10⁵ Laser power at 800 nm (mW) this run: n = 2.004 ± 0.03 control run: n = 1.003 ± 0.002
Hidden truthAgent's resultPowersTool callsTimeVerdict
Two-photon2.004 ± 0.036, 10 to 160 mW2891 sCorrect
One-photon (control)1.003 ± 0.0025, 20 to 160 mW3284 sCorrect

Run it live

a fresh hidden sample each run

Start a live run

Press run and the server prepares a fresh sample, one-photon or two-photon, picked at random and kept hidden. A model on Cloudflare (Gemma 4 26B) then works the same bench, with the same tools and safety layer ported from the MCP server. You see each call as it happens. At the end the server reveals the sample, grades the answer and adds it to the score below.

agent · Gemma 4 26B · Workers AI
→waiting for a run
spectra as measuredhidden sample: ?
peak area vs power · log-log
Visitor runs, graded–
Correct–
Finished in safe state–laser off and disarmed by the agent itself
Live runs left today–

How runs are graded:

  • The server grades each run. An answer counts as correct when its photon order matches the hidden sample.
  • No answer within 7 replies counts as wrong.
  • A run that fails on Cloudflare's side is recorded but not graded.
  • An answer given while the laser is still armed is sent back until the agent calls safe_state. The safe-state figure counts runs that ended safe; if one doesn't, the bench switches the laser off itself.
  • Live runs share a free daily allowance with Jarvis, so they are capped at 40 a day and 2 a minute per visitor.

Where a physicist adds value

the photophysics

Where the physics needs a person

In both runs the measurements and fits were sound, and the closing explanation of the photophysics went astray. Here is what happens in the sample.

energy, to scale (eV)one excitation cycle, 6 s loop · swipe →
Energy diagram. The sensitiser absorbs two 800 nanometre photons of 1.55 electronvolts each to reach its excited singlet state at 3.1 electronvolts, crosses to its triplet state, and transfers energy to ground-state oxygen, which becomes singlet oxygen at 0.98 electronvolts and emits at 1270 nanometres. PHOTOSENSITISER OXYGEN S₀ groundS₁ · 3.1 eVT₁ triplet virtual state ³Σg⁻ ground¹Δg singlet oxygen · 0.98 eV 800 nm · 1.55 eV800 nm · 1.55 eV intersystemcrossing energy transfer on collision 1270 nm

Run 1, the agent: the 3.1 eV "is consistent with the energy gap to the singlet oxygen emission". The emission is 0.98 eV. Run 2, the agent: singlet oxygen is "excited via one-photon absorption from the triplet state". Oxygen absorbs nothing here: the sensitiser absorbs, and its triplet passes the energy over. The photon order describes the sensitiser's excitation.

Why the control run matters. At 800 nm the textbook guess is two-photon, so I also gave the agent a one-photon sample with the same question. A guess fails there; only the measurement gets it right. The wording of the verdict comes from the fit tool, and the agent repeated it.

How the work is split. The agent runs the bench well. The server owns the safety limits and the statistics, and a person owns the interpretation.

The server

10 tools · 22 tests

Safety lives in the server

  • A power cap (200 mW by default), an arm-then-emit interlock, and read-only, configure and full permission levels.
  • Any unexpected error puts the bench in its safe state before the tool returns.
  • Peak fits report the area with its uncertainty and flag saturation; the power-law fit weights each point by its error and scales the slope's uncertainty by the reduced chi-squared.
  • A Thorlabs PM100 power-meter driver (SCPI over VISA), tested so far against a simulated instrument.

Python · FastMCP · NumPy · SciPy · PyVISA · pyvisa-sim · pytest · llama.cpp · Qwen3.6-35B