PyLumericalMCP#

class ansys.lumerical.mcp.server.PyLumericalMCP(config: ansys.lumerical.mcp.config.Config | None = None, python_executable: str | None = None, working_directory: str | None = None, **kwargs)#

Bases: ansys.common.mcp.PyAnsysBaseMCP

MCP server exposing Ansys Lumerical (via PyLumerical) to LLM agents.

Hosts a single PersistentPythonSession subprocess that owns all live Lumerical handles. Multiple named sessions of any product type (FDTD/MODE/DEVICE/INTERCONNECT) live in that subprocess as _lumerical_sessions[name]. The MCP server only tracks metadata.

Overview#

create_context

Build the typed app context with the Lumerical-aware subprocess.

product_startup

Run framework startup logging after subprocess launch.

cleanup_python_session

Close Lumerical handles before tearing down the subprocess.

product_cleanup

No-op: Lumerical handle cleanup happens in cleanup_python_session.

product_lifespan

Lifespan wrapper that keeps the event loop responsive on shutdown.

Import detail#

from ansys.lumerical.mcp.server import PyLumericalMCP

Method detail#

PyLumericalMCP.create_context() ansys.lumerical.mcp.context.PyLumericalContext#

Build the typed app context with the Lumerical-aware subprocess.

Uses LumericalPersistentPythonSession so that long, silent Lumerical startup phases (FDTD() / MODE() / …). Don’t trip the framework’s 0.5 s no-data safety break and lose their output.

PyLumericalMCP.product_startup() None#

Run framework startup logging after subprocess launch.

PyLumericalMCP.cleanup_python_session() None#

Close Lumerical handles before tearing down the subprocess.

_lum_close_all() must run while the subprocess is still alive. It is flushed here (best-effort, bounded by _CLOSE_ALL_JOIN_TIMEOUT_S via a daemon thread) before delegating to the base teardown. Running it in product_cleanup would be too late. The subprocess is gone by then, and lumapi’s child processes are left orphaned.

Stays synchronous so direct unit tests work without an event loop; product_lifespan() offloads the call via asyncio.to_thread().

PyLumericalMCP.product_cleanup() None#

No-op: Lumerical handle cleanup happens in cleanup_python_session.

async PyLumericalMCP.product_lifespan(server: fastmcp.FastMCP) AsyncIterator[ansys.lumerical.mcp.context.PyLumericalContext]#

Lifespan wrapper that keeps the event loop responsive on shutdown.

Identical to the base PyAnsysBaseMCP.product_lifespan() except that the potentially slow cleanup_python_session() (which runs _lum_close_all() against the live subprocess) is dispatched via asyncio.to_thread(), so the synchronous teardown can’t freeze the event loop and block the STDIO transport from flushing its final JSON-RPC writes. Startup stays on the loop – it’s fast and runs before any tool traffic.