gfp-mcp 0.2.1__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.
- {mcp_standalone → gfp_mcp}/__init__.py +10 -8
- {mcp_standalone → gfp_mcp}/client.py +57 -33
- gfp_mcp/config.py +161 -0
- {mcp_standalone → gfp_mcp}/registry.py +0 -4
- gfp_mcp/render.py +139 -0
- {mcp_standalone → gfp_mcp}/resources.py +0 -3
- gfp_mcp/samples.py +206 -0
- gfp_mcp/server.py +235 -0
- gfp_mcp/tools/__init__.py +134 -0
- gfp_mcp/tools/base.py +235 -0
- gfp_mcp/tools/bbox.py +115 -0
- gfp_mcp/tools/build.py +159 -0
- gfp_mcp/tools/cells.py +103 -0
- gfp_mcp/tools/connectivity.py +70 -0
- gfp_mcp/tools/drc.py +379 -0
- gfp_mcp/tools/freeze.py +82 -0
- gfp_mcp/tools/lvs.py +86 -0
- gfp_mcp/tools/pdk.py +47 -0
- gfp_mcp/tools/port.py +82 -0
- gfp_mcp/tools/project.py +160 -0
- gfp_mcp/tools/samples.py +215 -0
- gfp_mcp/tools/simulation.py +153 -0
- gfp_mcp/utils.py +55 -0
- {gfp_mcp-0.2.1.dist-info → gfp_mcp-0.3.2.dist-info}/METADATA +37 -8
- gfp_mcp-0.3.2.dist-info/RECORD +29 -0
- gfp_mcp-0.3.2.dist-info/entry_points.txt +2 -0
- gfp_mcp-0.3.2.dist-info/top_level.txt +1 -0
- gfp_mcp-0.2.1.dist-info/RECORD +0 -14
- gfp_mcp-0.2.1.dist-info/entry_points.txt +0 -2
- gfp_mcp-0.2.1.dist-info/top_level.txt +0 -1
- mcp_standalone/config.py +0 -56
- mcp_standalone/mappings.py +0 -386
- mcp_standalone/server.py +0 -294
- mcp_standalone/tools.py +0 -530
- {gfp_mcp-0.2.1.dist-info → gfp_mcp-0.3.2.dist-info}/WHEEL +0 -0
- {gfp_mcp-0.2.1.dist-info → gfp_mcp-0.3.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
gfp_mcp/__init__.py,sha256=7q7olhYHIRXAfT1rSsgTW0duRoQf_uRpFx4yFjSwXSk,1127
|
|
2
|
+
gfp_mcp/client.py,sha256=2-b_FZSC_8576qNLhVUrGT-oYWqr1RLTuz6BRrT0kak,9488
|
|
3
|
+
gfp_mcp/config.py,sha256=lrfg98VZDnIBlmPQD2V5sqw9aVZf7vsdPkOOsgqcqe0,4471
|
|
4
|
+
gfp_mcp/registry.py,sha256=ozDrMPksWoyKAkNd9MH2g8FRoN0XEQKWnLwZsgDxWLc,6127
|
|
5
|
+
gfp_mcp/render.py,sha256=rfiLzmcO9M4Zlvob-B89gu5K0GGu2cHX9GSJys4fexQ,3773
|
|
6
|
+
gfp_mcp/resources.py,sha256=upY93XVemc5ezx1p_YML57O6HYVahCs2RsYXn4Dw_X0,3517
|
|
7
|
+
gfp_mcp/samples.py,sha256=cJRho3G7RKeX7X9fIjJBpjUEGLAHuwHvPbYI5OZphTg,5610
|
|
8
|
+
gfp_mcp/server.py,sha256=dGhnKFKfoB4dBzk3MJ5aSVpeBFD1xQueLgCMdTYh8u8,7540
|
|
9
|
+
gfp_mcp/utils.py,sha256=awyJqKFlyIMIvM-8N0XT3S2vG_FYXkfpHJSAJCp-WcA,1719
|
|
10
|
+
gfp_mcp/tools/__init__.py,sha256=TwgWlw1fMyJKHYifjtESBQ21JmyGZ0pv9b2lmXU44r0,3557
|
|
11
|
+
gfp_mcp/tools/base.py,sha256=kNci7NdIKdY_qUWGgrARJk3KAS3PqzqzKiSNYNJ96Ks,7381
|
|
12
|
+
gfp_mcp/tools/bbox.py,sha256=P7PF0s4ykXfvaUtTNdPV7_WNOQZu76RXf85cXH70SIw,4296
|
|
13
|
+
gfp_mcp/tools/build.py,sha256=hW55JzNw3QL8RXDz8Yx0VJB-vq3WzZ9exwScSNcIYiA,5575
|
|
14
|
+
gfp_mcp/tools/cells.py,sha256=2kOPCxvMqz7rt7qr5Pytv7nEvdvxb7wmUPWc6s-WDxE,3044
|
|
15
|
+
gfp_mcp/tools/connectivity.py,sha256=wHAppW9b6Nern6YAdZpRcZ5jjPS09uJji5xFSZipu68,2259
|
|
16
|
+
gfp_mcp/tools/drc.py,sha256=DHRZDT9JsTLr8CMO6mEplBjUC8OSOAQSuisVYm5d5QI,12888
|
|
17
|
+
gfp_mcp/tools/freeze.py,sha256=d4U-nmHCVMJNgN0RSSwGjL9nXIusmuy5S0KgVJSqLLI,2702
|
|
18
|
+
gfp_mcp/tools/lvs.py,sha256=UfBqO5AQdKhvOkDHrjI-npTc6ktNSW_5HPR5WUUVTxY,2718
|
|
19
|
+
gfp_mcp/tools/pdk.py,sha256=jNWerrJy2NbdEVJxioTlM-uyvWJo1ZSm9l5FINAnUiE,1318
|
|
20
|
+
gfp_mcp/tools/port.py,sha256=Ldm2l771S3h_lEjZOoC1CMexdbk9nvrbU5tmbwkNFiY,2567
|
|
21
|
+
gfp_mcp/tools/project.py,sha256=ULqsMcnMDE9hJAxWTh9abWytkr0jhqLaqu9_2PgcMyg,4984
|
|
22
|
+
gfp_mcp/tools/samples.py,sha256=H-rwOtcqInTA9lmCMvuh8ZpLcVsa_fiZw1BPsxEDBI8,6819
|
|
23
|
+
gfp_mcp/tools/simulation.py,sha256=VtCq2gLloPeikFCNbn2Fvq9yngCSdXgz6kt32_0RUmI,5999
|
|
24
|
+
gfp_mcp-0.3.2.dist-info/licenses/LICENSE,sha256=ixSuHdKKXzNJw_eTgAxHzaCNIds8k48hytA_eJgA8gQ,225
|
|
25
|
+
gfp_mcp-0.3.2.dist-info/METADATA,sha256=fTeePDc3kjuicIEMJi-7wiIB3Nq2OrRMUpWJ17Nk9mY,8236
|
|
26
|
+
gfp_mcp-0.3.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
27
|
+
gfp_mcp-0.3.2.dist-info/entry_points.txt,sha256=Fbd4_16Giw-_y6piVlbk8sHDgvZbE3oqAfeYl21Xx98,54
|
|
28
|
+
gfp_mcp-0.3.2.dist-info/top_level.txt,sha256=F7RvvaMcHgg7J_fNBN77ZMt7r32U7ZbVrn4zM1odUSs,8
|
|
29
|
+
gfp_mcp-0.3.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gfp_mcp
|
gfp_mcp-0.2.1.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
gfp_mcp-0.2.1.dist-info/licenses/LICENSE,sha256=ixSuHdKKXzNJw_eTgAxHzaCNIds8k48hytA_eJgA8gQ,225
|
|
2
|
-
mcp_standalone/__init__.py,sha256=B6JB7U_vEMARO87RzNDesfSZdGgD1U-RC2GkL2ijOCA,1069
|
|
3
|
-
mcp_standalone/client.py,sha256=vS_mw3frp5dQr2s_uFbPH-cF4k98rOJFGZLIz1FOU7A,8371
|
|
4
|
-
mcp_standalone/config.py,sha256=1B00PLrKOz96c62lkhIgrraF-HWZ015uoPZ7okCcuKk,1525
|
|
5
|
-
mcp_standalone/mappings.py,sha256=2J2DEZzXTdmi6VmrtCeI1cg-2T7tYHkytRGrZJ62zog,10583
|
|
6
|
-
mcp_standalone/registry.py,sha256=1E61UalVot8HUS3cALjM7ejYB0qR6tI5QbQSZZeQe7Y,6401
|
|
7
|
-
mcp_standalone/resources.py,sha256=iMkYIyTxLWwWE0NLxprLabGFYbPnUaIbgwwtbvZ2av0,3606
|
|
8
|
-
mcp_standalone/server.py,sha256=jDCns5Cgb5JZahbx8pjCCDqryT-RiLCOFD5FIpgr3OA,9149
|
|
9
|
-
mcp_standalone/tools.py,sha256=totbebwVzqOej1TjmY6lOZ7raSPFIwGWmWfOFVN3IyE,18734
|
|
10
|
-
gfp_mcp-0.2.1.dist-info/METADATA,sha256=16kFH1_7TvI11tZnuXXrdTyQ6os5s7I5E7mTvSdRfVA,6858
|
|
11
|
-
gfp_mcp-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
-
gfp_mcp-0.2.1.dist-info/entry_points.txt,sha256=mgyus9dsB_8mjgnztuHNPqzPi-7HcPg1iYzfM5NMIjk,61
|
|
13
|
-
gfp_mcp-0.2.1.dist-info/top_level.txt,sha256=g2hRJHoDDPNtrNdXR70T7FR9Ev6DTRJiGW7ZvlvnXMc,15
|
|
14
|
-
gfp_mcp-0.2.1.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
mcp_standalone
|
mcp_standalone/config.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"""Configuration management for MCP standalone server."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
from typing import Final
|
|
7
|
-
|
|
8
|
-
__all__ = ["MCPConfig"]
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class MCPConfig:
|
|
12
|
-
"""Configuration for MCP standalone server.
|
|
13
|
-
|
|
14
|
-
Manages environment variables and default settings for the MCP server
|
|
15
|
-
that proxies requests to the FastAPI backend.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
# FastAPI base URL (default: http://localhost:8787)
|
|
19
|
-
# This default is primarily for backward compatibility.
|
|
20
|
-
# The MCP server automatically discovers running servers via the registry.
|
|
21
|
-
API_URL: Final[str] = os.getenv("GFP_API_URL", "http://localhost:8787")
|
|
22
|
-
|
|
23
|
-
# Timeout for tool calls in seconds (default: 300 = 5 minutes)
|
|
24
|
-
TIMEOUT: Final[int] = int(os.getenv("GFP_MCP_TIMEOUT", "300"))
|
|
25
|
-
|
|
26
|
-
# Enable debug logging
|
|
27
|
-
DEBUG: Final[bool] = os.getenv("GFP_MCP_DEBUG", "false").lower() in (
|
|
28
|
-
"true",
|
|
29
|
-
"1",
|
|
30
|
-
"yes",
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
# Retry configuration
|
|
34
|
-
MAX_RETRIES: Final[int] = 3
|
|
35
|
-
RETRY_BACKOFF: Final[float] = 0.5 # Initial backoff in seconds
|
|
36
|
-
|
|
37
|
-
@classmethod
|
|
38
|
-
def get_api_url(cls, override: str | None = None) -> str:
|
|
39
|
-
"""Get the FastAPI base URL.
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
override: Optional URL to override the environment variable
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
The API base URL
|
|
46
|
-
"""
|
|
47
|
-
return override or cls.API_URL
|
|
48
|
-
|
|
49
|
-
@classmethod
|
|
50
|
-
def get_timeout(cls) -> int:
|
|
51
|
-
"""Get the timeout for tool calls.
|
|
52
|
-
|
|
53
|
-
Returns:
|
|
54
|
-
Timeout in seconds
|
|
55
|
-
"""
|
|
56
|
-
return cls.TIMEOUT
|
mcp_standalone/mappings.py
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
"""Mappings from MCP tools to FastAPI endpoints.
|
|
2
|
-
|
|
3
|
-
This module defines how MCP tool arguments are transformed into HTTP
|
|
4
|
-
requests to the FastAPI backend, and how responses are transformed back.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
from collections.abc import Callable
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
__all__ = [
|
|
13
|
-
"TOOL_MAPPINGS",
|
|
14
|
-
"get_mapping",
|
|
15
|
-
"transform_request",
|
|
16
|
-
"transform_response",
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class EndpointMapping:
|
|
21
|
-
"""Configuration for mapping an MCP tool to a FastAPI endpoint."""
|
|
22
|
-
|
|
23
|
-
def __init__(
|
|
24
|
-
self,
|
|
25
|
-
method: str,
|
|
26
|
-
path: str,
|
|
27
|
-
request_transformer: Callable[[dict[str, Any]], dict[str, Any]] | None = None,
|
|
28
|
-
response_transformer: Callable[[Any], Any] | None = None,
|
|
29
|
-
) -> None:
|
|
30
|
-
"""Initialize endpoint mapping.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
method: HTTP method (GET, POST, etc.)
|
|
34
|
-
path: API endpoint path
|
|
35
|
-
request_transformer: Optional function to transform MCP args to
|
|
36
|
-
HTTP params
|
|
37
|
-
response_transformer: Optional function to transform HTTP response
|
|
38
|
-
to MCP format
|
|
39
|
-
"""
|
|
40
|
-
self.method = method
|
|
41
|
-
self.path = path
|
|
42
|
-
self.request_transformer = request_transformer or (lambda x: x)
|
|
43
|
-
self.response_transformer = response_transformer or (lambda x: x)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _build_cell_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
47
|
-
"""Transform build_cell MCP args to FastAPI params.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
args: MCP tool arguments
|
|
51
|
-
|
|
52
|
-
Returns:
|
|
53
|
-
Dict with 'params' key for query parameters
|
|
54
|
-
"""
|
|
55
|
-
return {
|
|
56
|
-
"params": {
|
|
57
|
-
"name": args["name"],
|
|
58
|
-
"with_metadata": args.get("with_metadata", True),
|
|
59
|
-
"register": args.get("register", True),
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _build_cells_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
65
|
-
"""Transform build_cells MCP args to FastAPI params.
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
args: MCP tool arguments
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
Dict with 'params' key for query parameters and 'json_data' for body
|
|
72
|
-
"""
|
|
73
|
-
return {
|
|
74
|
-
"params": {
|
|
75
|
-
"with_metadata": args.get("with_metadata", True),
|
|
76
|
-
"register": args.get("register", True),
|
|
77
|
-
},
|
|
78
|
-
"json_data": args["names"],
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def _list_cells_response(response: Any) -> dict[str, Any]:
|
|
83
|
-
"""Transform list_cells response to MCP format.
|
|
84
|
-
|
|
85
|
-
Args:
|
|
86
|
-
response: FastAPI response (list of cell names)
|
|
87
|
-
|
|
88
|
-
Returns:
|
|
89
|
-
Formatted response with cell names
|
|
90
|
-
"""
|
|
91
|
-
if isinstance(response, list):
|
|
92
|
-
return {"cells": response, "count": len(response)}
|
|
93
|
-
return response
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _get_cell_info_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
97
|
-
"""Transform get_cell_info MCP args to FastAPI params.
|
|
98
|
-
|
|
99
|
-
Args:
|
|
100
|
-
args: MCP tool arguments
|
|
101
|
-
|
|
102
|
-
Returns:
|
|
103
|
-
Dict with 'params' key for query parameters
|
|
104
|
-
"""
|
|
105
|
-
return {"params": {"name": args["name"]}}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def _download_gds_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
109
|
-
"""Transform download_gds MCP args to FastAPI path.
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
args: MCP tool arguments
|
|
113
|
-
|
|
114
|
-
Returns:
|
|
115
|
-
Dict with modified 'path' for the endpoint
|
|
116
|
-
"""
|
|
117
|
-
path = args["path"]
|
|
118
|
-
# The path template in FastAPI is /api/download/{path:path}.gds
|
|
119
|
-
# We need to construct the full path
|
|
120
|
-
return {"path": f"/api/download/{path}.gds"}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def _download_gds_response(response: Any) -> dict[str, Any]:
|
|
124
|
-
"""Transform download_gds response to MCP format.
|
|
125
|
-
|
|
126
|
-
Args:
|
|
127
|
-
response: FastAPI response (file path or error)
|
|
128
|
-
|
|
129
|
-
Returns:
|
|
130
|
-
Formatted response with file path
|
|
131
|
-
"""
|
|
132
|
-
# If response is a string (text), it's likely the file content or path
|
|
133
|
-
if isinstance(response, str):
|
|
134
|
-
return {"status": "success", "message": response}
|
|
135
|
-
return response
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def _check_drc_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
139
|
-
"""Transform check_drc MCP args to FastAPI params.
|
|
140
|
-
|
|
141
|
-
Args:
|
|
142
|
-
args: MCP tool arguments
|
|
143
|
-
|
|
144
|
-
Returns:
|
|
145
|
-
Dict with 'json_data' key for request body
|
|
146
|
-
"""
|
|
147
|
-
json_data: dict[str, Any] = {"path": args["path"]}
|
|
148
|
-
|
|
149
|
-
# Add optional parameters if provided
|
|
150
|
-
if "pdk" in args and args["pdk"]:
|
|
151
|
-
json_data["pdk"] = args["pdk"]
|
|
152
|
-
if "process" in args and args["process"]:
|
|
153
|
-
json_data["process"] = args["process"]
|
|
154
|
-
if "timeout" in args and args["timeout"]:
|
|
155
|
-
json_data["timeout"] = args["timeout"]
|
|
156
|
-
if "host" in args and args["host"]:
|
|
157
|
-
json_data["host"] = args["host"]
|
|
158
|
-
|
|
159
|
-
return {"json_data": json_data}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def _check_connectivity_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
163
|
-
"""Transform check_connectivity MCP args to FastAPI params.
|
|
164
|
-
|
|
165
|
-
Args:
|
|
166
|
-
args: MCP tool arguments
|
|
167
|
-
|
|
168
|
-
Returns:
|
|
169
|
-
Dict with 'json_data' key for request body
|
|
170
|
-
"""
|
|
171
|
-
return {"json_data": {"path": args["path"]}}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def _check_lvs_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
175
|
-
"""Transform check_lvs MCP args to FastAPI params.
|
|
176
|
-
|
|
177
|
-
Args:
|
|
178
|
-
args: MCP tool arguments
|
|
179
|
-
|
|
180
|
-
Returns:
|
|
181
|
-
Dict with 'json_data' key for request body
|
|
182
|
-
"""
|
|
183
|
-
return {
|
|
184
|
-
"json_data": {
|
|
185
|
-
"cell": args["cell"],
|
|
186
|
-
"netpath": args["netpath"],
|
|
187
|
-
"cellargs": args.get("cellargs", ""),
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def _simulate_component_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
193
|
-
"""Transform simulate_component MCP args to FastAPI params.
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
args: MCP tool arguments
|
|
197
|
-
|
|
198
|
-
Returns:
|
|
199
|
-
Dict with 'params' key for query parameters
|
|
200
|
-
"""
|
|
201
|
-
return {"params": {"name": args["name"]}}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def _get_port_center_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
205
|
-
"""Transform get_port_center MCP args to FastAPI params.
|
|
206
|
-
|
|
207
|
-
Args:
|
|
208
|
-
args: MCP tool arguments
|
|
209
|
-
|
|
210
|
-
Returns:
|
|
211
|
-
Dict with 'params' key for query parameters
|
|
212
|
-
"""
|
|
213
|
-
return {
|
|
214
|
-
"params": {
|
|
215
|
-
"netlist": args["netlist"],
|
|
216
|
-
"instance": args["instance"],
|
|
217
|
-
"port": args["port"],
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def _generate_bbox_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
223
|
-
"""Transform generate_bbox MCP args to FastAPI params.
|
|
224
|
-
|
|
225
|
-
Args:
|
|
226
|
-
args: MCP tool arguments
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
Dict with 'json_data' key for request body
|
|
230
|
-
"""
|
|
231
|
-
json_data: dict[str, Any] = {"path": args["path"]}
|
|
232
|
-
|
|
233
|
-
# Add optional parameters if provided
|
|
234
|
-
if "outpath" in args and args["outpath"]:
|
|
235
|
-
json_data["outpath"] = args["outpath"]
|
|
236
|
-
if "layers_to_keep" in args and args["layers_to_keep"]:
|
|
237
|
-
json_data["layers_to_keep"] = args["layers_to_keep"]
|
|
238
|
-
if "bbox_layer" in args and args["bbox_layer"]:
|
|
239
|
-
json_data["bbox_layer"] = args["bbox_layer"]
|
|
240
|
-
if "ignore_ports" in args:
|
|
241
|
-
json_data["ignore_ports"] = args["ignore_ports"]
|
|
242
|
-
|
|
243
|
-
return {"json_data": json_data}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def _freeze_cell_request(args: dict[str, Any]) -> dict[str, Any]:
|
|
247
|
-
"""Transform freeze_cell MCP args to FastAPI params.
|
|
248
|
-
|
|
249
|
-
Args:
|
|
250
|
-
args: MCP tool arguments
|
|
251
|
-
|
|
252
|
-
Returns:
|
|
253
|
-
Dict with 'path' and 'json_data' for the request
|
|
254
|
-
"""
|
|
255
|
-
cell_name = args["cell_name"]
|
|
256
|
-
kwargs = args.get("kwargs", {})
|
|
257
|
-
|
|
258
|
-
# The freeze endpoint expects a JSON string in the body.
|
|
259
|
-
# httpx with json= will JSON-encode the dict and send it as the body,
|
|
260
|
-
# which FastAPI's Body() will read as a string.
|
|
261
|
-
return {
|
|
262
|
-
"path": f"/freeze/{cell_name}",
|
|
263
|
-
"json_data": kwargs, # httpx will JSON-encode this to a string
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
# Tool name -> Endpoint mapping
|
|
268
|
-
TOOL_MAPPINGS: dict[str, EndpointMapping] = {
|
|
269
|
-
# Phase 1: Core Building Tools
|
|
270
|
-
"build_cell": EndpointMapping(
|
|
271
|
-
method="GET",
|
|
272
|
-
path="/api/build-cell",
|
|
273
|
-
request_transformer=_build_cell_request,
|
|
274
|
-
),
|
|
275
|
-
"build_cells": EndpointMapping(
|
|
276
|
-
method="POST",
|
|
277
|
-
path="/api/build-cells",
|
|
278
|
-
request_transformer=_build_cells_request,
|
|
279
|
-
),
|
|
280
|
-
"list_cells": EndpointMapping(
|
|
281
|
-
method="GET",
|
|
282
|
-
path="/api/cells",
|
|
283
|
-
response_transformer=_list_cells_response,
|
|
284
|
-
),
|
|
285
|
-
"get_cell_info": EndpointMapping(
|
|
286
|
-
method="GET",
|
|
287
|
-
path="/api/cell-info",
|
|
288
|
-
request_transformer=_get_cell_info_request,
|
|
289
|
-
),
|
|
290
|
-
"download_gds": EndpointMapping(
|
|
291
|
-
method="GET",
|
|
292
|
-
path="/api/download/{path}.gds",
|
|
293
|
-
request_transformer=_download_gds_request,
|
|
294
|
-
response_transformer=_download_gds_response,
|
|
295
|
-
),
|
|
296
|
-
# Phase 2: Verification Tools
|
|
297
|
-
"check_drc": EndpointMapping(
|
|
298
|
-
method="POST",
|
|
299
|
-
path="/api/check-drc",
|
|
300
|
-
request_transformer=_check_drc_request,
|
|
301
|
-
),
|
|
302
|
-
"check_connectivity": EndpointMapping(
|
|
303
|
-
method="POST",
|
|
304
|
-
path="/api/check-connectivity",
|
|
305
|
-
request_transformer=_check_connectivity_request,
|
|
306
|
-
),
|
|
307
|
-
"check_lvs": EndpointMapping(
|
|
308
|
-
method="POST",
|
|
309
|
-
path="/api/check-lvs",
|
|
310
|
-
request_transformer=_check_lvs_request,
|
|
311
|
-
),
|
|
312
|
-
# Phase 4: Simulation & Advanced Tools
|
|
313
|
-
"simulate_component": EndpointMapping(
|
|
314
|
-
method="GET",
|
|
315
|
-
path="/api/simulate",
|
|
316
|
-
request_transformer=_simulate_component_request,
|
|
317
|
-
),
|
|
318
|
-
"get_port_center": EndpointMapping(
|
|
319
|
-
method="GET",
|
|
320
|
-
path="/api/port-center",
|
|
321
|
-
request_transformer=_get_port_center_request,
|
|
322
|
-
),
|
|
323
|
-
"generate_bbox": EndpointMapping(
|
|
324
|
-
method="POST",
|
|
325
|
-
path="/api/bbox",
|
|
326
|
-
request_transformer=_generate_bbox_request,
|
|
327
|
-
),
|
|
328
|
-
"freeze_cell": EndpointMapping(
|
|
329
|
-
method="POST",
|
|
330
|
-
path="/freeze/{cell_name}",
|
|
331
|
-
request_transformer=_freeze_cell_request,
|
|
332
|
-
),
|
|
333
|
-
"get_pdk_info": EndpointMapping(
|
|
334
|
-
method="GET",
|
|
335
|
-
path="/info",
|
|
336
|
-
),
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
def get_mapping(tool_name: str) -> EndpointMapping | None:
|
|
341
|
-
"""Get the endpoint mapping for a tool.
|
|
342
|
-
|
|
343
|
-
Args:
|
|
344
|
-
tool_name: Name of the MCP tool
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
EndpointMapping or None if not found
|
|
348
|
-
"""
|
|
349
|
-
return TOOL_MAPPINGS.get(tool_name)
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
def transform_request(
|
|
353
|
-
tool_name: str,
|
|
354
|
-
args: dict[str, Any],
|
|
355
|
-
) -> dict[str, Any]:
|
|
356
|
-
"""Transform MCP tool arguments to HTTP request parameters.
|
|
357
|
-
|
|
358
|
-
Args:
|
|
359
|
-
tool_name: Name of the MCP tool
|
|
360
|
-
args: Tool arguments from MCP
|
|
361
|
-
|
|
362
|
-
Returns:
|
|
363
|
-
Dict containing HTTP request parameters (params, json_data, path, etc.)
|
|
364
|
-
"""
|
|
365
|
-
mapping = get_mapping(tool_name)
|
|
366
|
-
if mapping is None:
|
|
367
|
-
return {}
|
|
368
|
-
|
|
369
|
-
return mapping.request_transformer(args)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
def transform_response(tool_name: str, response: Any) -> Any:
|
|
373
|
-
"""Transform HTTP response to MCP format.
|
|
374
|
-
|
|
375
|
-
Args:
|
|
376
|
-
tool_name: Name of the MCP tool
|
|
377
|
-
response: HTTP response from FastAPI
|
|
378
|
-
|
|
379
|
-
Returns:
|
|
380
|
-
Transformed response for MCP
|
|
381
|
-
"""
|
|
382
|
-
mapping = get_mapping(tool_name)
|
|
383
|
-
if mapping is None:
|
|
384
|
-
return response
|
|
385
|
-
|
|
386
|
-
return mapping.response_transformer(response)
|