MCP / RPC reference

Complete MCP tool reference

klink's control plane is one tool catalogue with two faces: a typed Python client (KLinkClient) and the same methods exposed as MCP tools. Both are generated from the live plugin's method registry — there is no hand-maintained list that can drift. This page lists all 107 plugin RPCs + 46 MCP local tools (153 total) across 13 domains: function, parameters, examples.

Overview & conventions

Tool names are stable namespace.verb. Each maps to exactly one domain; that domain token is both a klink.find_tools domain=<token> navigation key and a --profile <token> filter.

Reading conventions

  • name* in a parameter list means required.
  • Coordinates: _um = microns (most natural); _dbu = integer database units. micron = dbu × layout.dbu, where dbu comes from layout.info.
  • Layers: layer index int, "L/D" string (e.g. "1/0"), or {layer, datatype} object.
  • The Kind column tells you where a tool runs: rpc executes inside the KLayout plugin; local executes in the MCP process. Other badges: read write verify escape mutates (undoable) long (separate timeout).
Batch first. Never author generated layout one RPC per object — the loop pays TCP + JSON + transaction + GUI bookkeeping per call and is often hundreds of times slower. Use shape.insert_boxes / shape.insert_many / instance.insert_many / instance.insert_pcell_many. Singleton inserts are for debugging one object.

Discover tools live

The catalogue is queried live, never memorized. It is generated from the live plugin's method registry — there is no hand-maintained list that can drift.

from klink import KLinkClient
with KLinkClient() as c:
    print([m["name"] for m in c.methods()["methods"]])   # meta.methods
klink.find_tools                          # no args → domain index
klink.find_tools domain="routing_backends"  # that area's tools + usage
klink.find_tools query="lvs route"          # ranked matches across all tools

klink.guide reports what is open, the on-disk intent state (declared nets / LVS reports / spec files), and the literal next call for each available intention.

Profiles

--profile filters along two orthogonal axes — intent and domain. Default is read,write,verify,escape.

IntentExposes
readRead-only: layout.info, cell.list, shape.query, view.*, pcell.*, recorder.
writeEditing: shape.insert_*, cell.create, layer.ensure, instance.insert*, edit.undo.
verifyChecks: drc.run, lvs.run.
escapeEscape hatches: exec.python, exec.reset, events.*.
allEverything, no filtering.
python -m klink.mcp --profile read,write,verify,escape   # default
python -m klink.mcp --profile read,device_photonics      # mix both axes
python -m klink.mcp --profile routing_backends           # one domain only

All local tools are always included under any intent profile; klink.find_tools / klink.status / klink.reconnect are always on. Legacy aliases: basic→read, draw→write, advanced→escape, drc→verify.

1 · Connection, self-check, discovery & view connection_and_view · 30 tools

klink.find_tools domain="connection_and_view"

Start here when unsure. klink.status reports connection, active session, interpreter and optional capabilities; klink.guide reports what is open + on-disk intent state + the literal next call; klink.find_tools navigates the rest; klink.reconnect recovers a dropped link. This domain also carries the MCP-side session registry helpers (klink.session_*, klink.transfer_*) — klink drives many KLayout sessions from one MCP bridge; sessions are equal peers, pass the one you mean explicitly. View tools are read-mostly navigation: a freshly created cell is invisible until view.show_cell; view.new_tab opens a disposable scratch tab; view.show_25d opens the native 2.5D viewer from a display list; view.hier_levels raises displayed hierarchy depth if child instances render as name-label boxes. Screenshots (view.screenshot) are a user-requested artifact only — never an agent verification step; prefer geometry queries. Destructive here: view.close_tab — disposable test tabs only.

ToolKindParamsFunction
hellorpcclient, protocolIntroduce the client, receive server info + capabilities. Recommended first call per connection.
klink.find_toolslocaldomain, queryDiscover tools by domain or keyword; no args -> domain index, domain=<token> -> that domain's tools + usage, query=<kw> -> ranked matches.
klink.guidelocalReports what is open, on-disk intent state, and the literal call for each available intention + a suggested next action.
klink.reconnectlocalClose a stale client and try to reconnect to KLayout.
klink.session_labellocalaliases, description, label*, session_id*Attach a human label and aliases.
klink.session_listlocalinclude_staleEnumerate discoverable sessions.
klink.session_resolvelocalquery*Resolve id / label / alias / active cell / top cell to a session.
klink.session_set_klive_targetlocalsession_id*Choose the klive-compatible 8082 endpoint (where c.show() lands).
klink.session_statuslocalinclude_stale, session_idOne session record (default: active).
klink.session_uselocalsession_id*Repoint the bridge's primary RPC target.
klink.statuslocalMCP bridge connection status, active session, interpreter, last connection error. First stop when debugging.
klink.transfer_commitlocaldry_run, package_id*Commit a prepared package.
klink.transfer_preparelocalcopy_mode, layer_map, source_session*, target_cell, target_session*, translate_umBuild a flat-selection package and dry-run it on the target.
meta.debug_signalsrpcfireSignalHub diagnostics; optionally fire a synthetic event to test delivery.
meta.methodsrpcFull RPC method catalogue with descriptions + JSON schemas, ready for LLM function-calling.
meta.pingrpcLiveness probe; echoes params + trace id.
view.activate_tabrpcindex*Switch the current tab; all single-layout RPCs then act on it.
view.close_tabrpcview_indexClose a layout view tab by index (active tab if omitted).
view.hier_levelsrpcmax, minRead or set the view's displayed hierarchy depth (min/max); raise max if dense instances render as name boxes.
view.highlightrpcboxes_um, circles_um, clear, color, expire_s, halo, line_width, polygons_umDraw transient highlight markers (boxes/polygons/circles) on the view - overlay only, never touches layout/selection/undo.
view.highlight_clearrpcRemove all klink highlight markers immediately.
view.list_tabsrpcList layout tabs (index, title, file, active cell, current).
view.new_tabrpccell_name, dbuOpen a new empty layout tab with a fresh top cell and make it current; returns previous_current_index to restore later.
view.screenshotrpcbbox_dbu, bbox_um, height_px, mode, path, width_pxRender a PNG. base64 data URL or path to disk. User-requested only.
view.show_25drpccell, displays*, generatorOpen KLayout's native 2.5D extruded viewer from a display list (layer + z range per material); z heights are process facts the caller supplies.
view.show_cellrpccell*, zoom_fitSet the displayed top cell (makes a new cell visible), zoom-fits by default.
view.show_lvsdbrpckind, path*Load a saved LVS/netlist DB into the Netlist Browser and show it. lvs -> .lvsdb, l2n -> .l2n.
view.viewportrpcReport current viewport: visible bbox (um and dbu), pixel size, cellview index.
view.zoom_boxrpcbbox_dbu, bbox_umZoom to exactly the given bbox (um or dbu).
view.zoom_fitrpcFit the whole layout into the viewport.

Example.

klink.status
klink.guide
view.list_tabs

# Python client equivalent
from klink import KLinkClient
with KLinkClient() as c:
    print(c.hello(client="my-script"))
    print(c.layout_info(verbosity="summary"))
    print(c.ping(nonce=42))

# session registry + two-phase transfer (prepare + dry-run, then commit)
klink.session_list
klink.session_label session_id="klayout-8766" label="scratch" aliases=["test"]
klink.session_resolve query="scratch"
klink.transfer_prepare source_session="klayout-8765" target_session="klayout-8766" copy_mode="flat_selection"
klink.transfer_commit package_id="pkg_0001"

2 · Multi-session registry & cross-session transfer (plugin side) multi_session_transfer · 7 tools

klink.find_tools domain="multi_session_transfer"

The plugin-side half of session/transfer: session.label_set and session.mark_klive_target manage the shared registry directly from a KLayout window; transfer.pending_set/status/clear and transfer.paste_pending hold and apply a reviewed flat-selection package in this window; transfer.import_cell_tree_package imports a cell tree from a GDS/OAS package via native Cell.copy_tree. The MCP-side orchestration (klink.session_*, klink.transfer_prepare/commit) lives in connection_and_view — start there for the two-phase, confirmation-safe transfer flow.

ToolKindParamsFunction
session.label_setrpcaliases, description, label*, session_id*(plugin) Set label/aliases in the shared registry.
session.mark_klive_targetrpc(plugin) Mark this window as the klive/8082 target.
transfer.import_cell_tree_packagerpcdry_run, path*, source_cell*Import one cell tree from a GDS/OAS via native Cell.copy_tree; conflicts get $N.
transfer.paste_pendingrpcclear_after, dry_runPaste the pending package (already contains final layers/coords).
transfer.pending_clearrpcClear the pending package (no geometry written).
transfer.pending_setrpcpackage*Store a reviewed package in this window.
transfer.pending_statusrpcPending package status for this window.

3 · Geometry & cell-structure authoring geometry_authoring · 50 tools

klink.find_tools domain="geometry_authoring"

The core drawing surface. Read first: layout.info, cell.list/cell.tree, layer.list/layer.display_list, shape.query, instance.query, pcell.*, library.list. Author with BATCH RPCs for anything generated — never one RPC per object. layer.ensure before drawing. edit.undo/redo/status wrap edits in transactions. Beyond basic authoring this domain also carries: geometry.boolean/cell_xor/density (diff and coverage checks), cell.fill_region (dummy fill / test-structure tiling), cell.flatten, layer.load_lyp/save_lyp/set_style/set_visible (view styling), library.list/refresh/register_file, pcell.convert_to_static, shape.change_layer/transform, instance.transform. Destructive: layout.clear, cell.delete — disposable/test cells only; do NOT touch the user's working tab/layout UNLESS the user explicitly instructs it.

Read

ToolKindParamsFunction
layout.inforpcverbositySnapshot: views, active cellview, top cell, source file, dbu, top-cell list, layers. Refresh your world view.
cell.listrpclimit, name_prefix, offset, top_only, with_bboxFlat paginated cell list.
cell.treerpcmax_depth, max_nodes, rootHierarchical cell tree (bounded).
layer.listrpcAll layers: layer_index, layer/datatype, name, dbu_um.
layer.display_listrpcList the current view's layer DISPLAY entries (visible, colors, dither pattern, name) - the view-side counterpart of layer.list.
shape.queryrpcbbox_dbu, cell*, kinds, layers, limitRead shapes from ONE cell (no recursion) as JSON. Narrow with layers+bbox; paginate (default 500, max 5000).
instance.queryrpcbbox_dbu, bbox_um, child, limit, parent*Direct child instances: name, bbox, transform, array, PCell metadata, per-layer shape counts.
pcell.librariesrpcAvailable PCell libraries (Basic + PDKs).
pcell.listrpclibraryPCell names in a library (default Basic).
pcell.inforpclibrary, pcell*Parameters of a PCell (name/type/default/description/choices).
library.listrpcAll libraries registered in this KLayout process (Basic, salt/PDK, runtime-registered).

Write · cells & layers

ToolKindParamsFunction
cell.createrpcnameNew cell (dup name -> $1...). Not idempotent.
cell.renamerpcallow_suffix, cell*, new_name*Rename; dup name errors unless allow_suffix=true.
cell.deleterpccell*, recursiveDelete a cell (recursive drops orphaned children). Destructive.
cell.flattenrpccell*, dry_run, levels, pruneFlatten a cell's hierarchy into plain shapes; dry_run previews, prune removes orphaned children.
cell.fill_regionrpcboxes_um, cell*, circles_um, column_step_um, exclude_layers, exclude_margin_um, fc_bbox_um, fill_cell*, origin_um, polygons_um, region_layers, row_step_umTile a fill cell across a region (boxes/polygons/circles/region_layers minus exclude_layers) - KLayout's Fill Utility for dummy fill, device arrays, test structures.
layer.ensurerpcdatatype, layer*, nameUpsert a GDS layer; returns layer_index.
layer.set_stylerpccolor, dither_pattern, fill_color, frame_color, layer*, line_widthStyle one layer's display (color/fill/frame/dither/line width) in the current view - display only, layout data untouched.
layer.set_visiblerpcexclusive, layers*, visibleShow/hide layers in the current view; exclusive=true shows ONLY the listed layers.
layer.load_lyprpcpath*Load a KLayout .lyp layer-properties file into the current view (colors/stipples/visibility) in one call.
layer.save_lyprpcpath*Save the current view's layer properties (colors/stipples/visibility) to a .lyp file.
library.refreshrpclibraryRe-evaluate library content in every layout that uses it, e.g. after a PCell was re-registered.
library.register_filerpcdescription, name, path*, technologyRegister a layout file as a runtime library so its cells become placeable by name via instance.insert*.

Write · shapes (batch-first)

ToolKindParamsFunction
shape.insert_boxesrpcboxes_dbu, boxes_um, cell*, datatype, dry_run, layer, layer_indexBatch many rectangles on one cell/layer.
shape.insert_manyrpccell*, dry_run, items*Batch mixed box/polygon/path/text.
shape.insert_boxrpcbbox_dbu, bbox_um, cell*, datatype, layer, layer_indexOne rectangle.
shape.insert_polygonrpccell*, datatype, layer, layer_index, points_dbu, points_umOne polygon (hull only), auto-closed.
shape.insert_pathrpcbegin_ext_dbu, begin_ext_um, cell*, datatype, end_ext_dbu, end_ext_um, layer, layer_index, points_dbu, points_um, round_ends, width_dbu, width_umOne path (center line + width).
shape.insert_textrpccell*, datatype, layer, layer_index, position_dbu, position_um, size_dbu, size_um, string*One text label (annotation, no mask geometry).
shape.deleterpcall_layers, bbox_dbu, bbox_um, cell*, datatype, dry_run, kinds, layer, layer_index, layers, limitDelete shapes by selector; dry_run previews count. One transaction.
shape.change_layerrpcbbox_um, cell*, from_layer*, to_layer*Move shapes from one layer to another within a cell (optionally only those touching a bbox).
shape.transformrpcbbox_um, cell*, layers, limit, mirror, move_um, rotationMove/rotate/mirror EXISTING shapes in place matching a filter (layers and/or bbox required).

Write · instances & PCells (batch-first)

ToolKindParamsFunction
instance.insert_manyrpcdry_run, items*, parent*Batch child-cell instances.
instance.insert_pcell_manyrpcdry_run, items*, parent*Batch PCell instances.
instance.insertrpcarray, child*, klink_id, library, magnification, mirror, parent*, position_dbu, position_um, rotationPlace child in parent; optional array grid.
instance.insert_pcellrpcarray, klink_id, library, magnification, mirror, params, parent*, pcell*, position_dbu, position_um, rotationBuild a PCell variant then insert. Call pcell.info first.
instance.deleterpcall, bbox_dbu, bbox_um, child, dry_run, limit, parent*Delete instances by selector (non-destructive to the child cell).
instance.transformrpcbbox_um, child, mirror, move_um, parent*, rotationMove/rotate/mirror PLACED instances matching a filter (child and/or bbox); zero matches errors.
pcell.register_fittedrpcfit_table*, name*Register a fitted-device PCell at runtime into klink_structdevice; no plugin reload.
pcell.convert_to_staticrpccell*, prune_variantConvert a PCell variant into a static cell and retarget every instance to it; geometry is then frozen.

Write · layout-level & edit history

ToolKindParamsFunction
layout.show_filerpckeep_position, mode, path*, technologyLoad GDS/OAS (reload if open). replace or new tab.
layout.save_filerpccellview_index, path*Save; extension picks GDSII/OASIS.
layout.import_filerpccreate_other_layers, layer_map, on_conflict, path*MERGE a layout file into the ACTIVE layout with layer remap + cell-name-conflict policy (rename/add/overwrite/skip).
layout.clearrpccellview_indexDestructive: clear the whole layout.
edit.undorpcUndo last undoable op; returns before/after stack.
edit.redorpcRedo.
edit.statusrpcdebugUndo/redo availability.

Geometry checks (read-only reports)

ToolKindParamsFunction
geometry.booleanrpca*, b*, op*, write_toBoolean (and/or/xor/not) between two {cell, layer} sources; reports polygon_count/area, optionally writes the result to write_to.
geometry.cell_xorrpccell_a*, cell_b*, layers, only_differingGeometric diff between two cells per layer (pure report, writes nothing) - the tool for 'did my edit change only what I intended'.
geometry.densityrpccell*, layer*, window_umCovered-area density of one layer in a cell (area / window area); the pre-check for dummy-fill decisions.

Example.

# 1) read
layout.info verbosity="summary"
cell.list top_only=true

# 2) create cell + ensure layer
cell.create name="MYBLOCK"
layer.ensure layer=1 datatype=0 name="M1"

# 3) batch-draw a row of rectangles (microns) -- never loop single inserts!
shape.insert_boxes cell="MYBLOCK" layer="1/0" boxes_um=[[0,0,10,4],[20,0,30,4],[40,0,50,4]]

# 4) mixed shapes in one call
shape.insert_many cell="MYBLOCK" items=[
  {"kind":"box","layer":"1/0","bbox_um":[0,10,50,14]},
  {"kind":"path","layer":"2/0","points_um":[[0,20],[50,20]],"width_um":2},
  {"kind":"text","layer":"63/0","position_um":[0,26],"text":"MYBLOCK","size_um":4}
]

# 5) make it visible (a new cell is invisible until show_cell)
view.show_cell cell="MYBLOCK"

# PCell placement (check params first)
pcell.info library="Basic" pcell="CIRCLE"
instance.insert_pcell parent="MYBLOCK" library="Basic" pcell="CIRCLE" \
    params={"l":"1/0","r":5.0,"n":64} position_um=[100,0]

# one call places an 8x8 grid
instance.insert parent="TOP" child="MYBLOCK" position_um=[0,0] \
    array={"rows":8,"cols":8,"pitch_x_um":60,"pitch_y_um":40}

4 · Selection & SEND interaction memory selection_and_send_memory · 10 tools

klink.find_tools domain="selection_and_send_memory"

Two distinct things. selection.* is the LIVE current selection in KLayout: selection.get, selection.set_box (replaces current selection), selection.clear, selection.send_context (agent-side explicit SEND). interaction.* is durable session memory of selections the user explicitly SENT (toolbar SEND, recorded as ids like sel_0006): interaction.selection.latest/recent (default latest 5, ordered NOT time-pruned)/get/label, and interaction.context (current selection + recent memory together). Use these whenever the user says "just sent", "this area", "here", "that one". Resolve by order/count, not age. Bind user phrases to these ids/queries, NOT to screenshots.

ToolKindParamsFunction
interaction.contextlocalinclude_current_selectionCurrent selection + recent SEND memory together.
interaction.selection.clear_sessionlocalconfirm*Clear this session's interaction context after confirmation.
interaction.selection.getlocalid*One stored selection by id.
interaction.selection.labellocaldescription, id*, labelAttach a label/description.
interaction.selection.latestlocalLatest stored SEND.
interaction.selection.recentlocallimitRecent SENDs (default latest 5, by order).
selection.clearrpcClear the current selection.
selection.getrpclimitCurrent selection (shape or instance). Empty = empty list, not an error.
selection.send_contextrpcmax_items, sourceEmit the current selection as a selection_sent event (agent-side SEND).
selection.set_boxrpcbbox_dbu, bbox_um, cell*, include_instances, layers, limitSelect all shapes intersecting a box on given layers; replaces current selection.

Example.

# user selects A -> SEND, selects B -> SEND
interaction.selection.recent limit=2       # -> [sel_0007(B), sel_0006(A)]
interaction.selection.label id="sel_0006" label="probe pad"
# now act by id, not by guessing position from a screenshot

5 · Ports & anchors (routing markers) ports_and_anchors · 18 tools

klink.find_tools domain="ports_and_anchors"

Ports are net endpoints (klink_Port PCells: net + orientation + width). Anchors are routing constraints (klink_Anchor PCells) whose kind is waypoint_region, bend_region, or corridor (a plain corridor is a REQUIRED pass-through; label it choice_group=BUS for an OPTIONAL channel). port.mark/list/update/transform/set_layer/unmark/delete_all/repair_names; the same verbs on anchor.* (+ anchor.repair_ids). port.mark_many marks many ports in one cell with a single RPC/undo step (validate-before-mutate) — use it instead of looping port.mark for generated port arrays. port.harvest_blackbox derives Ports from LIVE gdsfactory/PDK blackbox instance positions via the waveguide stub convention — a photonics-adjacent tool that lives in this domain. Routing tools default port_layer=999/99, anchor_layer=999/1. Keepouts are NOT an anchor kind — pass your OWN design's obstacle layer(s) to routing tools as obstacle_layers; klink ships no default keepout layer (900/0 is klink's reserved keepout layer, used internally by structdevice). These are the INPUT to routing_backends: mark Ports+Anchors, then call a routing.* tool.

ToolKindParamsFunction
port.set_layerrpclayer*Configure the default Port marker layer.
port.markrpcaccess_mode, cell*, center_dbu, center_um, label, layer, name, net, orientation, port_type, show_label, slide_allowed, slide_edge, target_layer, width_umCreate one klink_Port PCell (a net endpoint).
port.mark_manyrpcaccess_mode, cell*, items*, label, layer, net, orientation, port_type, show_label, slide_allowed, slide_edge, target_layer, width_umCreate many Port PCells in one cell in one call/undo step; validated before any insert (one bad item rejects the whole batch).
port.listrpccell*, layer, sortList Ports in a cell.
port.updaterpcaccess_mode, cell*, label, layer, name*, net, orientation, port_type, show_label, slide_allowed, slide_edge, target_layer, width_umUpdate one Port by immutable name.
port.transformrpcaccess_mode, cell*, label, layer, names, net, orientation, port_type, rotate_delta, selection, show_label, slide_allowed, slide_edge, target_layer, width_umBatch-update Ports by names or GUI selection.
port.repair_namesrpccell*, layer, prefixRepair duplicate/empty Port names (GUI-inserted).
port.harvest_blackboxlocalcell*, clear, nets, port_layer, stub_size_um*, tags*, wg_layer*Derive Ports from PDK blackbox instances via the waveguide stub convention. Re-run after moving instances.
port.unmarkrpccell*, name*Delete one Port.
port.delete_allrpccell*, layerDelete all Ports in a cell.
anchor.set_layerrpclayer*Configure the default Anchor marker layer.
anchor.markrpccell*, center_dbu, center_um, height_um, id, kind, label, layer, mode, name, net, orientation, path_points, priority, radius_um, required, show_label, width_umCreate one klink_Anchor PCell (routing constraint).
anchor.listrpccell*, layer, sortList Anchors in a cell.
anchor.updaterpccell*, height_um, id*, kind, label, layer, mode, net, new_id, orientation, path_points, priority, radius_um, required, show_label, width_umUpdate one Anchor by immutable id.
anchor.transformrpccell*, height_um, ids, kind, label, layer, mode, names, net, orientation, path_points, priority, radius_um, required, selection, show_label, width_umBatch-update Anchors.
anchor.repair_idsrpccell*, layer, prefixRepair duplicate/empty Anchor ids.
anchor.unmarkrpccell*, id*Delete one Anchor.
anchor.delete_allrpccell*, layerDelete all Anchors in a cell.

Example.

port.mark cell="NET1" name="A" center_um=[0,0]   orientation="E" width_um=2 net="sig"
port.mark cell="NET1" name="B" center_um=[80,20] orientation="W" width_um=2 net="sig"
anchor.mark cell="NET1" kind="waypoint_region" center_um=[40,40] radius_um=6 net="sig"
port.list cell="NET1"

routing.tapered_hybrid_cell cell="NET1" angle_mode="manhattan" obstacle_layers=["10/0"]

6 · Routing backends routing_backends · 9 tools

klink.find_tools domain="routing_backends"

All read Port/Anchor PCells in a cell and write routes. Pick by topology/quality: routing.tapered_hybrid_cell is the main path+patch backend; routing.tapered_polygon_cell writes continuous taper polygons (first-class, not a fallback); routing.steiner_cell handles multi-terminal nets (>2 ports); the routing.damped_* family adds explicit extra obstacle clearance; routing.global_channel_cell is a global-decision router (candidate-sink assignment + corridor-capacity load-balancing) on top of tapered hybrid geometry; routing.multilayer_escape_cell routes wall-blocked nets via a bridge layer + vias; routing.gdsfactory_ports routes Port markers with a named gdsfactory strategy (needs gdsfactory in the interpreter). Always inspect the structured result: ok=false, obstacle_hit_count>0, sibling overlaps, short route_count all mean failure. Pass your own design's obstacle_layers (no default).

ToolKindParamsFunction
routing.damped_polygon_celllocalanchor_layer, angle_mode, cell*, clear, corner_style, damping_distance_um, obstacle_layers, port_layer, route_layer, spacing_umDamped continuous taper polygons.
routing.damped_segment_celllocalanchor_layer, angle_mode, cell*, clear, damping_distance_um, obstacle_layers, port_layer, spacing_umExtra obstacle clearance, segment output.
routing.damped_steiner_celllocalanchor_layer, angle_mode, cell*, clear, damping_distance_um, obstacle_layers, port_layer, root_ports, route_layer, spacing_umDamped multi-terminal trunk/branch.
routing.gdsfactory_portslocalall_two_port_nets, allow_crossing, auto_taper, backbone_um, bundle_gather_um, cell*, clear, collision_check_layers, cross_section, distance_um, end_straight_um, gf_route_layer, min_straight_taper_um, net, obstacle_bboxes_um, output_mode, pair_by, path_length_match, port_layer, radius_um, resolution_um, route_layer*, route_width_um, router, sbend_fallback, separation_um, sort_ports, source, source_orientation, source_prefix, start_straight_um, steps, taper, target, target_orientation, target_prefix, waypoints_umRoute Port markers with one named gdsfactory strategy. Needs gdsfactory in the interpreter.
routing.global_channel_celllocalanchor_layer, angle_mode, cell*, clear, obstacle_layers, port_layer, safe_distance_um, spacing_umStronger global-decision router: candidate assignment + corridor-capacity load-balancing, reuses hybrid geometry.
routing.multilayer_escape_celllocalbridge_layer*, cell*, clear, obstacle_layers, port_layer, route_layer*, spacing_um, via_layer*Wall-blocked nets via bridge layer + vias.
routing.steiner_celllocalanchor_layer, cell*, clear, obstacle_layers, port_layer, root_ports, route_layerMulti-terminal nets (>2 ports).
routing.tapered_hybrid_celllocalanchor_layer, angle_mode, cell*, clear, obstacle_layers, port_layer, spacing_umMain path+patch backend. angle_mode: any/manhattan/fortyfive.
routing.tapered_polygon_celllocalanchor_layer, angle_mode, cell*, clear, corner_style, obstacle_layers, port_layer, route_layer, spacing_umContinuous taper polygons (first-class).

routing.gdsfactory_ports routers (a parameter the chosen router can't honor is an error naming the routers that honor it, never silently ignored):

routerUse
bundle (default)Manhattan river routing with separation; also honors waypoints/steps, radius_um, start/end_straight_um, path_length_match, collision_check_layers.
electricalbundle + metal defaults + sharp corners + electrical port typing.
sbendSmooth S-transition for laterally offset, facing ports.
all_angleNon-Manhattan bundle (optional backbone_um spine).
singleIndependent Manhattan route per pair.
dubinsArc-based any-heading route per pair.
astar (experimental)Grid A* around obstacle_bboxes_um; gf's astar is fragile, so klink verifies the result and errors instead of returning a wall-crossing route. For reliable avoidance use klink's own tapered_hybrid/damped + obstacle_layers.

Example.

routing.tapered_hybrid_cell cell="BLOCK" angle_mode="manhattan" spacing_um=20 obstacle_layers=["900/0"]
routing.damped_segment_cell cell="BLOCK" damping_distance_um=15 obstacle_layers=["900/0"]
routing.steiner_cell cell="BLOCK" route_layer="1/0"
routing.gdsfactory_ports cell="BLOCK" route_layer="1/0" router="bundle" \
    separation_um=5 radius_um=10 path_length_match=true
"Done" for a route/P&R stage is a live LVS match=True — marker counts and "looks routed" never substitute. Check ok, obstacle_hit_count, sibling overlap and route_count in the structured result.

7 · DRC & LVS verification drc_and_lvs_verification · 2 tools

klink.find_tools domain="drc_and_lvs_verification"

Both are long-running, pure pya, domain-agnostic. drc.run runs arbitrary DRC DSL (Ruby) you supply against the layout — exceptions inside the script come back as results, they do NOT fail the RPC. lvs.run is the connectivity counterpart: extracts the live layout into a device netlist and compares it against a REFERENCE netlist you supply, writes a native .lvsdb and (default) opens it in the Netlist/LVS browser. A P&R/device stage counts as DONE only on a real live LVS match=True — offline fixtures and marker counts never substitute. For the structdevice flow prefer structdevice.lvs_check.

ToolKindParamsFunction
drc.runrpccode*, input_layout, output_rdb, result_mode, stderr_limit, stdout_limit, top_cellRun arbitrary DRC DSL in KLayout's Ruby DRC engine. With source() = standalone, else interactive on the current layout.
lvs.runrpccell*, conductors*, devices*, out_lvsdb, reference*, show, viasExtract (per-cell extractors + conductor layers) -> compare to reference (reference.spice or reference.netlist) -> write .lvsdb and show.

Example.

# DRC: M1 minimum spacing 0.2um (interactive, against the current layout)
drc.run script="""
m1 = input(1, 0)
m1.space(0.2.um).output("M1_space", "M1 spacing < 0.2um")
"""

# LVS: extract conductor layers, compare to a reference SPICE, open the browser
lvs.run cell="BLOCK" conductors=["1/0","3/0"] vias=["2/0"] \
    devices={...} reference={"spice":"ref.spice"} out_lvsdb="block.lvsdb" show=true

8 · Custom-device netlist → auto P&R → LVS device_structdevice · 6 tools

klink.find_tools domain="device_structdevice"

This is the device-AGNOSTIC custom-device P&R flow. A "device" is any cell with an arbitrary parameter set + terminals; klink assumes no parameter names/count and no device vocabulary. The device library, process profile, and terminal source are EXAMPLE/PDK data passed in explicitly — the tools ship none and return an instructive "write/run an example" error. structdevice.build_from_netlist is the headline one-call flow (confirmation-gated: call once for a proposal, again with the confirm token to build); routing runs on the flexdr engine using a compact physical model where the device's own metal layers double as routing layers. structdevice.declare_nets / connect_nets / lvs_check / spec_write are the SEND-driven interactive path. structdevice.register_pcell wraps the lower-level plugin RPC pcell.register_fitted (listed under geometry_authoring).

ToolKindParamsFunction
structdevice.build_from_netlistlocalcell*, cols, confirm, mode, netlist*, rows, sessionHeadline one-call flow: device-level netlist -> floorplan -> single-pass multilayer routes -> draw -> device-LVS a fresh cell. Confirmation-gated.
structdevice.connect_netslocalcell*, conductors, min_spacing_um, min_width_um, route_layer, route_width_um, session, via_cell, viasWire declared-but-unconnected nets + verify; any LVS mismatch undoes everything.
structdevice.declare_netslocalcell*, conductors, recent_sends*, viasOne SEND framing >=2 terminals = one declared net (persisted). Example-driven.
structdevice.lvs_checklocalcell*, conductors, mode, session, viasNet-level reconcile; device/both also run device-level NetlistComparer.
structdevice.register_pcelllocaldiff_report, fit_table*, name*, sessionRegister a fitted-device PCell at runtime; zero plugin reloads.
structdevice.spec_writelocalcell*, conductors, device_class, layer_roles*, session, viasProject a live cell into a klink.spec.json fact file.

Example.

# step 1: no confirm -> proposal (grid rows x cols, row pitch, routing layers, device mix)
structdevice.build_from_netlist cell="RINGOSC" netlist={
  "instances":[{"name":"INV0","device":"inv_x1"}, ...],
  "nets":[{"name":"a","terminals":["INV0/in","INV2/out"]}, ...],
  "groups":[]
} mode="3L"
# -> needs_confirmation + proposal + next_action(confirm=...)

# step 2: same args + confirm token -> actually place/route/draw/LVS
structdevice.build_from_netlist cell="RINGOSC" netlist={...} mode="3L" confirm="CONFIRM-xyz"

# SEND-driven interactive path
structdevice.declare_nets recent_sends=3 cell="BLOCK" conductors=["1/0","3/0"] vias=["2/0"]
structdevice.connect_nets cell="BLOCK" route_layer="3/0" route_width_um=0.5
structdevice.lvs_check cell="BLOCK" mode="both"
structdevice.spec_write cell="BLOCK" layer_roles={"1/0":"gate","3/0":"metal1"}

9 · Imaging (cross-section / 3D / SEM / Blender) imaging · 4 tools

klink.find_tools domain="imaging"

Everything runs klink-side (no plugin involvement); heavy deps are optional and the error names the exact pip install command. Recipes/VisualStack instances are example-owned — klink ships mechanism only. imaging.xsection_run makes a process cross-section along an explicit cut line from a .pyxs recipe (engine klayout_pyxs); steps=true + '# klink-step: <name>' recipe markers give a per-step film. imaging.render3d builds a GLB plus a self-contained interactive viewer HTML; mode=fast extrudes a visual-stack declaration, mode=process sweeps the xsection engine so process curvature (LOCOS, CMP) is real; fraction<1 exposes a true cutaway section. imaging.sem_top renders deterministic SEM-style top-view PNGs (greyscale + false color). imaging.blender renders a paper-grade image via a headless bpy subprocess — mode=die polishes a render3d GLB, mode=figure builds a device figure from GDS+stack at 1:1 layout coordinates (lattice layers become atomic-structure motifs).

ToolKindParamsFunction
imaging.blenderlocalbasename, camera, cell, gds, glb, lattice_a_um, mode*, output_dir*, overwrite, samples, session, slabs, stack, timeout_s, transparentPaper-grade Blender render via headless bpy subprocess. mode=die polishes a render3d GLB; mode=figure builds a device figure from GDS+stack at 1:1 layout scale (lattice layers -> atomic motifs).
imaging.render3dlocalbasename, cell, exclude, fraction, gds, mode, output_dir*, overwrite, recipe, session, slices, stack*Build a 3D model (GLB) plus a self-contained offline interactive viewer HTML. mode=fast extrudes a visual-stack declaration; mode=process sweeps the xsection engine so process curvature (LOCOS/CMP) is real.
imaging.sem_toplocalbasename, cell, corner_radius_um, gds, layers, output_dir*, overwrite, seed, session, stack*, width_pxSEM-style top-view PNGs (greyscale + false color) from a visual-stack declaration, with grain/scanlines/vignette; deterministic (seeded).
imaging.xsection_runlocalbasename, below_um, cell, cut_um*, delta_dbu, depth_um, exclude, extend_um, gds, height_um, output_dir*, overwrite, recipe*, render, session, show, stack, stepsProcess cross-section along an explicit cut line from a .pyxs recipe (engine klayout_pyxs); steps=true writes one section per marked process step.

10 · Nanodevices (Hall bar / EBL / flake) device_nanodevice · 2 tools

klink.find_tools domain="device_nanodevice"

nanodevice.hallbar is a one-call closed loop: from a HallBarSpec (bar length/width, contact_count, contact/pad dims, pitch, gaps) it computes and draws the whole device (bar + N symmetric contact arms + pads + Port markers + labels), then delegates routing to the generic router (overlap validation on, optional EBL writefield walls as keepouts), committing to a disposable cell (dry_run supported). Failures return problems/next_action and change nothing. nanodevice.detect_commit commits flake traces as polygons from a precomputed traces.json, or runs live detection from a microscope image (needs cv2 + numpy).

ToolKindParamsFunction
nanodevice.detect_commitlocalcell, coordinate, dry_run, image, pixel_size_um, session, traces_pathCommit flake traces as polygons. traces_path (no extra deps) or image+pixel_size_um for live detection (needs cv2 + numpy).
nanodevice.hallbarlocalcell, dry_run, route_layer, session, spacing_um, spec, writefieldBuild + route + validate + commit one Hall bar in one call.

Example.

nanodevice.hallbar cell="HB1" spec={
  "bar_length_um":60, "bar_width_um":8, "contact_count":6,
  "contact_width_um":4, "pad_size_um":40, "pitch_um":24, "gap_um":6
} route_layer="1/0" dry_run=true

nanodevice.detect_commit cell="FLAKE" traces_path="out/traces.json"

11 · Photonics (gdsfactory import / connect / reroute) device_photonics · 3 tools

klink.find_tools domain="device_photonics"

Photonic circuit flow. Needs gdsfactory in the MCP interpreter. Two port sources, one interactive loop: photonics.import_gf takes a finished user gdsfactory script over into the loop in one call — device instances become real KLayout cells+instances, routed connections collapse to device-level nets, per-device port templates persist in the spec, and nets are routed by klink. port.harvest_blackbox (listed under ports_and_anchors — it derives Ports from live blackbox instance positions) is the other port source; re-run it after moving instances, then route. photonics.connect reads the latest N SENDs as port pairs, auto-names nets, persists, re-harvests, and routes with gdsfactory. photonics.reroute re-routes a cell after the user moved components (reads the persisted net table). Multi-port optical nets are not routed as stars — insert an explicit splitter/MMI/Y-branch first and route the resulting two-port nets.

ToolKindParamsFunction
photonics.connectlocalcell, radius_um, recent_sends*, route_layer, separation_um, stub_size_um, wg_layer, width_umConnect ports the user just SENT: read latest N SENDs -> port pairs -> auto-name nets -> persist -> re-harvest -> route.
photonics.import_gflocalcell, component, port_layer, route, route_layer, script_path*, sessionTake over a finished gdsfactory script: import device instances (batch RPC), collapse routed connections to nets, persist port templates + net table, route with klink.
photonics.reroutelocalcell*, route_layer, session, stub_size_um, wg_layerRe-route after the user moved components (reads the persisted net table). Multi-port optical nets need a splitter first.

Example.

# 1) take over a finished script (c = build_mzi() inside it)
photonics.import_gf script_path="my_mzi.py" cell="MZI" route_layer="1/0"

# 2) drag a phase shifter in the KLayout GUI ... then:
photonics.reroute cell="MZI"     # optics + metal redrawn together, your drag is kept

# ports can also come from the blackbox stub convention
port.harvest_blackbox cell="MZI" tags=["gc","mmi"] wg_layer="1/0" stub_size_um=0.5
photonics.connect recent_sends=4 cell="MZI" radius_um=10 separation_um=5

12 · L-Edit bridge (file-exchange RPC) bridge_ledit · 3 tools

klink.find_tools domain="bridge_ledit"

Requires L-Edit running with the bridge macro loaded as SOURCE (example_template/ledit_bridge/ledit_bridge.cpp via Tools > Macro > Load Macro..., zero compile). Transport is a JSON file exchange under %LOCALAPPDATA%\klink\ledit_bridge\<namespace>; one namespace per L-Edit instance, only ONE L-Edit may hold a namespace at a time. ledit.status is discovery + handshake — call it first when anything misbehaves; errors name the exact fix. ledit.import_selection does a fresh GET of the user's current L-Edit selection into a new KLayout landing cell. ledit.push_cell pushes a flat KLayout cell back into L-Edit (append-only; use a fresh target cell to regenerate). Deeper T-Cell workflows live in the Python API klink.bridges.ledit — see the L-Edit Bridge guide.

ToolKindParamsFunction
ledit.import_selectionlocalnamespace, session, target_cellImport the user's CURRENT L-Edit selection into a fresh KLayout landing cell (fresh GET every call). Capability-matched conversion; layers migrate by name + GDS number; non-convertibles are listed.
ledit.push_celllocalcell*, ledit_cell, namespace, sessionPush a flat KLayout cell into L-Edit (box / path->wire / polygon). Sub-instances are counted, not silently dropped. L-Edit draw is append-only.
ledit.statuslocalnamespaceDiscovery and triage: macro liveness, whether a design is open, macro version/capabilities, current .tdb and cell. Call first when any ledit.* misbehaves.

13 · Escape hatch (pya exec, events, recorder) escape_hatch · 9 tools

klink.find_tools domain="escape_hatch"

Prefer typed RPCs. exec.python runs raw pya for operations no typed RPC covers / debugging / compact one-offs (exec.reset clears its namespace); it still schedules recorder + layout-diff detection. events.* (channels/status/subscribe/unsubscribe) is the live event stream the bridge subscribes to for SEND memory — usually you read interaction.* instead. recorder.* (start/stop/status) generates a replay SCRIPT (not a literal RPC log); a bulk RPC may expand into replay actions. Check recorder.status before tests so you never clobber a user recording.

ToolKindParamsFunction
events.channelsrpcList pushable event channels (NDJSON frames).
events.statusrpcSubscription + SignalHub diagnostics.
events.subscriberpcchannels*Subscribe to channels (unknown silently ignored).
events.unsubscriberpcchannelsUnsubscribe; * drops all.
exec.pythonrpccode*, protect_cellview, reset, result_mode, stderr_limit, stdout_limitRun arbitrary Python on KLayout's Qt main thread. Pre-bound pya, mw, view, layout. State persists per connection (reset=true wipes). stdout/stderr captured.
exec.resetrpcClear the per-connection namespace.
recorder.startrpcoutput_pathBegin recording into a replayable Python script. Idempotent.
recorder.statusrpcRecording state, event/action counts, output path.
recorder.stoprpcoutput_pathStop and write the script; returns stats + wrote.

Example.

recorder.status                 # confirm nobody else is recording first
recorder.start
# ... some typed-RPC edits + manual GUI edits ...
recorder.stop                   # produces <name>.py and standalone <name>_pya.py

# only use the escape hatch when no typed RPC covers it
exec.python code="print(layout.top_cell().name); print(len(list(layout.each_cell())))"

recorder produces two files: <name>.py (KLinkClient-based replay) and <name>_pya.py (a standalone pya version runnable inside KLayout).

See these tools composed into real loops and runnable demos in the Tutorials.