Agents and the MCP server¶
photonhub-mcp serves PhotonHub's local tools to any MCP client over stdio.
The data and compute stay on your machine. Write a scene in the Python SDK,
then use the tools to check a spec, estimate a run, run it locally, and inspect
the result. The tools do not author or change a scene.
Install¶
Install the MCP extra in the environment your client will use:
Running a simulation needs the solver.
estimate and the Python validation stages need no solver. When installed,
capabilities probes the local solver with phsolver info and
phsolver --capabilities. The default validate_spec(engine=True) may run
phsolver --capabilities before phsolver validate.
validate_spec(engine=False) starts no solver process. See Tools
for the local process behavior. Without a solver, validation can still check
the Python stages, but its answer is not authoritative and says so.
Configure a client¶
Add a server entry to your MCP client's configuration. The result directory is
optional; without it, call open_result when you need an existing result.
If the client does not inherit the activated environment, set command to
<venv>/bin/photonhub-mcp. On Windows, use
<venv>\Scripts\photonhub-mcp.exe. In JSON, escape the backslashes:
The safe first call¶
Call capabilities first. It is read-only, local, and makes no network call.
Check solver.found, devices, cpu_only_features, and safety in its
answer. A missing solver does not prevent the other checks.
Next, call validate_spec with a wire JSON string or a path to a .json file.
This small wire spec follows the repository's dipole example:
{
"schema_version": "1.22.0-alpha.1",
"size_um": [4.0, 4.0, 4.0],
"grid": {"type": "uniform", "dl_um": 0.05},
"run": {"run_time_s": 8.0e-14, "courant": 0.99},
"background": {"permittivity": 1.0},
"structures": [],
"boundaries": {"x": "periodic", "y": "periodic", "z": "periodic"},
"sources": [{
"type": "point_dipole", "center_um": [2.0, 2.0, 2.0],
"polarization": "Ez", "amplitude": 1.0,
"source_time": {"type": "gaussian_pulse", "freq0_hz": 1.934e14,
"fwidth_hz": 4.0e13, "offset": 5.0, "phase": 0.0}
}],
"monitors": [{"type": "field_time", "name": "probe",
"center_um": [3.0, 2.0, 2.0], "fields": ["Ez"],
"interval_steps": 1}]
}
For your own scene, save the output of Simulation.to_wire_json(). Fix any
error issues before running. When the engine stage cannot run,
authoritative is false even if ok is true.
The agent loop¶
- Write the scene in Python with
import photonhub as phand savesim.to_wire_json(). - Call
validate_specuntilokis true. Use each issue'sfixhint. - Call
estimateto review the local size and CPU time range. - Call
run_simulationwithdevice: "cpu"to run on this machine. - Call
result_summary, thenconvergence_report, before quoting a number.
Report a physics number with its mesh (dl_um or cells per wavelength),
subpixel state and method, dispersion state, and boundary layers. Call a result
converged only when the convergence report shows it.
Tools¶
The inspection and checking tools read local data. open_result selects the
current local result. run_simulation runs a simulation locally.
capabilities starts the local solver with info and --capabilities.
validate_spec(engine=True) starts --capabilities when the spec needs that
probe, then starts validate. validate_spec(engine=False) starts no solver
process. run_simulation starts a local run and may query --capabilities
as part of that run.
| Tool | Use | Behavior |
|---|---|---|
open_result |
Load a result bundle as the current result. | Read-only local files |
describe_result |
Describe the current result and run metadata. | Read-only |
list_monitors |
List monitors and their axes. | Read-only |
field_stats |
Return statistics for a field cut plane. | Read-only |
get_spectrum |
Return a monitor's spectrum as numbers. | Read-only |
run_simulation |
Run a wire spec and load its result. | Runs the solver locally only |
get_schema |
Read the wire schema or one definition. | Read-only |
capabilities |
Inspect local solver, devices, and safety flags. | Read-only local solver probes |
validate_spec |
Check a wire spec and return itemized issues. | Read-only; optional local solver validation |
estimate |
Estimate grid, steps, memory, output, and CPU time. | Read-only |
result_summary |
Read compact result metadata and advisories. | Read-only |
convergence_report |
Read saved convergence evidence, if present. | Read-only |
The six new tools return structured outputs with these contracts and
arguments. result_dir=None means the result selected by open_result or
run_simulation; passing a directory does not replace that selection.
| Tool | Arguments and defaults | Contract | Main fields |
|---|---|---|---|
get_schema |
definition=None |
photonhub.schema/1 |
schema_version, schema_major, schema_sha256, definitions, definition, schema |
capabilities |
none | photonhub.capabilities/1 |
sdk_version, schema_version, schema_major, solver, devices, cpu_only_features, pinned_features, safety |
validate_spec |
spec, device="cpu", engine=True |
photonhub.validation/1 |
ok, authoritative, input_sha256, input_sha256_basis, stages, issues, counts, engine |
estimate |
spec, cpu_mcells_per_s_low=20.0, cpu_mcells_per_s_high=300.0, device="cpu" |
photonhub.estimate/1 |
cells_per_axis, num_cells, num_steps, dt_s, cell_steps, tcell_steps, memory_bytes, output_bytes, cpu_seconds, assumptions, notes |
result_summary |
result_dir=None, data_checks=True |
photonhub.result_summary/1 |
input_sha256, input_sha256_source, geometry_status, monitors, issues, counts, convergence |
convergence_report |
result_dir=None |
photonhub.convergence_report/1 |
found, path, relation, format, converged, metric_name, ladder, message |
result_summary(data_checks=False) skips advisory checks that read monitor
data. It still reports recorded metadata and can report setup advisories.
After construction and device checks, validate_spec.input_sha256 is the
SHA-256 of the wire input plus a trailing newline. Its
input_sha256_basis is canonical for CPU validation with engine=False,
when no solver is available for a CPU check, or when a CPU capability probe
fails. For GPU validation it is local-execution because the execution bytes need no solver
capability probe. With engine=True and a working solver, it is also
local-execution. Both fields are null if the spec cannot be hashed. For a
local run of the same scene and device with the same solver capabilities, a
local-execution digest equals
result_summary.input_sha256 from the result manifest's
provenance.input_sha256. If the manifest lacks that field,
result_summary hashes sim.json and sets input_sha256_source to
sim.json. Neither tool emits spec_sha256; that name is reserved for the
canonical ph.spec_sha256 digest in a separate feature.
estimate.memory_bytes is the padded device layout model from
photonhub.cost.quote. The device argument validates the selector; the
reported time range remains a CPU planning estimate.
convergence_report reads only convergence.json in the result directory.
To attach a report, save a ConvergenceReport object's to_dict() there
under that filename. With no file, found is false and there is no saved
convergence evidence for the result.
Resources¶
| URI | Content |
|---|---|
photonhub://schema/simulation_v1.json |
The wire schema. |
photonhub://capabilities |
Current local capabilities. |
photonhub://error-codes |
The stable issue-code catalog. |
photonhub://skill/SKILL.md |
The packaged agent skill. |
photonhub://skill/import/SKILL.md |
The packaged skill for importing a simulation. |
Errors and issue codes¶
An issue in a tool output has id, severity, path, monitor, message, fix, and
detail (null when empty). path points into the wire document, and monitor names a monitor
when one is involved. In validation, an error issue makes ok false;
correct it before running. A rejected spec returns ok: false with issues;
it is not a tool error.
A tool failure returns isError: true with text of the form
Error executing tool <name>: <json>, where the JSON object has this shape:
For example, calling result_summary without a selected result or a
result_dir returns no-result-loaded. An unknown monitor returns
unknown-monitor; bad field arguments return invalid-argument. An invalid
spec passed to run_simulation or estimate returns spec-rejected.
The original error text stays inside message.
The registry also covers validation issues and result advisories. IDs are
stable and sorted here; the photonhub://error-codes resource returns the
same entries as JSON.
| ID | Severity | Category | Title | Fix |
|---|---|---|---|---|
absorbing-band |
warning |
setup-advisory |
Absorbing band | Move the monitor into the nonabsorbing interior. |
advisory-skipped |
info |
setup-advisory |
Setup advisories skipped | Inspect the setup advisory error and retry the summary. |
aliasing |
warning |
setup-advisory |
Aliasing risk | Sample often enough to resolve the signal. |
all-zero |
warning |
result-advisory |
All-zero monitor | Check the monitor placement and recording interval. |
apodization-window |
warning |
setup-advisory |
Apodization window | Adjust the recording window to include the signal. |
boundary-layers-overlap |
error |
runnable |
Boundary layers overlap | Enlarge the domain, refine the mesh, or reduce boundary layers. |
construction-warning |
warning |
spec |
Construction warning | Review the construction warning before running. |
cpu-only-feature |
error |
device |
CPU-only feature | Choose the CPU device for this feature. |
cw-ramp-longer-than-run |
error |
runnable |
CW ramp too long | Lengthen the run or reduce ramp cycles. |
engine-error |
warning |
engine |
Engine error | Check the local solver and retry validation. |
engine-rejected |
error |
engine |
Engine rejected spec | Correct the spec according to the engine error. |
engine-unavailable |
info |
engine |
Engine unavailable | Install or build the local solver to run engine validation. |
flux-above-unity |
warning |
result-advisory |
Flux above unity | Check resolution, normalization, and source bandwidth. |
flux-plane-in-boundary |
error |
runnable |
Flux plane in boundary | Move the flux plane outside the boundary layers. |
internal-error |
error |
tool |
Internal error | Report this error with the tool input and message. |
invalid-argument |
error |
tool |
Invalid argument | Provide valid argument values. |
modal-port-not-ready |
error |
spec |
Modal port not ready | Prepare the modal port before running. |
mode-source-stale |
error |
spec |
Stale mode source | Re-solve the mode source against the current geometry and grid. |
no-monitors |
info |
setup-advisory |
No monitors | Add a monitor if recorded output is needed. |
no-result-loaded |
error |
tool |
No result loaded | Call open_result(result_dir) first or pass result_dir. |
no-sources |
error |
runnable |
No sources | Add a source or a sourced port. |
non-finite |
error |
result-advisory |
Non-finite values | Inspect the run for divergence or incomplete output. |
out-of-band |
warning |
setup-advisory |
Outside source band | Choose frequencies within the source band. |
output-size |
warning |
setup-advisory |
Large output | Reduce the monitor output size if storage is limited. |
read-error |
error |
result-advisory |
Monitor read error | Check the monitor output file. |
redundant-frequencies |
warning |
setup-advisory |
Redundant frequencies | Reduce the number of recorded frequencies. |
result-unreadable |
error |
tool |
Result unreadable | Provide a readable result bundle. |
run-aborted |
error |
run |
Run aborted | Inspect the abort reason and rerun if needed. |
solver-not-found |
error |
tool |
Solver not found | Install or build the local solver. |
solver-run-failed |
error |
tool |
Solver run failed | Inspect the local solver error and retry. |
source-in-boundary |
error |
runnable |
Source in boundary | Move the source out of the boundary layers. |
spec-constraint |
error |
spec |
Spec constraint | Change the value to satisfy the spec constraint. |
spec-invalid |
error |
spec |
Invalid spec | Check the simulation schema and correct the spec. |
spec-invalid-choice |
error |
spec |
Invalid choice | Choose a value allowed by the schema. |
spec-missing-field |
error |
spec |
Required field missing | Add the required field. |
spec-not-json |
error |
spec |
Invalid JSON spec | Provide a JSON object or a path to a readable .json file. |
spec-rejected |
error |
tool |
Spec rejected | Call validate_spec to inspect and correct the spec. |
spec-unknown-field |
error |
spec |
Unknown spec field | Remove the unsupported field. |
spec-unreadable |
error |
spec |
Spec unreadable | Use a readable .json spec file. |
spec-wrong-type |
error |
spec |
Wrong field type | Use the field type required by the schema. |
step-cap-reached |
warning |
run |
Step cap reached | Increase the run duration or inspect the stop criteria. |
unknown-definition |
error |
tool |
Unknown definition | Choose a definition name from get_schema().definitions. |
unknown-monitor |
error |
tool |
Unknown monitor | Choose a monitor from list_monitors. |
What the tools never do¶
The tools never submit a cloud job, spend money, call the network, upload
files, or change a spec. run_simulation runs on this machine only, as before.
Cloud GPU runs stay in the Python SDK through ph.cloud, behind the user's
own approval and quote.
The agent skill¶
The SDK ships a skill file for an agent host. Write it where that host reads skills:
The same file is the photonhub://skill/SKILL.md resource.
A second skill rebuilds a simulation from another FDTD tool as a PhotonHub script:
It reads the source as text and does not run it. It writes a PhotonHub script
and an import report that lists what maps, what maps in part, and what has no
equivalent. An imported scene is a new PhotonHub scene, so it needs its own
convergence study. The same file is the photonhub://skill/import/SKILL.md
resource.
Python¶
The same functions are importable without the mcp extra from
photonhub.agent_tools: get_schema, capabilities, validate_spec,
estimate, result_summary, and convergence_report. Each returns a
pydantic model. See the agent tools reference
for the Python API and the error codes reference
for photonhub.error_codes.