gfp-mcp 0.2.4__py3-none-any.whl → 0.3.2__py3-none-any.whl

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.
gfp_mcp/tools/bbox.py ADDED
@@ -0,0 +1,115 @@
1
+ """Bounding box generation tool handler.
2
+
3
+ This module provides the handler for generating bounding box
4
+ GDS files from input GDS files.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from mcp.types import Tool
12
+
13
+ from .base import EndpointMapping, ToolHandler, add_project_param
14
+
15
+ __all__ = ["GenerateBboxHandler"]
16
+
17
+
18
+ class GenerateBboxHandler(ToolHandler):
19
+ """Handler for generating bounding box GDS files.
20
+
21
+ Creates a simplified version of the layout with only a bounding box
22
+ on specified layers.
23
+ """
24
+
25
+ @property
26
+ def name(self) -> str:
27
+ return "generate_bbox"
28
+
29
+ @property
30
+ def definition(self) -> Tool:
31
+ return Tool(
32
+ name="generate_bbox",
33
+ description=(
34
+ "Generate a bounding box GDS file from an input GDS. This creates "
35
+ "a simplified version of the layout with only a bounding box on "
36
+ "specified layers. Useful for creating abstract views, floorplanning, "
37
+ "or hierarchical design. Can optionally preserve specific layers and "
38
+ "ports."
39
+ ),
40
+ inputSchema=add_project_param(
41
+ {
42
+ "type": "object",
43
+ "properties": {
44
+ "path": {
45
+ "type": "string",
46
+ "description": (
47
+ "Path to the input GDS file. Can be absolute or relative "
48
+ "to the project directory."
49
+ ),
50
+ },
51
+ "outpath": {
52
+ "type": "string",
53
+ "description": (
54
+ "Output path for the bounding box GDS. If not specified, "
55
+ "uses the input filename with '-bbox' suffix."
56
+ ),
57
+ "default": "",
58
+ },
59
+ "layers_to_keep": {
60
+ "type": "array",
61
+ "items": {"type": "string"},
62
+ "description": (
63
+ "List of layer names to preserve in the output. "
64
+ "Other layers will be replaced by the bounding box."
65
+ ),
66
+ "default": [],
67
+ },
68
+ "bbox_layer": {
69
+ "type": "array",
70
+ "items": {"type": "integer"},
71
+ "description": (
72
+ "Layer (as [layer, datatype]) to use for the bounding box. "
73
+ "Default is [99, 0]."
74
+ ),
75
+ "default": [99, 0],
76
+ },
77
+ "ignore_ports": {
78
+ "type": "boolean",
79
+ "description": (
80
+ "If true, do not include ports in the output. "
81
+ "Default is false."
82
+ ),
83
+ "default": False,
84
+ },
85
+ },
86
+ "required": ["path"],
87
+ }
88
+ ),
89
+ )
90
+
91
+ @property
92
+ def mapping(self) -> EndpointMapping:
93
+ return EndpointMapping(method="POST", path="/api/bbox")
94
+
95
+ def transform_request(self, args: dict[str, Any]) -> dict[str, Any]:
96
+ """Transform generate_bbox MCP args to FastAPI params.
97
+
98
+ Args:
99
+ args: MCP tool arguments
100
+
101
+ Returns:
102
+ Dict with 'json_data' key for request body
103
+ """
104
+ json_data: dict[str, Any] = {"path": args["path"]}
105
+
106
+ if "outpath" in args and args["outpath"]:
107
+ json_data["outpath"] = args["outpath"]
108
+ if "layers_to_keep" in args and args["layers_to_keep"]:
109
+ json_data["layers_to_keep"] = args["layers_to_keep"]
110
+ if "bbox_layer" in args and args["bbox_layer"]:
111
+ json_data["bbox_layer"] = args["bbox_layer"]
112
+ if "ignore_ports" in args:
113
+ json_data["ignore_ports"] = args["ignore_ports"]
114
+
115
+ return {"json_data": json_data}
gfp_mcp/tools/build.py ADDED
@@ -0,0 +1,159 @@
1
+ """Build cells tool handler.
2
+
3
+ This module provides the handler for building GDS cells, including
4
+ optional PNG image rendering for visualization.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ from mcp.types import ImageContent, TextContent, Tool
14
+
15
+ from ..render import HAS_KLAYOUT, render_built_cells
16
+ from .base import EndpointMapping, ToolHandler, add_project_param
17
+
18
+ if TYPE_CHECKING:
19
+ from ..client import FastAPIClient
20
+
21
+ __all__ = ["BuildCellsHandler"]
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class BuildCellsHandler(ToolHandler):
27
+ """Handler for building GDS cells.
28
+
29
+ This handler builds one or more GDS cells and optionally renders
30
+ specified cells to PNG images for visualization.
31
+ """
32
+
33
+ @property
34
+ def name(self) -> str:
35
+ return "build_cells"
36
+
37
+ @property
38
+ def definition(self) -> Tool:
39
+ return Tool(
40
+ name="build_cells",
41
+ description=(
42
+ "Build one or more GDS cells by name. This creates the physical layout "
43
+ "files (.gds) for photonic components. Pass a list of cell names to build. "
44
+ "For a single cell, pass a list with one element. All cells are built "
45
+ "in the background and saved to the project build directory. "
46
+ "Use the optional 'visualize' parameter to specify which cells should be "
47
+ "rendered to PNG images and returned in the response."
48
+ ),
49
+ inputSchema=add_project_param(
50
+ {
51
+ "type": "object",
52
+ "properties": {
53
+ "names": {
54
+ "type": "array",
55
+ "items": {"type": "string"},
56
+ "description": "List of cell/component names to build (can be a single-item list)",
57
+ },
58
+ "visualize": {
59
+ "type": "array",
60
+ "items": {"type": "string"},
61
+ "description": (
62
+ "Optional list of cell names to render as PNG images. "
63
+ "Only cells in this list will be visualized. If not provided, "
64
+ "no images are returned. Use this to selectively view only "
65
+ "the cells you need to inspect."
66
+ ),
67
+ },
68
+ "with_metadata": {
69
+ "type": "boolean",
70
+ "description": (
71
+ "Include metadata in the GDS files (default: true)"
72
+ ),
73
+ "default": True,
74
+ },
75
+ "register": {
76
+ "type": "boolean",
77
+ "description": (
78
+ "Re-register the cells in the KLayout cache (default: true)"
79
+ ),
80
+ "default": True,
81
+ },
82
+ },
83
+ "required": ["names"],
84
+ }
85
+ ),
86
+ )
87
+
88
+ @property
89
+ def mapping(self) -> EndpointMapping:
90
+ return EndpointMapping(method="POST", path="/api/build-cells")
91
+
92
+ def transform_request(self, args: dict[str, Any]) -> dict[str, Any]:
93
+ """Transform build_cells MCP args to FastAPI params.
94
+
95
+ Args:
96
+ args: MCP tool arguments
97
+
98
+ Returns:
99
+ Dict with 'params' key for query parameters and 'json_data' for body
100
+ """
101
+ return {
102
+ "params": {
103
+ "with_metadata": args.get("with_metadata", True),
104
+ "register": args.get("register", True),
105
+ },
106
+ "json_data": args["names"],
107
+ }
108
+
109
+ async def handle(
110
+ self,
111
+ arguments: dict[str, Any],
112
+ client: FastAPIClient,
113
+ ) -> list[TextContent | ImageContent]:
114
+ """Build cells and optionally render images.
115
+
116
+ Args:
117
+ arguments: MCP tool arguments
118
+ client: FastAPI client for making requests
119
+
120
+ Returns:
121
+ List of TextContent with build results and optional ImageContent
122
+ """
123
+ try:
124
+ project = arguments.get("project")
125
+ transformed = self.transform_request(arguments)
126
+
127
+ response = await client.request(
128
+ method=self.mapping.method,
129
+ path=self.mapping.path,
130
+ params=transformed.get("params"),
131
+ json_data=transformed.get("json_data"),
132
+ project=project,
133
+ )
134
+
135
+ logger.debug("Build cells result: %s", response)
136
+
137
+ results: list[TextContent | ImageContent] = [
138
+ TextContent(
139
+ type="text",
140
+ text=json.dumps(response, indent=2),
141
+ )
142
+ ]
143
+
144
+ # Render images if klayout is available and visualize is specified
145
+ visualize = arguments.get("visualize", [])
146
+ if visualize and HAS_KLAYOUT:
147
+ results.extend(await render_built_cells(visualize, project))
148
+
149
+ return results
150
+
151
+ except Exception as e:
152
+ error_msg = f"Tool execution failed: {e!s}"
153
+ logger.exception(error_msg)
154
+ return [
155
+ TextContent(
156
+ type="text",
157
+ text=json.dumps({"error": error_msg}),
158
+ )
159
+ ]
gfp_mcp/tools/cells.py ADDED
@@ -0,0 +1,103 @@
1
+ """Cell listing and info tool handlers.
2
+
3
+ These tools provide information about available cells/components
4
+ in the current PDK.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from mcp.types import Tool
12
+
13
+ from .base import EndpointMapping, ToolHandler, add_project_param
14
+
15
+ __all__ = ["ListCellsHandler", "GetCellInfoHandler"]
16
+
17
+
18
+ class ListCellsHandler(ToolHandler):
19
+ """Handler for listing all available cells/components."""
20
+
21
+ @property
22
+ def name(self) -> str:
23
+ return "list_cells"
24
+
25
+ @property
26
+ def definition(self) -> Tool:
27
+ return Tool(
28
+ name="list_cells",
29
+ description=(
30
+ "List all available cells/components that can be built. Returns "
31
+ "the names of all registered component factories in the current PDK. "
32
+ "Use this to discover what components are available before building."
33
+ ),
34
+ inputSchema=add_project_param(
35
+ {
36
+ "type": "object",
37
+ "properties": {},
38
+ }
39
+ ),
40
+ )
41
+
42
+ @property
43
+ def mapping(self) -> EndpointMapping:
44
+ return EndpointMapping(method="GET", path="/api/cells")
45
+
46
+ def transform_response(self, response: Any) -> dict[str, Any]:
47
+ """Transform list_cells response to MCP format.
48
+
49
+ Args:
50
+ response: FastAPI response (list of cell names)
51
+
52
+ Returns:
53
+ Formatted response with cell names
54
+ """
55
+ if isinstance(response, list):
56
+ return {"cells": response, "count": len(response)}
57
+ return response
58
+
59
+
60
+ class GetCellInfoHandler(ToolHandler):
61
+ """Handler for getting detailed info about a specific cell."""
62
+
63
+ @property
64
+ def name(self) -> str:
65
+ return "get_cell_info"
66
+
67
+ @property
68
+ def definition(self) -> Tool:
69
+ return Tool(
70
+ name="get_cell_info",
71
+ description=(
72
+ "Get detailed information about a specific cell/component. Returns "
73
+ "metadata including the source file, parameters, and other details "
74
+ "about the component factory."
75
+ ),
76
+ inputSchema=add_project_param(
77
+ {
78
+ "type": "object",
79
+ "properties": {
80
+ "name": {
81
+ "type": "string",
82
+ "description": "Name of the cell/component to get info about",
83
+ },
84
+ },
85
+ "required": ["name"],
86
+ }
87
+ ),
88
+ )
89
+
90
+ @property
91
+ def mapping(self) -> EndpointMapping:
92
+ return EndpointMapping(method="GET", path="/api/cell-info")
93
+
94
+ def transform_request(self, args: dict[str, Any]) -> dict[str, Any]:
95
+ """Transform get_cell_info MCP args to FastAPI params.
96
+
97
+ Args:
98
+ args: MCP tool arguments
99
+
100
+ Returns:
101
+ Dict with 'params' key for query parameters
102
+ """
103
+ return {"params": {"name": args["name"]}}
@@ -0,0 +1,70 @@
1
+ """Connectivity check tool handler.
2
+
3
+ This module provides the handler for running local connectivity
4
+ checks on GDS files.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ from mcp.types import Tool
12
+
13
+ from .base import EndpointMapping, ToolHandler, add_project_param
14
+
15
+ __all__ = ["CheckConnectivityHandler"]
16
+
17
+
18
+ class CheckConnectivityHandler(ToolHandler):
19
+ """Handler for running connectivity checks on GDS files.
20
+
21
+ This is a fast, local check that verifies all layers are properly
22
+ connected and identifies any connectivity violations.
23
+ """
24
+
25
+ @property
26
+ def name(self) -> str:
27
+ return "check_connectivity"
28
+
29
+ @property
30
+ def definition(self) -> Tool:
31
+ return Tool(
32
+ name="check_connectivity",
33
+ description=(
34
+ "Run a local connectivity check on a GDS file. This verifies that "
35
+ "all layers are properly connected and identifies any connectivity "
36
+ "violations. This is a fast, local check (does not require uploading "
37
+ "to a remote server). Use this to quickly check for disconnected "
38
+ "components. Returns XML results showing connectivity issues."
39
+ ),
40
+ inputSchema=add_project_param(
41
+ {
42
+ "type": "object",
43
+ "properties": {
44
+ "path": {
45
+ "type": "string",
46
+ "description": (
47
+ "Path to the GDS file to check. Can be absolute or "
48
+ "relative to the project directory."
49
+ ),
50
+ },
51
+ },
52
+ "required": ["path"],
53
+ }
54
+ ),
55
+ )
56
+
57
+ @property
58
+ def mapping(self) -> EndpointMapping:
59
+ return EndpointMapping(method="POST", path="/api/check-connectivity")
60
+
61
+ def transform_request(self, args: dict[str, Any]) -> dict[str, Any]:
62
+ """Transform check_connectivity MCP args to FastAPI params.
63
+
64
+ Args:
65
+ args: MCP tool arguments
66
+
67
+ Returns:
68
+ Dict with 'json_data' key for request body
69
+ """
70
+ return {"json_data": {"path": args["path"]}}