Thorlabs Optical Power Meter — MCP Server
Let an AI agent read laser power, log stability and warm-up drift, set the wavelength correction, averaging and range, and dark-zero the sensor on Thorlabs optical power meters through their documented SCPI command set over USB (USBTMC / VISA).
| Package | labmcp-thorlabs-pm |
| Instruments | PM100D, PM100A, PM100USB, PM400 consoles with C-series photodiode and thermal sensors. PM101 and PM5020 use the same documented commands but are unverified (see Notes) |
| Interfaces | USB (USBTMC, via VISA) |
| Protocol | SCPI: PM100D Operation Manual 17654-D02 §6.4, PM100USB Operation Manual 19570-D02 §6.3, PM400 Operating Manual v1.1 §5.5, and Thorlabs' per-model SCPI command references |
| Status | 🧪 simulated: tested against a wire-level SCPI simulator, not yet verified on hardware. Report a hardware test |
Try it without hardware
uvx labmcp-thorlabs-pm --simulate --check
uvx labmcp-thorlabs-pm --simulate --option sim_sensor=S302C --check # thermal sensor head
The simulator is a PM100D with an S120C photodiode (or S302C thermopile) looking at a 1.234 mW, 632.8 nm HeNe laser. Setting the wrong wavelength on the photodiode gives a proportionally wrong reading, as on the real bench.
Connect your power meter
- Driver. Thorlabs meters enumerate as USB Test & Measurement Class (USBTMC) devices, vendor ID
0x1313.- Windows: the Thorlabs driver (TLPM / Thorlabs VISA) is not visible to PyVISA. Install the free NI-VISA runtime, then switch the meter to the NI-VISA driver with the Thorlabs Driver Switcher (installed with the Thorlabs Optical Power Monitor software), and start the server with
?backend=@ivi(see below). This is the route Thorlabs documents for PyVISA. - Linux: pyvisa-py talks USBTMC directly (installed with this package). Give your user access to the device, e.g.
/etc/udev/rules.d/99-thorlabs-pm.rules:
thenSUBSYSTEM=="usb", ATTR{idVendor}=="1313", MODE="0666"sudo udevadm control --reload-rulesand re-plug the meter. NI-VISA also works (?backend=@ivi). - macOS: pyvisa-py + libusb (
brew install libusb); NI-VISA is an alternative.
- Windows: the Thorlabs driver (TLPM / Thorlabs VISA) is not visible to PyVISA. Install the free NI-VISA runtime, then switch the meter to the NI-VISA driver with the Thorlabs Driver Switcher (installed with the Thorlabs Optical Power Monitor software), and start the server with
- Find the resource string:
uvx labmcp ports(orpython -m pyvisa info). It looks likeUSB0::0x1313::0x8078::P0012345::INSTR(the product ID depends on the model: PM100D0x8078, PM100USB0x8072). - Test the connection:
uvx labmcp-thorlabs-pm --address USB0::0x1313::0x8078::P0012345::INSTR --check uvx labmcp-thorlabs-pm --address "visa://USB0::0x1313::0x8078::P0012345::INSTR?backend=@ivi" --check # NI-VISA
For the second input of a PM5020 add--option channel=2.
Add to your MCP client
Claude Code
claude mcp add power-meter -- uvx labmcp-thorlabs-pm --address USB0::0x1313::0x8078::P0012345::INSTR
Claude Desktop / Cursor / Windsurf (claude_desktop_config.json, .cursor/mcp.json, …)
{
"mcpServers": {
"power-meter": {
"command": "uvx",
"args": ["labmcp-thorlabs-pm", "--address", "USB0::0x1313::0x8078::P0012345::INSTR"]
}
}
}
Add --read-only to allow readings but block wavelength, averaging, range and zero changes. For other clients, generate the snippet with uvx labmcp config thorlabs-pm --address <resource> --client vscode (also cursor, codex, claude-code).
Tools
| Tool | Kind | Description |
|---|---|---|
get_command_log |
👁 read | Return the most recent raw commands sent to / replies received from the instrument (newest last). Useful for debugging and for recording what was done. |
get_connection_info |
👁 read | Report which instrument is connected (identity, address, simulated or real), whether the server is read-only, and the active safety limits. Call this first. |
get_device_info |
👁 read | Identify the meter console and the attached sensor head (model, serial, type, wavelength range, power ranges), and report the present wavelength, averaging, range and zero value. |
log_power_series |
👁 read | Log a series of power readings to characterise laser stability, warm-up or drift. Returns mean, stdev, min/max, RMS and peak-to-peak stability (%) and drift (%/min), plus the readings (downsampled to max_points; use save_path to keep all of them). |
read_power |
👁 read | Read the optical power once (in W, plus dBm and a formatted string). The reading uses the configured correction wavelength and averaging; set the wavelength first. |
read_sensor_temperature |
👁 read | Read the sensor head temperature in °C (thermal sensors and other heads with a built-in temperature sensor only). |
reconnect |
🛑 safety | Close and re-open the connection to the instrument (e.g. after it was power cycled or a cable was re-plugged). |
set_averaging |
🎛 control | Set how many samples the meter averages for each reading. More averaging lowers noise but slows each reading (PM100 series: ~3 ms per sample; PM400/PM101/PM5020: 1 ms). |
set_range |
🎛 control | Select auto-ranging, or a fixed power range that fits range_w (the meter picks the most sensitive range that can hold that power). A fixed range reports over-range if exceeded. |
set_wavelength |
🎛 control | Set the correction wavelength (nm) the meter uses to convert sensor signal to power. Must be within the sensor's calibrated range (see get_device_info); sent as a whole number of nm. |
zero_sensor |
🎛 control | Dark-zero the sensor (removes dark current / thermal offset). BEFORE calling, ask the user to block the beam and cover the sensor aperture completely, and wait for them to confirm: zeroing with light on the sensor corrupts all later readings. Takes a few seconds. |
get_connection_info, get_command_log and reconnect are built into every LabMCP server.
A power meter only measures, so this server has no hazard tools. Your laser is the hazard: wear the right eyewear and never exceed the sensor's rated power or power density.
Safety limits
| Limit | Default | Meaning |
|---|---|---|
max_series_duration_s |
600 s | Longest power-logging series an agent may start |
Override at launch: --limit max_series_duration_s=3600.
Example prompts
- "Set the meter to 1064 nm and tell me the power after the objective."
- "Log the laser power every 2 s for 10 minutes and report the RMS and peak-to-peak stability. Save the data to ~/data/laser_warmup.csv."
- "I've capped the sensor. Zero it, then I'll remove the cap and you read the power."
- "Measure the transmission of this ND filter: read the power without it, then again after I insert it, and give me the optical density."
- "Is the thermal sensor head still warming up? Read its temperature every minute until it's stable."
Notes
- Wavelength matters. Photodiode responsivity varies by a factor of several across the sensor's range, so a wrong wavelength setting gives a wrong power. The server sends whole nanometres (the PM400/PM101/PM5020 references specify an integer), so 632.8 nm is set as 633 nm. That changes the responsivity by far less than the sensor's calibration uncertainty.
- Units.
MEAS:POW?answers in the meter's configured unit (W or dBm). The driver readsSENS:POW:UNIT?first and always returns watts, so a front-panel unit change can't mislead the agent. - Out of range. The meter answers INFINITY / 9.9E37 when the signal exceeds a manually selected range. The server reports this as an error that suggests auto-ranging.
- Zeroing (
SENS:CORR:COLL:ZERO) runs in the background. The server polls…:ZERO:STAT?, checks the error queue, and returns the zero value (…:ZERO:MAGN?). Zero values are lost when the meter reboots. - Energy (pyroelectric) sensors are recognised but not supported: single-pulse energy measurement needs a trigger/fetch workflow that this server doesn't implement yet.
- Averaging is
SENS:AVER:COUN. One sample takes ~3 ms on the PM100 series and 1 ms on the PM400/PM101/PM5020. The reply timeout scales with the averaging count. - PM101 and PM5020: Thorlabs' command references for these list exactly the commands used here. The PM5020 adds a channel suffix (
SENS2,MEAS2,SYST:SENS2:IDN?), selected with--option channel=2. Neither has been tested yet, and PM5020 LAN/serial connections have not been tried. The PM100D's Windows TLPM DLL driver is not used; this server speaks SCPI only. - The simulator's error replies for out-of-range values (
-222,"Data out of range") use the standard SCPI code. The Thorlabs documents only list-113,"Undefined header"explicitly.
Hardware verification
| Model | Firmware | Interface | Verified by | Date |
|---|---|---|---|---|
| none yet: be the first |
Release files for labmcp-thorlabs-pm 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| labmcp_thorlabs_pm-0.1.0.tar.gz | 17.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| labmcp_thorlabs_pm-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.9 kB
Release files / labmcp_thorlabs_pm-0.1.0.tar.gz
| Download URL | labmcp_thorlabs_pm-0.1.0.tar.gz |
|---|---|
| Size | 17.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2fe69e859c89cfaa1cff5f04a148131ff1e29fe5e84a14eaf677e45c277b7ad
|
|
BLAKE2b-256 checksum How to use checksums |
fd342b30bdc2e1dc4584f483e17ec964e82538c314410b96ef777446137ddab3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / labmcp_thorlabs_pm-0.1.0-py3-none-any.whl
| Download URL | labmcp_thorlabs_pm-0.1.0-py3-none-any.whl |
|---|---|
| Size | 18.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5b8a7d275df3cddb5b9c135e4a7b4fb40d81023e2705e2a01429e1a0f14c0c27
|
|
BLAKE2b-256 checksum How to use checksums |
1010370e420077e3a78b4b2a574de37f04ca4d88640848ba5a50be757801d586
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log