Skip to content

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:

python -m pip install "photonhub[mcp]"

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.

{
  "mcpServers": {
    "photonhub": {
      "command": "photonhub-mcp",
      "args": ["<result-dir>"]
    }
  }
}

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:

{"command": "<venv>\\Scripts\\photonhub-mcp.exe"}

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

  1. Write the scene in Python with import photonhub as ph and save sim.to_wire_json().
  2. Call validate_spec until ok is true. Use each issue's fix hint.
  3. Call estimate to review the local size and CPU time range.
  4. Call run_simulation with device: "cpu" to run on this machine.
  5. Call result_summary, then convergence_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:

{"id": "...", "severity": "error", "message": "...", "fix": "..."}

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:

photonhub-mcp --print-skill > SKILL.md

The same file is the photonhub://skill/SKILL.md resource.

A second skill rebuilds a simulation from another FDTD tool as a PhotonHub script:

mkdir photonhub-import
photonhub-mcp --print-import-skill > photonhub-import/SKILL.md

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.