FDTD MCP

MCP server for Lumerical FDTD automation. Let AI assistants read, edit, run, and analyze FDTD simulations through the Model Context Protocol.

中文说明

Architecture

AI Assistant --MCP stdio--> server.py (system Python ≥3.10)
                                │ subprocess stdin/stdout
                             bridge.py (Lumerical embed Python 3.6.8)
                                │ lumapi
                             Lumerical FDTD engine

The dual-process design isolates the MCP protocol (which needs modern Python) from the Lumerical API (which only runs on the bundled Python 3.6.8). The bridge communicates via line-delimited JSON over stdin/stdout.

Installation

Prerequisites: Python ≥ 3.10, Lumerical FDTD

1. Install the package

git clone https://github.com/plaask/fdtd-mcp.git && cd fdtd-mcp
pip install .

2. Register with Claude Code

python install.py

This auto-detects your Lumerical installation and prints the registration command. Run it, restart Claude Code, done.

If auto-detection succeeds, the printed command will be as simple as:

claude mcp add fdtd -- python -m fdtd_mcp.server

If auto-detection fails

If Lumerical is installed at a non-standard location, specify the path manually:

Option A — pass it in the registration command:

claude mcp add fdtd -- python -m fdtd_mcp.server --lumerical-home "C:/Program Files/Lumerical/v241"

Option B — set the env var once:

[Environment]::SetEnvironmentVariable("LUMERICAL_HOME", "C:/Program Files/Lumerical/v241", "User")
# Restart the terminal, then just:
claude mcp add fdtd -- python -m fdtd_mcp.server

Other MCP clients (Cursor, VS Code, etc.)

The JSON equivalent of claude mcp add. Auto-detection works here too:

{
  "mcpServers": {
    "fdtd": {
      "command": "python",
      "args": ["-m", "fdtd_mcp.server"]
    }
  }
}

If auto-detection fails, add the --lumerical-home argument:

{
  "mcpServers": {
    "fdtd": {
      "command": "python",
      "args": ["-m", "fdtd_mcp.server", "--lumerical-home", "C:/Program Files/Lumerical/v241"]
    }
  }
}

Tools (27)

Category Tools
Lifecycle new, open, close, save
Overview get_model_overview
Inspect get_scene_info, get_object_info, get_model_variables, get_script, get_parameters, get_sweep_info
Knowledge get_lumapi_ref
Edit set_parameter, set_script
Execute execute, execute_file
Materials add_material, set_material, get_material
Run run, run_sweep, get_sweep_result
Data get_results, list_result_fields, get_result_data, get_result_file, has_result

Usage examples

Open and inspect

open("my_sim.fsp")
get_model_overview()                 → objects, variables, materials (one call — anti-skip-step)
get_script("::model")                → setup + analysis scripts
get_parameters()                     → all model & group parameters

Anti-hallucination workflow

LLMs are undertrained on Lumerical FDTD and often hallucinate function names, object types, and variable names. These tools help:

get_model_overview()                 → objects (disabled filtered by default),
                                        GUI model variables (LD, DBR, top_layer, au, ...),
                                        and material list — all in one call

get_lumapi_ref(list_only=true)       → before writing scripts, verify
                                        the function name exists in the
                                        bundled 32-entry cheatsheet

get_object_info("source_1")          → explicit type + source_kind discriminator
                                        (dipole vs plane vs tfsf vs gaussian)

Script scanning                      → execute() and set_script() warn about
                                        unrecognized function names non-blockingly

Edit and save

set_parameter("wavelength", 1550e-9)
set_script("::model", "analysis", "plot_spectrum();")
save("my_sim_v2.fsp")

Run and get data

run()
get_results("monitor1")              → list available datasets
list_result_fields("monitor1", "E")  → preview field names (Ex, Ey, Ez, ...)
get_result_data("monitor1", "E", fields=["Ex","T","f"])
                                     → multi-field data (not just f/lambda)
has_result("monitor1")               → safe existence check before fetching
get_result_file("monitor1", "E", "C:/data/fields.mat")
                                     → full export to .mat file

Build from scratch

new(dimension="3D", x_span=2e-6, y_span=2e-6, z_span=2e-6, mesh_accuracy=4)
execute("addrect()")
set_parameter("x span", 500e-9, object="rectangle")
set_parameter("material", "Si (Silicon) - Palik", object="rectangle")
execute("addfdtd()")
save("new_sim.fsp")

Custom materials

add_material(type="Sampled 3D data")                 → {name: "material_1"}
set_material("material_1", "nk data",
  [[300e-9,800e-9], [1.52,1.52], [0.001,0.001]])
set_material("material_1", "mesh order", 2)
# Built-in materials need no setup:
execute("addrect()")
set_parameter("material", "Au (Gold) - Johnson and Christy", object="rectangle")

Files

fdtd-mcp/
├── README.md
├── README_zh.md
├── LICENSE
├── pyproject.toml
├── install.py
└── fdtd_mcp/
    ├── __init__.py
    ├── discovery.py      # auto-detect Lumerical installation
    ├── bridge.py         # JSON-RPC bridge (Lumerical Python 3.6.8)
    ├── server.py         # MCP server (system Python)
    └── cheatsheet/
        └── lumapi_ref.json  # 32-entry Lumerical API reference

Requirements

  • Python ≥ 3.10
  • Lumerical FDTD (v202 or later)
  • mcp