gfp-mcp 0.1.0__py3-none-any.whl → 0.2.4__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-0.2.4.dist-info/METADATA +227 -0
- gfp_mcp-0.2.4.dist-info/RECORD +14 -0
- {gfp_mcp-0.1.0.dist-info → gfp_mcp-0.2.4.dist-info}/WHEEL +1 -1
- gfp_mcp-0.2.4.dist-info/licenses/LICENSE +4 -0
- mcp_standalone/__init__.py +4 -1
- mcp_standalone/client.py +61 -19
- mcp_standalone/config.py +3 -7
- mcp_standalone/mappings.py +341 -62
- mcp_standalone/registry.py +0 -4
- mcp_standalone/resources.py +138 -0
- mcp_standalone/server.py +78 -24
- mcp_standalone/tools.py +169 -71
- gfp_mcp-0.1.0.dist-info/METADATA +0 -360
- gfp_mcp-0.1.0.dist-info/RECORD +0 -12
- {gfp_mcp-0.1.0.dist-info → gfp_mcp-0.2.4.dist-info}/entry_points.txt +0 -0
- {gfp_mcp-0.1.0.dist-info → gfp_mcp-0.2.4.dist-info}/top_level.txt +0 -0
mcp_standalone/server.py
CHANGED
|
@@ -13,11 +13,17 @@ from typing import Any
|
|
|
13
13
|
|
|
14
14
|
from mcp.server import Server
|
|
15
15
|
from mcp.server.stdio import stdio_server
|
|
16
|
-
from mcp.types import
|
|
16
|
+
from mcp.types import (
|
|
17
|
+
Resource,
|
|
18
|
+
TextContent,
|
|
19
|
+
Tool,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
22
|
from .client import FastAPIClient
|
|
19
23
|
from .config import MCPConfig
|
|
20
24
|
from .mappings import get_mapping, transform_request, transform_response
|
|
25
|
+
from .registry import get_registry_path
|
|
26
|
+
from .resources import get_all_resources, get_resource_content
|
|
21
27
|
from .tools import get_all_tools
|
|
22
28
|
|
|
23
29
|
__all__ = ["create_server", "run_server", "main"]
|
|
@@ -34,10 +40,8 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
34
40
|
Returns:
|
|
35
41
|
Configured MCP Server instance
|
|
36
42
|
"""
|
|
37
|
-
# Create server instance
|
|
38
43
|
server = Server("gdsfactoryplus")
|
|
39
44
|
|
|
40
|
-
# Create HTTP client for FastAPI backend
|
|
41
45
|
client = FastAPIClient(api_url)
|
|
42
46
|
|
|
43
47
|
@server.list_tools()
|
|
@@ -51,6 +55,40 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
51
55
|
logger.info("Listing %d tools", len(tools))
|
|
52
56
|
return tools
|
|
53
57
|
|
|
58
|
+
@server.list_resources()
|
|
59
|
+
async def list_resources() -> list[Resource]:
|
|
60
|
+
"""List all available MCP resources.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of resource definitions
|
|
64
|
+
"""
|
|
65
|
+
resources = get_all_resources()
|
|
66
|
+
logger.info("Listing %d resources", len(resources))
|
|
67
|
+
return resources
|
|
68
|
+
|
|
69
|
+
@server.read_resource()
|
|
70
|
+
async def read_resource(uri: str) -> str:
|
|
71
|
+
"""Read a specific resource by URI.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
uri: Resource URI
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Resource content as string
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: If resource URI is not found
|
|
81
|
+
"""
|
|
82
|
+
logger.info("Resource requested: %s", uri)
|
|
83
|
+
|
|
84
|
+
content = get_resource_content(uri)
|
|
85
|
+
if content is None:
|
|
86
|
+
error_msg = f"Unknown resource URI: {uri}"
|
|
87
|
+
logger.error(error_msg)
|
|
88
|
+
raise ValueError(error_msg)
|
|
89
|
+
|
|
90
|
+
return content
|
|
91
|
+
|
|
54
92
|
@server.call_tool()
|
|
55
93
|
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]: # noqa: PLR0911
|
|
56
94
|
"""Call an MCP tool.
|
|
@@ -65,7 +103,6 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
65
103
|
logger.info("Tool called: %s", name)
|
|
66
104
|
logger.debug("Arguments: %s", arguments)
|
|
67
105
|
|
|
68
|
-
# Handle special tools that don't require HTTP requests
|
|
69
106
|
if name == "list_projects":
|
|
70
107
|
try:
|
|
71
108
|
projects = client.list_projects()
|
|
@@ -97,7 +134,6 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
97
134
|
logger.exception(error_msg)
|
|
98
135
|
return [TextContent(type="text", text=json.dumps({"error": error_msg}))]
|
|
99
136
|
|
|
100
|
-
# Get the endpoint mapping
|
|
101
137
|
mapping = get_mapping(name)
|
|
102
138
|
if mapping is None:
|
|
103
139
|
error_msg = f"Unknown tool: {name}"
|
|
@@ -105,21 +141,17 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
105
141
|
return [TextContent(type="text", text=json.dumps({"error": error_msg}))]
|
|
106
142
|
|
|
107
143
|
try:
|
|
108
|
-
# Extract project parameter (optional)
|
|
109
144
|
project = arguments.get("project")
|
|
110
145
|
|
|
111
|
-
# Transform MCP arguments to HTTP request parameters
|
|
112
146
|
transformed = transform_request(name, arguments)
|
|
113
147
|
logger.debug("Transformed request: %s", transformed)
|
|
114
148
|
|
|
115
|
-
# Extract request parameters
|
|
116
149
|
method = mapping.method
|
|
117
150
|
path = transformed.get("path", mapping.path)
|
|
118
151
|
params = transformed.get("params")
|
|
119
152
|
json_data = transformed.get("json_data")
|
|
120
153
|
data = transformed.get("data")
|
|
121
154
|
|
|
122
|
-
# Make HTTP request to FastAPI backend (with optional project routing)
|
|
123
155
|
response = await client.request(
|
|
124
156
|
method=method,
|
|
125
157
|
path=path,
|
|
@@ -129,11 +161,9 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
129
161
|
project=project,
|
|
130
162
|
)
|
|
131
163
|
|
|
132
|
-
# Transform response to MCP format
|
|
133
164
|
result = transform_response(name, response)
|
|
134
165
|
logger.debug("Tool result: %s", result)
|
|
135
166
|
|
|
136
|
-
# Return as text content
|
|
137
167
|
return [
|
|
138
168
|
TextContent(
|
|
139
169
|
type="text",
|
|
@@ -151,19 +181,51 @@ def create_server(api_url: str | None = None) -> Server:
|
|
|
151
181
|
)
|
|
152
182
|
]
|
|
153
183
|
|
|
154
|
-
# Store client reference for cleanup
|
|
155
184
|
server._http_client = client # type: ignore[attr-defined] # noqa: SLF001
|
|
156
185
|
|
|
157
186
|
return server
|
|
158
187
|
|
|
159
188
|
|
|
189
|
+
def _log_server_discovery(client: FastAPIClient) -> None:
|
|
190
|
+
"""Log discovered GDSFactory+ servers at startup."""
|
|
191
|
+
try:
|
|
192
|
+
projects = client.list_projects()
|
|
193
|
+
if projects:
|
|
194
|
+
logger.info("Discovered %d GDSFactory+ server(s):", len(projects))
|
|
195
|
+
for project in projects[:3]: # Show first 3
|
|
196
|
+
logger.info(
|
|
197
|
+
" - %s (port %d, PDK: %s)",
|
|
198
|
+
project["project_name"],
|
|
199
|
+
project["port"],
|
|
200
|
+
project.get("pdk", "unknown"),
|
|
201
|
+
)
|
|
202
|
+
if len(projects) > 3:
|
|
203
|
+
logger.info(" ... and %d more", len(projects) - 3)
|
|
204
|
+
else:
|
|
205
|
+
logger.warning(
|
|
206
|
+
"No GDSFactory+ servers found. To start a server:\n"
|
|
207
|
+
" 1. Open VSCode with the GDSFactory+ extension installed\n"
|
|
208
|
+
" 2. Open a GDSFactory+ project folder\n"
|
|
209
|
+
" 3. The extension will automatically start and register the server"
|
|
210
|
+
)
|
|
211
|
+
logger.info("Registry location: %s", get_registry_path())
|
|
212
|
+
logger.info(
|
|
213
|
+
"Alternatively, set GFP_API_URL environment variable to connect to a specific server"
|
|
214
|
+
)
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.warning("Could not read server registry: %s", e)
|
|
217
|
+
logger.info(
|
|
218
|
+
"If GDSFactory+ is running, ensure registry is accessible at: %s",
|
|
219
|
+
get_registry_path(),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
160
223
|
async def run_server(api_url: str | None = None) -> None:
|
|
161
224
|
"""Run the MCP server with STDIO transport.
|
|
162
225
|
|
|
163
226
|
Args:
|
|
164
227
|
api_url: Optional FastAPI base URL (default from config)
|
|
165
228
|
"""
|
|
166
|
-
# Configure logging
|
|
167
229
|
log_level = logging.DEBUG if MCPConfig.DEBUG else logging.INFO
|
|
168
230
|
logging.basicConfig(
|
|
169
231
|
level=log_level,
|
|
@@ -171,25 +233,18 @@ async def run_server(api_url: str | None = None) -> None:
|
|
|
171
233
|
)
|
|
172
234
|
|
|
173
235
|
logger.info("Starting GDSFactory+ MCP server")
|
|
174
|
-
|
|
236
|
+
base_url_info = MCPConfig.get_api_url(api_url) or "registry-based routing"
|
|
237
|
+
logger.info("Base URL: %s", base_url_info)
|
|
175
238
|
logger.info("Timeout: %ds", MCPConfig.get_timeout())
|
|
176
239
|
|
|
177
|
-
# Create server
|
|
178
240
|
server = create_server(api_url)
|
|
179
241
|
client: FastAPIClient = server._http_client # type: ignore[attr-defined] # noqa: SLF001
|
|
180
242
|
|
|
181
243
|
try:
|
|
182
|
-
# Start HTTP client
|
|
183
244
|
await client.start()
|
|
184
245
|
|
|
185
|
-
|
|
186
|
-
healthy = await client.health_check()
|
|
187
|
-
if not healthy:
|
|
188
|
-
logger.warning(
|
|
189
|
-
"FastAPI server health check failed. Server may not be running."
|
|
190
|
-
)
|
|
246
|
+
_log_server_discovery(client)
|
|
191
247
|
|
|
192
|
-
# Run server with STDIO transport
|
|
193
248
|
async with stdio_server() as (read_stream, write_stream):
|
|
194
249
|
logger.info("MCP server ready (STDIO transport)")
|
|
195
250
|
await server.run(
|
|
@@ -204,7 +259,6 @@ async def run_server(api_url: str | None = None) -> None:
|
|
|
204
259
|
logger.exception("MCP server error")
|
|
205
260
|
raise
|
|
206
261
|
finally:
|
|
207
|
-
# Cleanup
|
|
208
262
|
await client.close()
|
|
209
263
|
logger.info("MCP server stopped")
|
|
210
264
|
|
mcp_standalone/tools.py
CHANGED
|
@@ -15,7 +15,6 @@ __all__ = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
# Project discovery tools (for multi-project support)
|
|
19
18
|
PROJECT_TOOLS: list[Tool] = [
|
|
20
19
|
Tool(
|
|
21
20
|
name="list_projects",
|
|
@@ -52,14 +51,13 @@ PROJECT_TOOLS: list[Tool] = [
|
|
|
52
51
|
),
|
|
53
52
|
]
|
|
54
53
|
|
|
55
|
-
# Standard optional project parameter for all tools
|
|
56
54
|
PROJECT_PARAM_SCHEMA = {
|
|
57
55
|
"project": {
|
|
58
56
|
"type": "string",
|
|
59
57
|
"description": (
|
|
60
58
|
"Optional project name or path to route this request to a specific "
|
|
61
|
-
"server. If not provided, uses the
|
|
62
|
-
"Use list_projects to see available projects."
|
|
59
|
+
"server. If not provided, uses the first available server from the registry "
|
|
60
|
+
"or GFP_API_URL if set. Use list_projects to see available projects."
|
|
63
61
|
),
|
|
64
62
|
},
|
|
65
63
|
}
|
|
@@ -80,48 +78,13 @@ def _add_project_param(schema: dict) -> dict:
|
|
|
80
78
|
return schema
|
|
81
79
|
|
|
82
80
|
|
|
83
|
-
# Phase 1: Core Building Tools (5 tools)
|
|
84
81
|
CORE_TOOLS: list[Tool] = [
|
|
85
|
-
Tool(
|
|
86
|
-
name="build_cell",
|
|
87
|
-
description=(
|
|
88
|
-
"Build a single GDS cell by name. This creates the physical layout "
|
|
89
|
-
"file (.gds) for a photonic component. The build happens in the "
|
|
90
|
-
"background and the GDS file will be saved to the project build "
|
|
91
|
-
"directory."
|
|
92
|
-
),
|
|
93
|
-
inputSchema=_add_project_param(
|
|
94
|
-
{
|
|
95
|
-
"type": "object",
|
|
96
|
-
"properties": {
|
|
97
|
-
"name": {
|
|
98
|
-
"type": "string",
|
|
99
|
-
"description": "Name of the cell/component to build",
|
|
100
|
-
},
|
|
101
|
-
"with_metadata": {
|
|
102
|
-
"type": "boolean",
|
|
103
|
-
"description": (
|
|
104
|
-
"Include metadata in the GDS file (default: true)"
|
|
105
|
-
),
|
|
106
|
-
"default": True,
|
|
107
|
-
},
|
|
108
|
-
"register": {
|
|
109
|
-
"type": "boolean",
|
|
110
|
-
"description": (
|
|
111
|
-
"Re-register the cell in the KLayout cache (default: true)"
|
|
112
|
-
),
|
|
113
|
-
"default": True,
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
"required": ["name"],
|
|
117
|
-
}
|
|
118
|
-
),
|
|
119
|
-
),
|
|
120
82
|
Tool(
|
|
121
83
|
name="build_cells",
|
|
122
84
|
description=(
|
|
123
|
-
"Build
|
|
124
|
-
"
|
|
85
|
+
"Build one or more GDS cells by name. This creates the physical layout "
|
|
86
|
+
"files (.gds) for photonic components. Pass a list of cell names to build. "
|
|
87
|
+
"For a single cell, pass a list with one element. All cells are built "
|
|
125
88
|
"in the background and saved to the project build directory."
|
|
126
89
|
),
|
|
127
90
|
inputSchema=_add_project_param(
|
|
@@ -131,7 +94,7 @@ CORE_TOOLS: list[Tool] = [
|
|
|
131
94
|
"names": {
|
|
132
95
|
"type": "array",
|
|
133
96
|
"items": {"type": "string"},
|
|
134
|
-
"description": "List of cell/component names to build",
|
|
97
|
+
"description": "List of cell/component names to build (can be a single-item list)",
|
|
135
98
|
},
|
|
136
99
|
"with_metadata": {
|
|
137
100
|
"type": "boolean",
|
|
@@ -186,32 +149,8 @@ CORE_TOOLS: list[Tool] = [
|
|
|
186
149
|
}
|
|
187
150
|
),
|
|
188
151
|
),
|
|
189
|
-
Tool(
|
|
190
|
-
name="download_gds",
|
|
191
|
-
description=(
|
|
192
|
-
"Download a GDS file from the project build directory. Returns the "
|
|
193
|
-
"file path to the downloaded GDS file. The file must have been "
|
|
194
|
-
"previously built using build_cell or build_cells."
|
|
195
|
-
),
|
|
196
|
-
inputSchema=_add_project_param(
|
|
197
|
-
{
|
|
198
|
-
"type": "object",
|
|
199
|
-
"properties": {
|
|
200
|
-
"path": {
|
|
201
|
-
"type": "string",
|
|
202
|
-
"description": (
|
|
203
|
-
"Relative path to the GDS file (without .gds extension). "
|
|
204
|
-
"For example, 'mzi' or 'components/coupler'"
|
|
205
|
-
),
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
"required": ["path"],
|
|
209
|
-
}
|
|
210
|
-
),
|
|
211
|
-
),
|
|
212
152
|
]
|
|
213
153
|
|
|
214
|
-
# Phase 2: Verification Tools
|
|
215
154
|
VERIFICATION_TOOLS: list[Tool] = [
|
|
216
155
|
Tool(
|
|
217
156
|
name="check_drc",
|
|
@@ -328,13 +267,172 @@ VERIFICATION_TOOLS: list[Tool] = [
|
|
|
328
267
|
),
|
|
329
268
|
]
|
|
330
269
|
|
|
331
|
-
# Phase 3: SPICE Workflow Tools (to be implemented)
|
|
332
270
|
SPICE_TOOLS: list[Tool] = []
|
|
333
271
|
|
|
334
|
-
|
|
335
|
-
|
|
272
|
+
ADVANCED_TOOLS: list[Tool] = [
|
|
273
|
+
Tool(
|
|
274
|
+
name="simulate_component",
|
|
275
|
+
description=(
|
|
276
|
+
"Run a SAX circuit simulation on a photonic component by name. "
|
|
277
|
+
"This simulates the optical behavior of the component using its "
|
|
278
|
+
"SAX model. Returns S-parameters showing how light propagates "
|
|
279
|
+
"through the component's ports. Use this to analyze component "
|
|
280
|
+
"performance before fabrication."
|
|
281
|
+
),
|
|
282
|
+
inputSchema=_add_project_param(
|
|
283
|
+
{
|
|
284
|
+
"type": "object",
|
|
285
|
+
"properties": {
|
|
286
|
+
"name": {
|
|
287
|
+
"type": "string",
|
|
288
|
+
"description": (
|
|
289
|
+
"Name of the component/cell to simulate. The component "
|
|
290
|
+
"must have a SAX model defined."
|
|
291
|
+
),
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
"required": ["name"],
|
|
295
|
+
}
|
|
296
|
+
),
|
|
297
|
+
),
|
|
298
|
+
Tool(
|
|
299
|
+
name="get_port_center",
|
|
300
|
+
description=(
|
|
301
|
+
"Get the center coordinates (x, y) of a specific port in a component "
|
|
302
|
+
"instance. This is useful for positioning components, routing waveguides, "
|
|
303
|
+
"or analyzing layout geometry. Returns the physical coordinates in "
|
|
304
|
+
"microns."
|
|
305
|
+
),
|
|
306
|
+
inputSchema=_add_project_param(
|
|
307
|
+
{
|
|
308
|
+
"type": "object",
|
|
309
|
+
"properties": {
|
|
310
|
+
"netlist": {
|
|
311
|
+
"type": "string",
|
|
312
|
+
"description": (
|
|
313
|
+
"Name of the component/netlist containing the instance"
|
|
314
|
+
),
|
|
315
|
+
},
|
|
316
|
+
"instance": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"description": "Name of the instance within the netlist",
|
|
319
|
+
},
|
|
320
|
+
"port": {
|
|
321
|
+
"type": "string",
|
|
322
|
+
"description": "Name of the port to get coordinates for",
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
"required": ["netlist", "instance", "port"],
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
),
|
|
329
|
+
Tool(
|
|
330
|
+
name="generate_bbox",
|
|
331
|
+
description=(
|
|
332
|
+
"Generate a bounding box GDS file from an input GDS. This creates "
|
|
333
|
+
"a simplified version of the layout with only a bounding box on "
|
|
334
|
+
"specified layers. Useful for creating abstract views, floorplanning, "
|
|
335
|
+
"or hierarchical design. Can optionally preserve specific layers and "
|
|
336
|
+
"ports."
|
|
337
|
+
),
|
|
338
|
+
inputSchema=_add_project_param(
|
|
339
|
+
{
|
|
340
|
+
"type": "object",
|
|
341
|
+
"properties": {
|
|
342
|
+
"path": {
|
|
343
|
+
"type": "string",
|
|
344
|
+
"description": (
|
|
345
|
+
"Path to the input GDS file. Can be absolute or relative "
|
|
346
|
+
"to the project directory."
|
|
347
|
+
),
|
|
348
|
+
},
|
|
349
|
+
"outpath": {
|
|
350
|
+
"type": "string",
|
|
351
|
+
"description": (
|
|
352
|
+
"Output path for the bounding box GDS. If not specified, "
|
|
353
|
+
"uses the input filename with '-bbox' suffix."
|
|
354
|
+
),
|
|
355
|
+
"default": "",
|
|
356
|
+
},
|
|
357
|
+
"layers_to_keep": {
|
|
358
|
+
"type": "array",
|
|
359
|
+
"items": {"type": "string"},
|
|
360
|
+
"description": (
|
|
361
|
+
"List of layer names to preserve in the output. "
|
|
362
|
+
"Other layers will be replaced by the bounding box."
|
|
363
|
+
),
|
|
364
|
+
"default": [],
|
|
365
|
+
},
|
|
366
|
+
"bbox_layer": {
|
|
367
|
+
"type": "array",
|
|
368
|
+
"items": {"type": "integer"},
|
|
369
|
+
"description": (
|
|
370
|
+
"Layer (as [layer, datatype]) to use for the bounding box. "
|
|
371
|
+
"Default is [99, 0]."
|
|
372
|
+
),
|
|
373
|
+
"default": [99, 0],
|
|
374
|
+
},
|
|
375
|
+
"ignore_ports": {
|
|
376
|
+
"type": "boolean",
|
|
377
|
+
"description": (
|
|
378
|
+
"If true, do not include ports in the output. "
|
|
379
|
+
"Default is false."
|
|
380
|
+
),
|
|
381
|
+
"default": False,
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
"required": ["path"],
|
|
385
|
+
}
|
|
386
|
+
),
|
|
387
|
+
),
|
|
388
|
+
Tool(
|
|
389
|
+
name="freeze_cell",
|
|
390
|
+
description=(
|
|
391
|
+
"Freeze a parametric Python cell as a static schematic netlist. "
|
|
392
|
+
"This converts a gdsfactory component with specific parameters into "
|
|
393
|
+
"a fixed netlist representation in YAML format. Useful for creating "
|
|
394
|
+
"versioned snapshots of parametric designs or preparing components "
|
|
395
|
+
"for simulation workflows."
|
|
396
|
+
),
|
|
397
|
+
inputSchema=_add_project_param(
|
|
398
|
+
{
|
|
399
|
+
"type": "object",
|
|
400
|
+
"properties": {
|
|
401
|
+
"cell_name": {
|
|
402
|
+
"type": "string",
|
|
403
|
+
"description": "Name of the cell/component to freeze",
|
|
404
|
+
},
|
|
405
|
+
"kwargs": {
|
|
406
|
+
"type": "object",
|
|
407
|
+
"description": (
|
|
408
|
+
"Optional keyword arguments to pass to the component "
|
|
409
|
+
"factory. Use this to specify parameter values when "
|
|
410
|
+
"freezing the cell. Default is empty (use default params)."
|
|
411
|
+
),
|
|
412
|
+
"default": {},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
"required": ["cell_name"],
|
|
416
|
+
}
|
|
417
|
+
),
|
|
418
|
+
),
|
|
419
|
+
Tool(
|
|
420
|
+
name="get_pdk_info",
|
|
421
|
+
description=(
|
|
422
|
+
"Get information about the current PDK (Process Design Kit) in use. "
|
|
423
|
+
"Returns metadata including PDK name, project name, project path, "
|
|
424
|
+
"server port, and version. Use this to verify which PDK is active "
|
|
425
|
+
"and get project configuration details."
|
|
426
|
+
),
|
|
427
|
+
inputSchema=_add_project_param(
|
|
428
|
+
{
|
|
429
|
+
"type": "object",
|
|
430
|
+
"properties": {},
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
),
|
|
434
|
+
]
|
|
336
435
|
|
|
337
|
-
# All tools (Phase 1 + Project tools)
|
|
338
436
|
TOOLS: list[Tool] = [
|
|
339
437
|
*PROJECT_TOOLS, # Project discovery tools (always available)
|
|
340
438
|
*CORE_TOOLS,
|