klayout-draw-mcp 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- klayout_draw_mcp-0.1.0/.github/workflows/docs.yml +63 -0
- klayout_draw_mcp-0.1.0/.github/workflows/publish.yml +53 -0
- klayout_draw_mcp-0.1.0/.gitignore +20 -0
- klayout_draw_mcp-0.1.0/.python-version +1 -0
- klayout_draw_mcp-0.1.0/LICENSE +21 -0
- klayout_draw_mcp-0.1.0/PKG-INFO +100 -0
- klayout_draw_mcp-0.1.0/README.md +71 -0
- klayout_draw_mcp-0.1.0/docs/assets/images/basic_shapes.png +0 -0
- klayout_draw_mcp-0.1.0/docs/assets/images/cis_aps_pixel.png +0 -0
- klayout_draw_mcp-0.1.0/docs/assets/images/cmos_inverter.png +0 -0
- klayout_draw_mcp-0.1.0/docs/assets/images/nmos_transistor.png +0 -0
- klayout_draw_mcp-0.1.0/docs/examples.md +64 -0
- klayout_draw_mcp-0.1.0/docs/index.md +64 -0
- klayout_draw_mcp-0.1.0/examples/basic_shapes.py +67 -0
- klayout_draw_mcp-0.1.0/examples/cis_aps_pixel.py +128 -0
- klayout_draw_mcp-0.1.0/examples/cmos_inverter.py +99 -0
- klayout_draw_mcp-0.1.0/examples/nmos_transistor.py +75 -0
- klayout_draw_mcp-0.1.0/mkdocs.yml +58 -0
- klayout_draw_mcp-0.1.0/pyproject.toml +44 -0
- klayout_draw_mcp-0.1.0/scripts/render_examples.py +60 -0
- klayout_draw_mcp-0.1.0/src/klayout_draw_mcp/__init__.py +3 -0
- klayout_draw_mcp-0.1.0/src/klayout_draw_mcp/server.py +237 -0
- klayout_draw_mcp-0.1.0/uv.lock +1495 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "examples/**"
|
|
9
|
+
- "scripts/render_examples.py"
|
|
10
|
+
- "mkdocs.yml"
|
|
11
|
+
- ".github/workflows/docs.yml"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
pages: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pages
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
env:
|
|
27
|
+
QT_QPA_PLATFORM: offscreen
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
|
|
35
|
+
# System libraries the headless klayout.lay renderer needs on Linux.
|
|
36
|
+
- name: Install system dependencies
|
|
37
|
+
run: |
|
|
38
|
+
sudo apt-get update
|
|
39
|
+
sudo apt-get install -y --no-install-recommends \
|
|
40
|
+
xvfb libgl1 libegl1 libxkbcommon0 libfontconfig1 libdbus-1-3
|
|
41
|
+
|
|
42
|
+
- name: Install Python dependencies
|
|
43
|
+
run: pip install klayout "mkdocs-material>=9.5"
|
|
44
|
+
|
|
45
|
+
- name: Render example screenshots
|
|
46
|
+
run: xvfb-run -a python scripts/render_examples.py
|
|
47
|
+
|
|
48
|
+
- name: Build site
|
|
49
|
+
run: mkdocs build --strict
|
|
50
|
+
|
|
51
|
+
- uses: actions/upload-pages-artifact@v3
|
|
52
|
+
with:
|
|
53
|
+
path: site
|
|
54
|
+
|
|
55
|
+
deploy:
|
|
56
|
+
needs: build
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment:
|
|
59
|
+
name: github-pages
|
|
60
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
61
|
+
steps:
|
|
62
|
+
- id: deployment
|
|
63
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
- name: Build sdist and wheel
|
|
22
|
+
run: |
|
|
23
|
+
pip install build
|
|
24
|
+
python -m build
|
|
25
|
+
|
|
26
|
+
- name: Check metadata
|
|
27
|
+
run: |
|
|
28
|
+
pip install twine
|
|
29
|
+
twine check dist/*
|
|
30
|
+
|
|
31
|
+
- uses: actions/upload-artifact@v4
|
|
32
|
+
with:
|
|
33
|
+
name: dist
|
|
34
|
+
path: dist/
|
|
35
|
+
|
|
36
|
+
publish:
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
# Only publish on a real GitHub release, not on manual dispatch.
|
|
40
|
+
if: github.event_name == 'release'
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/project/klayout-draw-mcp/
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write # OIDC token for PyPI Trusted Publishing
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
|
|
52
|
+
- name: Publish to PyPI
|
|
53
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 geniuskey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: klayout-draw-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for drawing, generating, and editing GDS layouts with KLayout.
|
|
5
|
+
Project-URL: Homepage, https://github.com/geniuskey/klayout-draw-mcp
|
|
6
|
+
Project-URL: Documentation, https://geniuskey.github.io/klayout-draw-mcp/
|
|
7
|
+
Project-URL: Repository, https://github.com/geniuskey/klayout-draw-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/geniuskey/klayout-draw-mcp/issues
|
|
9
|
+
Author-email: geniuskey <geniuskey@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: drawing,eda,gds,klayout,layout,mcp
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: klayout>=0.29
|
|
25
|
+
Requires-Dist: mcp>=1.2.0
|
|
26
|
+
Provides-Extra: docs
|
|
27
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# klayout-draw-mcp
|
|
31
|
+
|
|
32
|
+
An [MCP](https://modelcontextprotocol.io) server for **drawing GDS layouts** with **KLayout** — generate, edit, and view layouts from an AI assistant such as Claude Code or Claude Desktop.
|
|
33
|
+
|
|
34
|
+
Layout geometry is built in-process with the standalone [`klayout`](https://pypi.org/project/klayout/) Python module (no GUI needed). The installed KLayout application is launched only for viewing or manual editing.
|
|
35
|
+
|
|
36
|
+
## Tools
|
|
37
|
+
|
|
38
|
+
| Tool | Purpose |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `new_layout(top_cell, dbu)` | Start a new in-memory layout (coordinates in micrometers) |
|
|
41
|
+
| `add_box(layer, x1, y1, x2, y2, datatype)` | Add a rectangle |
|
|
42
|
+
| `add_polygon(layer, points, datatype)` | Add a polygon from `[x, y]` vertices |
|
|
43
|
+
| `add_path(layer, points, width, datatype)` | Add a path with width |
|
|
44
|
+
| `add_label(layer, x, y, text, datatype)` | Add a text label |
|
|
45
|
+
| `layout_info()` | Inspect current layout (layers, bbox, shape count) |
|
|
46
|
+
| `save_gds(path, open_after)` | Write GDS/OASIS; optionally open in the editor |
|
|
47
|
+
| `open_layout(file_path)` | Open a file in KLayout (viewer) |
|
|
48
|
+
| `open_editor(file_path?)` | Open KLayout in editor mode, or a blank layout |
|
|
49
|
+
| `run_script(code)` | Run arbitrary Python with `klayout.db` (cell hierarchy, arrays, booleans, DRC, ...) |
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Python ≥ 3.10
|
|
54
|
+
- The `klayout` and `mcp` Python packages (installed via the steps below)
|
|
55
|
+
- The [KLayout application](https://www.klayout.de/build.html) — only needed for `open_layout` / `open_editor`
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv sync
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Register with Claude Code
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
claude mcp add klayout -s user -- "<repo>/.venv/Scripts/python.exe" -m klayout_draw_mcp.server
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
On macOS/Linux use `.venv/bin/python`.
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
> "Draw a 5×5 µm box on layer 1 and a 2 µm-wide path, then save to out.gds and open it."
|
|
74
|
+
|
|
75
|
+
Claude calls `new_layout` → `add_box` / `add_path` → `save_gds(open_after=True)`.
|
|
76
|
+
|
|
77
|
+
For anything beyond the basic shapes, `run_script` exposes the full `klayout.db` API.
|
|
78
|
+
|
|
79
|
+
## Examples
|
|
80
|
+
|
|
81
|
+
Self-contained scripts in [`examples/`](examples). Each builds a layout with the
|
|
82
|
+
standalone `klayout.db` module and writes a GDS:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run python examples/cis_aps_pixel.py out.gds
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
You can also paste the body of a script's `build()` function into the `run_script`
|
|
89
|
+
tool. The scripts share one layer map (OD 3/0, POLY 6/0, NPLUS 4/0, METAL1 9/0, ...).
|
|
90
|
+
|
|
91
|
+
| Example | What it draws |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| `basic_shapes.py` | The four primitives: box, path, polygon, label |
|
|
94
|
+
| `nmos_transistor.py` | A single NMOS (active, poly gate, n+, contacts, M1 S/G/D) |
|
|
95
|
+
| `cmos_inverter.py` | A CMOS inverter: PMOS over NMOS, shared gate, Vdd/Vss rails |
|
|
96
|
+
| `cis_aps_pixel.py` | A 1 µm 4T CMOS image-sensor pixel, tiled as a 2×2 array |
|
|
97
|
+
|
|
98
|
+
## Note
|
|
99
|
+
|
|
100
|
+
`run_script` executes arbitrary Python locally, in-process. Only run scripts you trust.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# klayout-draw-mcp
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server for **drawing GDS layouts** with **KLayout** — generate, edit, and view layouts from an AI assistant such as Claude Code or Claude Desktop.
|
|
4
|
+
|
|
5
|
+
Layout geometry is built in-process with the standalone [`klayout`](https://pypi.org/project/klayout/) Python module (no GUI needed). The installed KLayout application is launched only for viewing or manual editing.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Purpose |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `new_layout(top_cell, dbu)` | Start a new in-memory layout (coordinates in micrometers) |
|
|
12
|
+
| `add_box(layer, x1, y1, x2, y2, datatype)` | Add a rectangle |
|
|
13
|
+
| `add_polygon(layer, points, datatype)` | Add a polygon from `[x, y]` vertices |
|
|
14
|
+
| `add_path(layer, points, width, datatype)` | Add a path with width |
|
|
15
|
+
| `add_label(layer, x, y, text, datatype)` | Add a text label |
|
|
16
|
+
| `layout_info()` | Inspect current layout (layers, bbox, shape count) |
|
|
17
|
+
| `save_gds(path, open_after)` | Write GDS/OASIS; optionally open in the editor |
|
|
18
|
+
| `open_layout(file_path)` | Open a file in KLayout (viewer) |
|
|
19
|
+
| `open_editor(file_path?)` | Open KLayout in editor mode, or a blank layout |
|
|
20
|
+
| `run_script(code)` | Run arbitrary Python with `klayout.db` (cell hierarchy, arrays, booleans, DRC, ...) |
|
|
21
|
+
|
|
22
|
+
## Requirements
|
|
23
|
+
|
|
24
|
+
- Python ≥ 3.10
|
|
25
|
+
- The `klayout` and `mcp` Python packages (installed via the steps below)
|
|
26
|
+
- The [KLayout application](https://www.klayout.de/build.html) — only needed for `open_layout` / `open_editor`
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv sync
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Register with Claude Code
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude mcp add klayout -s user -- "<repo>/.venv/Scripts/python.exe" -m klayout_draw_mcp.server
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
On macOS/Linux use `.venv/bin/python`.
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
> "Draw a 5×5 µm box on layer 1 and a 2 µm-wide path, then save to out.gds and open it."
|
|
45
|
+
|
|
46
|
+
Claude calls `new_layout` → `add_box` / `add_path` → `save_gds(open_after=True)`.
|
|
47
|
+
|
|
48
|
+
For anything beyond the basic shapes, `run_script` exposes the full `klayout.db` API.
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
|
|
52
|
+
Self-contained scripts in [`examples/`](examples). Each builds a layout with the
|
|
53
|
+
standalone `klayout.db` module and writes a GDS:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv run python examples/cis_aps_pixel.py out.gds
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You can also paste the body of a script's `build()` function into the `run_script`
|
|
60
|
+
tool. The scripts share one layer map (OD 3/0, POLY 6/0, NPLUS 4/0, METAL1 9/0, ...).
|
|
61
|
+
|
|
62
|
+
| Example | What it draws |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `basic_shapes.py` | The four primitives: box, path, polygon, label |
|
|
65
|
+
| `nmos_transistor.py` | A single NMOS (active, poly gate, n+, contacts, M1 S/G/D) |
|
|
66
|
+
| `cmos_inverter.py` | A CMOS inverter: PMOS over NMOS, shared gate, Vdd/Vss rails |
|
|
67
|
+
| `cis_aps_pixel.py` | A 1 µm 4T CMOS image-sensor pixel, tiled as a 2×2 array |
|
|
68
|
+
|
|
69
|
+
## Note
|
|
70
|
+
|
|
71
|
+
`run_script` executes arbitrary Python locally, in-process. Only run scripts you trust.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Self-contained scripts live in [`examples/`](https://github.com/geniuskey/klayout-draw-mcp/tree/main/examples).
|
|
4
|
+
Each one builds a layout with the standalone `klayout.db` module and writes a GDS:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
uv run python examples/cis_aps_pixel.py out.gds
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
You can also paste the body of a script's `build()` function into the `run_script`
|
|
11
|
+
tool. All examples share one layer map (OD `3/0`, POLY `6/0`, NPLUS `4/0`,
|
|
12
|
+
PPLUS `5/0`, METAL1 `9/0`, NWELL `1/0`, PWELL `2/0`, CONT `8/0`, TEXT `63/0`, …).
|
|
13
|
+
|
|
14
|
+
The screenshots below are rendered headlessly with `klayout.lay` by
|
|
15
|
+
[`scripts/render_examples.py`](https://github.com/geniuskey/klayout-draw-mcp/blob/main/scripts/render_examples.py),
|
|
16
|
+
the same way the documentation builds them in CI.
|
|
17
|
+
|
|
18
|
+
## Basic shapes
|
|
19
|
+
|
|
20
|
+
The four primitives — box, path, polygon and label — on a few layers. The place to
|
|
21
|
+
start if you just want to see geometry come out.
|
|
22
|
+
|
|
23
|
+
{ loading=lazy }
|
|
24
|
+
|
|
25
|
+
```python title="examples/basic_shapes.py"
|
|
26
|
+
--8<-- "examples/basic_shapes.py"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## NMOS transistor
|
|
30
|
+
|
|
31
|
+
A single planar n-channel MOSFET (W = 1 µm, L = 0.4 µm): active diffusion, a poly
|
|
32
|
+
gate crossing the channel, n+ source/drain implant, and contacts plus metal-1 pads
|
|
33
|
+
for source (S), gate (G) and drain (D).
|
|
34
|
+
|
|
35
|
+
{ loading=lazy }
|
|
36
|
+
|
|
37
|
+
```python title="examples/nmos_transistor.py"
|
|
38
|
+
--8<-- "examples/nmos_transistor.py"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## CMOS inverter
|
|
42
|
+
|
|
43
|
+
A pull-up PMOS (in an n-well, top) over a pull-down NMOS (in the p-well, bottom),
|
|
44
|
+
sharing one poly gate (the input, in red). The two drains are tied together on
|
|
45
|
+
metal-1 (the output). Vdd rails the top, Vss the bottom, with well ties on each.
|
|
46
|
+
|
|
47
|
+
{ loading=lazy }
|
|
48
|
+
|
|
49
|
+
```python title="examples/cmos_inverter.py"
|
|
50
|
+
--8<-- "examples/cmos_inverter.py"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## CIS APS pixel
|
|
54
|
+
|
|
55
|
+
A simplified 1 µm 4T CMOS image-sensor pixel: a large photodiode (PD), a transfer
|
|
56
|
+
gate (TX) to the floating diffusion (FD), and reset (RST), source-follower (SF) and
|
|
57
|
+
row-select (SEL) transistors in the shared active column. The unit pixel is drawn
|
|
58
|
+
into its own cell and instanced as a 2×2 array, the way a real sensor array repeats.
|
|
59
|
+
|
|
60
|
+
{ loading=lazy }
|
|
61
|
+
|
|
62
|
+
```python title="examples/cis_aps_pixel.py"
|
|
63
|
+
--8<-- "examples/cis_aps_pixel.py"
|
|
64
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# klayout-draw-mcp
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/klayout-draw-mcp/)
|
|
4
|
+
[](https://pypi.org/project/klayout-draw-mcp/)
|
|
5
|
+
[](https://github.com/geniuskey/klayout-draw-mcp/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
An [MCP](https://modelcontextprotocol.io) server for **drawing GDS layouts** with
|
|
8
|
+
**[KLayout](https://www.klayout.de)** — generate, edit and view chip layouts straight
|
|
9
|
+
from an AI assistant such as Claude Code or Claude Desktop.
|
|
10
|
+
|
|
11
|
+
Layout geometry is built in-process with the standalone
|
|
12
|
+
[`klayout`](https://pypi.org/project/klayout/) Python module — no GUI needed. The
|
|
13
|
+
installed KLayout application is launched only when you want to view or hand-edit a
|
|
14
|
+
result.
|
|
15
|
+
|
|
16
|
+
> "Draw a CIS 4T APS pixel at 1 µm pitch, tile it 2×2, then save to `out.gds` and open it."
|
|
17
|
+
|
|
18
|
+
The assistant calls `new_layout` → drawing tools / `run_script` → `save_gds(open_after=True)`.
|
|
19
|
+
See the [Examples](examples.md) for layouts produced exactly this way.
|
|
20
|
+
|
|
21
|
+
## Tools
|
|
22
|
+
|
|
23
|
+
| Tool | Purpose |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `new_layout(top_cell, dbu)` | Start a new in-memory layout (coordinates in micrometers) |
|
|
26
|
+
| `add_box(layer, x1, y1, x2, y2, datatype)` | Add a rectangle |
|
|
27
|
+
| `add_polygon(layer, points, datatype)` | Add a polygon from `[x, y]` vertices |
|
|
28
|
+
| `add_path(layer, points, width, datatype)` | Add a path with width |
|
|
29
|
+
| `add_label(layer, x, y, text, datatype)` | Add a text label |
|
|
30
|
+
| `layout_info()` | Inspect current layout (layers, bbox, shape count) |
|
|
31
|
+
| `save_gds(path, open_after)` | Write GDS/OASIS; optionally open in the editor |
|
|
32
|
+
| `open_layout(file_path)` | Open a file in KLayout (viewer) |
|
|
33
|
+
| `open_editor(file_path?)` | Open KLayout in editor mode, or a blank layout |
|
|
34
|
+
| `run_script(code)` | Run arbitrary Python with `klayout.db` (cell hierarchy, arrays, booleans, DRC, …) |
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install klayout-draw-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The [KLayout application](https://www.klayout.de/build.html) is only required for
|
|
43
|
+
`open_layout` / `open_editor`.
|
|
44
|
+
|
|
45
|
+
## Register with Claude Code
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
claude mcp add klayout -s user -- python -m klayout_draw_mcp.server
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
When running from a checkout, point at the project's interpreter instead, e.g.
|
|
52
|
+
`<repo>/.venv/Scripts/python.exe` on Windows or `<repo>/.venv/bin/python` on macOS/Linux.
|
|
53
|
+
|
|
54
|
+
## How it works
|
|
55
|
+
|
|
56
|
+
There are two complementary ways to produce geometry:
|
|
57
|
+
|
|
58
|
+
1. **High-level drawing tools** (`new_layout`, `add_box`, `add_polygon`, …) build a
|
|
59
|
+
layout step by step. All coordinates are in micrometers.
|
|
60
|
+
2. **`run_script`** executes arbitrary Python with `klayout.db` available, for anything
|
|
61
|
+
the high-level tools do not cover — cell hierarchy, array instances, boolean ops, DRC.
|
|
62
|
+
|
|
63
|
+
!!! warning
|
|
64
|
+
`run_script` executes arbitrary Python locally, in-process. Only run scripts you trust.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Basic shapes sample for klayout-draw-mcp.
|
|
2
|
+
|
|
3
|
+
Shows the four primitives (box, path, polygon, label) on a few layers.
|
|
4
|
+
|
|
5
|
+
Run standalone:
|
|
6
|
+
uv run python examples/basic_shapes.py [out.gds]
|
|
7
|
+
|
|
8
|
+
Or paste the body of ``build()`` into the ``run_script`` MCP tool (``db`` is
|
|
9
|
+
injected there; ``ly``/``top`` map to ``session.layout``/``session.top``).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import klayout.db as db
|
|
15
|
+
|
|
16
|
+
# Shared layer map used across the examples: name -> (layer, datatype)
|
|
17
|
+
LAYERS = {
|
|
18
|
+
"OD": (3, 0), # active / diffusion
|
|
19
|
+
"POLY": (6, 0), # gate poly
|
|
20
|
+
"M1": (9, 0), # metal 1
|
|
21
|
+
"TEXT": (63, 0), # labels
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build() -> db.Layout:
|
|
26
|
+
ly = db.Layout()
|
|
27
|
+
ly.dbu = 0.001 # 1 nm grid
|
|
28
|
+
top = ly.create_cell("BASIC")
|
|
29
|
+
|
|
30
|
+
def lay(name: str) -> int:
|
|
31
|
+
return ly.layer(*LAYERS[name])
|
|
32
|
+
|
|
33
|
+
def box(name, x1, y1, x2, y2):
|
|
34
|
+
top.shapes(lay(name)).insert(db.DBox(x1, y1, x2, y2))
|
|
35
|
+
|
|
36
|
+
def path(name, pts, width):
|
|
37
|
+
top.shapes(lay(name)).insert(db.DPath([db.DPoint(*p) for p in pts], width))
|
|
38
|
+
|
|
39
|
+
def polygon(name, pts):
|
|
40
|
+
top.shapes(lay(name)).insert(db.DPolygon([db.DPoint(*p) for p in pts]))
|
|
41
|
+
|
|
42
|
+
def label(name, x, y, text):
|
|
43
|
+
top.shapes(lay(name)).insert(db.DText(text, db.DTrans(db.DVector(x, y))))
|
|
44
|
+
|
|
45
|
+
# a rectangle
|
|
46
|
+
box("OD", 0.0, 0.0, 5.0, 5.0)
|
|
47
|
+
|
|
48
|
+
# a 0.5 um wide L-shaped path
|
|
49
|
+
path("M1", [(0.0, 6.0), (5.0, 6.0), (5.0, 10.0)], 0.5)
|
|
50
|
+
|
|
51
|
+
# a triangle polygon
|
|
52
|
+
polygon("POLY", [(7.0, 0.0), (12.0, 0.0), (9.5, 5.0)])
|
|
53
|
+
|
|
54
|
+
# a couple of labels
|
|
55
|
+
label("TEXT", 2.5, 2.5, "box")
|
|
56
|
+
label("TEXT", 9.5, 1.5, "polygon")
|
|
57
|
+
|
|
58
|
+
return ly
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
import sys
|
|
63
|
+
|
|
64
|
+
out = sys.argv[1] if len(sys.argv) > 1 else "basic_shapes.gds"
|
|
65
|
+
layout = build()
|
|
66
|
+
layout.write(out)
|
|
67
|
+
print(f"wrote {out} bbox={layout.top_cell().dbbox().to_s()} um")
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""CIS 4T active-pixel-sensor (APS) sample for klayout-draw-mcp.
|
|
2
|
+
|
|
3
|
+
A simplified 1 um 4T CMOS image-sensor pixel: a large photodiode (PD), a
|
|
4
|
+
transfer gate (TX) to the floating diffusion (FD), and reset (RST),
|
|
5
|
+
source-follower (SF) and row-select (SEL) transistors in the shared active
|
|
6
|
+
column. The unit pixel is drawn into its own cell and instanced as a 2x2
|
|
7
|
+
array, the way a real sensor array repeats.
|
|
8
|
+
|
|
9
|
+
Run standalone:
|
|
10
|
+
uv run python examples/cis_aps_pixel.py [out.gds]
|
|
11
|
+
|
|
12
|
+
Or paste the body of ``build()`` into the ``run_script`` MCP tool.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import klayout.db as db
|
|
18
|
+
|
|
19
|
+
# Shared layer map: name -> (layer, datatype)
|
|
20
|
+
LAYERS = {
|
|
21
|
+
"PWELL": (2, 0),
|
|
22
|
+
"OD": (3, 0), # active / diffusion
|
|
23
|
+
"NPLUS": (4, 0), # n+ implant
|
|
24
|
+
"PPLUS": (5, 0), # p+ well tap
|
|
25
|
+
"POLY": (6, 0), # gate poly (TX / RST / SF / SEL)
|
|
26
|
+
"CONT": (8, 0),
|
|
27
|
+
"M1": (9, 0),
|
|
28
|
+
"PD": (10, 0), # photodiode n-implant
|
|
29
|
+
"TEXT": (63, 0),
|
|
30
|
+
"BND": (100, 0), # pixel boundary marker
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
PIXEL_PITCH = 1.0 # um
|
|
34
|
+
ARRAY = (2, 2) # columns, rows
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build() -> db.Layout:
|
|
38
|
+
ly = db.Layout()
|
|
39
|
+
ly.dbu = 0.001
|
|
40
|
+
top = ly.create_cell("CIS_APS")
|
|
41
|
+
pix = ly.create_cell("APS_PIXEL")
|
|
42
|
+
|
|
43
|
+
def lay(name: str) -> int:
|
|
44
|
+
return ly.layer(*LAYERS[name])
|
|
45
|
+
|
|
46
|
+
def box(name, x1, y1, x2, y2):
|
|
47
|
+
pix.shapes(lay(name)).insert(db.DBox(x1, y1, x2, y2))
|
|
48
|
+
|
|
49
|
+
def label(name, x, y, text):
|
|
50
|
+
pix.shapes(lay(name)).insert(db.DText(text, db.DTrans(db.DVector(x, y))))
|
|
51
|
+
|
|
52
|
+
def cont(cx, cy):
|
|
53
|
+
box("CONT", cx - 0.02, cy - 0.02, cx + 0.02, cy + 0.02)
|
|
54
|
+
|
|
55
|
+
# pixel boundary + well
|
|
56
|
+
box("BND", 0.0, 0.0, 1.0, 1.0)
|
|
57
|
+
box("PWELL", 0.0, 0.0, 1.0, 1.0)
|
|
58
|
+
|
|
59
|
+
# photodiode (left, large fill factor)
|
|
60
|
+
box("PD", 0.05, 0.05, 0.55, 0.95)
|
|
61
|
+
box("OD", 0.08, 0.08, 0.52, 0.92)
|
|
62
|
+
|
|
63
|
+
# transfer gate TX, bridging PD -> FD
|
|
64
|
+
box("POLY", 0.50, 0.58, 0.60, 0.82)
|
|
65
|
+
|
|
66
|
+
# floating diffusion FD
|
|
67
|
+
box("OD", 0.58, 0.60, 0.70, 0.80)
|
|
68
|
+
box("NPLUS", 0.57, 0.59, 0.71, 0.81)
|
|
69
|
+
|
|
70
|
+
# shared transistor active column on the right
|
|
71
|
+
box("OD", 0.70, 0.06, 0.94, 0.94)
|
|
72
|
+
box("NPLUS", 0.69, 0.05, 0.95, 0.95)
|
|
73
|
+
|
|
74
|
+
# RST / SF / SEL gates (horizontal poly across the column)
|
|
75
|
+
box("POLY", 0.66, 0.66, 0.97, 0.74) # RST
|
|
76
|
+
box("POLY", 0.66, 0.40, 0.97, 0.48) # SF
|
|
77
|
+
box("POLY", 0.66, 0.16, 0.97, 0.24) # SEL
|
|
78
|
+
|
|
79
|
+
# p-well tap (Vss), bottom-left
|
|
80
|
+
box("OD", 0.10, 0.10, 0.24, 0.20)
|
|
81
|
+
box("PPLUS", 0.09, 0.09, 0.25, 0.21)
|
|
82
|
+
|
|
83
|
+
# contacts
|
|
84
|
+
cont(0.64, 0.70) # FD
|
|
85
|
+
cont(0.18, 0.15) # Vss tap
|
|
86
|
+
for cy in (0.11, 0.32, 0.57, 0.84): # right column source/drains
|
|
87
|
+
cont(0.82, cy)
|
|
88
|
+
cont(0.69, 0.70) # RST gate
|
|
89
|
+
cont(0.69, 0.44) # SF gate
|
|
90
|
+
cont(0.69, 0.20) # SEL gate
|
|
91
|
+
|
|
92
|
+
# metal-1: FD -> SF gate strap, and a Vdd rail
|
|
93
|
+
box("M1", 0.62, 0.68, 0.72, 0.72)
|
|
94
|
+
box("M1", 0.66, 0.44, 0.72, 0.72)
|
|
95
|
+
box("M1", 0.88, 0.06, 0.95, 0.94)
|
|
96
|
+
|
|
97
|
+
# labels
|
|
98
|
+
label("TEXT", 0.26, 0.48, "PD")
|
|
99
|
+
label("TEXT", 0.50, 0.86, "TX")
|
|
100
|
+
label("TEXT", 0.60, 0.83, "FD")
|
|
101
|
+
label("TEXT", 0.74, 0.69, "RST")
|
|
102
|
+
label("TEXT", 0.76, 0.43, "SF")
|
|
103
|
+
label("TEXT", 0.74, 0.19, "SEL")
|
|
104
|
+
label("TEXT", 0.10, 0.23, "Vss")
|
|
105
|
+
label("TEXT", 0.90, 0.965, "Vdd")
|
|
106
|
+
|
|
107
|
+
# tile the unit pixel into a 2x2 array
|
|
108
|
+
nx, ny = ARRAY
|
|
109
|
+
top.insert(
|
|
110
|
+
db.DCellInstArray(
|
|
111
|
+
pix.cell_index(),
|
|
112
|
+
db.DTrans(db.DVector(0.0, 0.0)),
|
|
113
|
+
db.DVector(PIXEL_PITCH, 0.0),
|
|
114
|
+
db.DVector(0.0, PIXEL_PITCH),
|
|
115
|
+
nx,
|
|
116
|
+
ny,
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
return ly
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __name__ == "__main__":
|
|
123
|
+
import sys
|
|
124
|
+
|
|
125
|
+
out = sys.argv[1] if len(sys.argv) > 1 else "cis_aps.gds"
|
|
126
|
+
layout = build()
|
|
127
|
+
layout.write(out)
|
|
128
|
+
print(f"wrote {out} bbox={layout.top_cell().dbbox().to_s()} um")
|