gfp-mcp 0.1.0__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.1.0.dist-info/METADATA +360 -0
- gfp_mcp-0.1.0.dist-info/RECORD +12 -0
- gfp_mcp-0.1.0.dist-info/WHEEL +5 -0
- gfp_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- gfp_mcp-0.1.0.dist-info/top_level.txt +1 -0
- mcp_standalone/__init__.py +39 -0
- mcp_standalone/client.py +268 -0
- mcp_standalone/config.py +54 -0
- mcp_standalone/mappings.py +286 -0
- mcp_standalone/registry.py +210 -0
- mcp_standalone/server.py +228 -0
- mcp_standalone/tools.py +368 -0
mcp_standalone/server.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""MCP server implementation for GDSFactory+.
|
|
2
|
+
|
|
3
|
+
This module provides the main MCP server that exposes GDSFactory+ operations
|
|
4
|
+
as tools for AI assistants using the STDIO transport.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from mcp.server import Server
|
|
15
|
+
from mcp.server.stdio import stdio_server
|
|
16
|
+
from mcp.types import TextContent, Tool
|
|
17
|
+
|
|
18
|
+
from .client import FastAPIClient
|
|
19
|
+
from .config import MCPConfig
|
|
20
|
+
from .mappings import get_mapping, transform_request, transform_response
|
|
21
|
+
from .tools import get_all_tools
|
|
22
|
+
|
|
23
|
+
__all__ = ["create_server", "run_server", "main"]
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_server(api_url: str | None = None) -> Server:
|
|
29
|
+
"""Create an MCP server instance.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
api_url: Optional FastAPI base URL (default from config)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Configured MCP Server instance
|
|
36
|
+
"""
|
|
37
|
+
# Create server instance
|
|
38
|
+
server = Server("gdsfactoryplus")
|
|
39
|
+
|
|
40
|
+
# Create HTTP client for FastAPI backend
|
|
41
|
+
client = FastAPIClient(api_url)
|
|
42
|
+
|
|
43
|
+
@server.list_tools()
|
|
44
|
+
async def list_tools() -> list[Tool]:
|
|
45
|
+
"""List all available MCP tools.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
List of tool definitions
|
|
49
|
+
"""
|
|
50
|
+
tools = get_all_tools()
|
|
51
|
+
logger.info("Listing %d tools", len(tools))
|
|
52
|
+
return tools
|
|
53
|
+
|
|
54
|
+
@server.call_tool()
|
|
55
|
+
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]: # noqa: PLR0911
|
|
56
|
+
"""Call an MCP tool.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
name: Tool name
|
|
60
|
+
arguments: Tool arguments
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of text content responses
|
|
64
|
+
"""
|
|
65
|
+
logger.info("Tool called: %s", name)
|
|
66
|
+
logger.debug("Arguments: %s", arguments)
|
|
67
|
+
|
|
68
|
+
# Handle special tools that don't require HTTP requests
|
|
69
|
+
if name == "list_projects":
|
|
70
|
+
try:
|
|
71
|
+
projects = client.list_projects()
|
|
72
|
+
return [
|
|
73
|
+
TextContent(
|
|
74
|
+
type="text",
|
|
75
|
+
text=json.dumps({"projects": projects}, indent=2),
|
|
76
|
+
)
|
|
77
|
+
]
|
|
78
|
+
except Exception as e:
|
|
79
|
+
error_msg = f"Failed to list projects: {e!s}"
|
|
80
|
+
logger.exception(error_msg)
|
|
81
|
+
return [TextContent(type="text", text=json.dumps({"error": error_msg}))]
|
|
82
|
+
|
|
83
|
+
if name == "get_project_info":
|
|
84
|
+
try:
|
|
85
|
+
project = arguments.get("project")
|
|
86
|
+
if not project:
|
|
87
|
+
return [
|
|
88
|
+
TextContent(
|
|
89
|
+
type="text",
|
|
90
|
+
text=json.dumps({"error": "project parameter is required"}),
|
|
91
|
+
)
|
|
92
|
+
]
|
|
93
|
+
info = await client.get_project_info(project)
|
|
94
|
+
return [TextContent(type="text", text=json.dumps(info, indent=2))]
|
|
95
|
+
except Exception as e:
|
|
96
|
+
error_msg = f"Failed to get project info: {e!s}"
|
|
97
|
+
logger.exception(error_msg)
|
|
98
|
+
return [TextContent(type="text", text=json.dumps({"error": error_msg}))]
|
|
99
|
+
|
|
100
|
+
# Get the endpoint mapping
|
|
101
|
+
mapping = get_mapping(name)
|
|
102
|
+
if mapping is None:
|
|
103
|
+
error_msg = f"Unknown tool: {name}"
|
|
104
|
+
logger.error(error_msg)
|
|
105
|
+
return [TextContent(type="text", text=json.dumps({"error": error_msg}))]
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
# Extract project parameter (optional)
|
|
109
|
+
project = arguments.get("project")
|
|
110
|
+
|
|
111
|
+
# Transform MCP arguments to HTTP request parameters
|
|
112
|
+
transformed = transform_request(name, arguments)
|
|
113
|
+
logger.debug("Transformed request: %s", transformed)
|
|
114
|
+
|
|
115
|
+
# Extract request parameters
|
|
116
|
+
method = mapping.method
|
|
117
|
+
path = transformed.get("path", mapping.path)
|
|
118
|
+
params = transformed.get("params")
|
|
119
|
+
json_data = transformed.get("json_data")
|
|
120
|
+
data = transformed.get("data")
|
|
121
|
+
|
|
122
|
+
# Make HTTP request to FastAPI backend (with optional project routing)
|
|
123
|
+
response = await client.request(
|
|
124
|
+
method=method,
|
|
125
|
+
path=path,
|
|
126
|
+
params=params,
|
|
127
|
+
json_data=json_data,
|
|
128
|
+
data=data,
|
|
129
|
+
project=project,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Transform response to MCP format
|
|
133
|
+
result = transform_response(name, response)
|
|
134
|
+
logger.debug("Tool result: %s", result)
|
|
135
|
+
|
|
136
|
+
# Return as text content
|
|
137
|
+
return [
|
|
138
|
+
TextContent(
|
|
139
|
+
type="text",
|
|
140
|
+
text=json.dumps(result, indent=2),
|
|
141
|
+
)
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
error_msg = f"Tool execution failed: {e!s}"
|
|
146
|
+
logger.exception(error_msg)
|
|
147
|
+
return [
|
|
148
|
+
TextContent(
|
|
149
|
+
type="text",
|
|
150
|
+
text=json.dumps({"error": error_msg}),
|
|
151
|
+
)
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
# Store client reference for cleanup
|
|
155
|
+
server._http_client = client # type: ignore[attr-defined] # noqa: SLF001
|
|
156
|
+
|
|
157
|
+
return server
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
async def run_server(api_url: str | None = None) -> None:
|
|
161
|
+
"""Run the MCP server with STDIO transport.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
api_url: Optional FastAPI base URL (default from config)
|
|
165
|
+
"""
|
|
166
|
+
# Configure logging
|
|
167
|
+
log_level = logging.DEBUG if MCPConfig.DEBUG else logging.INFO
|
|
168
|
+
logging.basicConfig(
|
|
169
|
+
level=log_level,
|
|
170
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
logger.info("Starting GDSFactory+ MCP server")
|
|
174
|
+
logger.info("FastAPI base URL: %s", MCPConfig.get_api_url(api_url))
|
|
175
|
+
logger.info("Timeout: %ds", MCPConfig.get_timeout())
|
|
176
|
+
|
|
177
|
+
# Create server
|
|
178
|
+
server = create_server(api_url)
|
|
179
|
+
client: FastAPIClient = server._http_client # type: ignore[attr-defined] # noqa: SLF001
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
# Start HTTP client
|
|
183
|
+
await client.start()
|
|
184
|
+
|
|
185
|
+
# Health check
|
|
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
|
+
)
|
|
191
|
+
|
|
192
|
+
# Run server with STDIO transport
|
|
193
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
194
|
+
logger.info("MCP server ready (STDIO transport)")
|
|
195
|
+
await server.run(
|
|
196
|
+
read_stream,
|
|
197
|
+
write_stream,
|
|
198
|
+
server.create_initialization_options(),
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
except KeyboardInterrupt:
|
|
202
|
+
logger.info("Shutting down MCP server (keyboard interrupt)")
|
|
203
|
+
except Exception:
|
|
204
|
+
logger.exception("MCP server error")
|
|
205
|
+
raise
|
|
206
|
+
finally:
|
|
207
|
+
# Cleanup
|
|
208
|
+
await client.close()
|
|
209
|
+
logger.info("MCP server stopped")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def main(api_url: str | None = None) -> None:
|
|
213
|
+
"""Main entry point for MCP server.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
api_url: Optional FastAPI base URL (default from config)
|
|
217
|
+
"""
|
|
218
|
+
try:
|
|
219
|
+
asyncio.run(run_server(api_url))
|
|
220
|
+
except KeyboardInterrupt:
|
|
221
|
+
pass
|
|
222
|
+
except Exception:
|
|
223
|
+
logger.exception("Fatal error")
|
|
224
|
+
raise
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
main()
|
mcp_standalone/tools.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""MCP tool definitions for GDSFactory+.
|
|
2
|
+
|
|
3
|
+
This module defines the MCP tools that are exposed to AI assistants.
|
|
4
|
+
Tools are organized by functionality and phase of implementation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from mcp.types import Tool
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"TOOLS",
|
|
13
|
+
"get_all_tools",
|
|
14
|
+
"get_tool_by_name",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Project discovery tools (for multi-project support)
|
|
19
|
+
PROJECT_TOOLS: list[Tool] = [
|
|
20
|
+
Tool(
|
|
21
|
+
name="list_projects",
|
|
22
|
+
description=(
|
|
23
|
+
"List all active GDSFactory+ projects. Returns information about "
|
|
24
|
+
"all running servers including project name, path, port, PID, and PDK. "
|
|
25
|
+
"Use this to discover which projects are available for interaction."
|
|
26
|
+
),
|
|
27
|
+
inputSchema={
|
|
28
|
+
"type": "object",
|
|
29
|
+
"properties": {},
|
|
30
|
+
},
|
|
31
|
+
),
|
|
32
|
+
Tool(
|
|
33
|
+
name="get_project_info",
|
|
34
|
+
description=(
|
|
35
|
+
"Get detailed information about a specific project. Returns metadata "
|
|
36
|
+
"including project name, path, port, PID, PDK, and version. "
|
|
37
|
+
"Use this to get information about a running project."
|
|
38
|
+
),
|
|
39
|
+
inputSchema={
|
|
40
|
+
"type": "object",
|
|
41
|
+
"properties": {
|
|
42
|
+
"project": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": (
|
|
45
|
+
"Project name or path. Can be the project directory name "
|
|
46
|
+
"or full path."
|
|
47
|
+
),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
"required": ["project"],
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
# Standard optional project parameter for all tools
|
|
56
|
+
PROJECT_PARAM_SCHEMA = {
|
|
57
|
+
"project": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": (
|
|
60
|
+
"Optional project name or path to route this request to a specific "
|
|
61
|
+
"server. If not provided, uses the default server (port 8787). "
|
|
62
|
+
"Use list_projects to see available projects."
|
|
63
|
+
),
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _add_project_param(schema: dict) -> dict:
|
|
69
|
+
"""Add optional project parameter to a tool schema.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
schema: Original input schema
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Schema with project parameter added
|
|
76
|
+
"""
|
|
77
|
+
if "properties" not in schema:
|
|
78
|
+
schema["properties"] = {}
|
|
79
|
+
schema["properties"].update(PROJECT_PARAM_SCHEMA)
|
|
80
|
+
return schema
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Phase 1: Core Building Tools (5 tools)
|
|
84
|
+
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
|
+
Tool(
|
|
121
|
+
name="build_cells",
|
|
122
|
+
description=(
|
|
123
|
+
"Build multiple GDS cells by name in one operation. This is more "
|
|
124
|
+
"efficient than building cells one at a time. All cells are built "
|
|
125
|
+
"in the background and saved to the project build directory."
|
|
126
|
+
),
|
|
127
|
+
inputSchema=_add_project_param(
|
|
128
|
+
{
|
|
129
|
+
"type": "object",
|
|
130
|
+
"properties": {
|
|
131
|
+
"names": {
|
|
132
|
+
"type": "array",
|
|
133
|
+
"items": {"type": "string"},
|
|
134
|
+
"description": "List of cell/component names to build",
|
|
135
|
+
},
|
|
136
|
+
"with_metadata": {
|
|
137
|
+
"type": "boolean",
|
|
138
|
+
"description": (
|
|
139
|
+
"Include metadata in the GDS files (default: true)"
|
|
140
|
+
),
|
|
141
|
+
"default": True,
|
|
142
|
+
},
|
|
143
|
+
"register": {
|
|
144
|
+
"type": "boolean",
|
|
145
|
+
"description": (
|
|
146
|
+
"Re-register the cells in the KLayout cache (default: true)"
|
|
147
|
+
),
|
|
148
|
+
"default": True,
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
"required": ["names"],
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
),
|
|
155
|
+
Tool(
|
|
156
|
+
name="list_cells",
|
|
157
|
+
description=(
|
|
158
|
+
"List all available cells/components that can be built. Returns "
|
|
159
|
+
"the names of all registered component factories in the current PDK. "
|
|
160
|
+
"Use this to discover what components are available before building."
|
|
161
|
+
),
|
|
162
|
+
inputSchema=_add_project_param(
|
|
163
|
+
{
|
|
164
|
+
"type": "object",
|
|
165
|
+
"properties": {},
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
),
|
|
169
|
+
Tool(
|
|
170
|
+
name="get_cell_info",
|
|
171
|
+
description=(
|
|
172
|
+
"Get detailed information about a specific cell/component. Returns "
|
|
173
|
+
"metadata including the source file, parameters, and other details "
|
|
174
|
+
"about the component factory."
|
|
175
|
+
),
|
|
176
|
+
inputSchema=_add_project_param(
|
|
177
|
+
{
|
|
178
|
+
"type": "object",
|
|
179
|
+
"properties": {
|
|
180
|
+
"name": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"description": "Name of the cell/component to get info about",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
"required": ["name"],
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
),
|
|
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
|
+
]
|
|
213
|
+
|
|
214
|
+
# Phase 2: Verification Tools
|
|
215
|
+
VERIFICATION_TOOLS: list[Tool] = [
|
|
216
|
+
Tool(
|
|
217
|
+
name="check_drc",
|
|
218
|
+
description=(
|
|
219
|
+
"Run a full DRC (Design Rule Check) on a GDS file. This uploads "
|
|
220
|
+
"the file to a remote DRC server and runs comprehensive design rule "
|
|
221
|
+
"verification for the specified PDK and process. Use this for "
|
|
222
|
+
"complete design rule validation. Returns XML results showing all "
|
|
223
|
+
"DRC violations."
|
|
224
|
+
),
|
|
225
|
+
inputSchema=_add_project_param(
|
|
226
|
+
{
|
|
227
|
+
"type": "object",
|
|
228
|
+
"properties": {
|
|
229
|
+
"path": {
|
|
230
|
+
"type": "string",
|
|
231
|
+
"description": (
|
|
232
|
+
"Path to the GDS file to check. Can be absolute or "
|
|
233
|
+
"relative to the project directory."
|
|
234
|
+
),
|
|
235
|
+
},
|
|
236
|
+
"pdk": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"description": (
|
|
239
|
+
"PDK to use for the check. If not specified, uses the "
|
|
240
|
+
"default PDK from settings."
|
|
241
|
+
),
|
|
242
|
+
},
|
|
243
|
+
"process": {
|
|
244
|
+
"type": "string",
|
|
245
|
+
"description": (
|
|
246
|
+
"Process variant for DRC rules. If not specified, uses "
|
|
247
|
+
"the default process from settings."
|
|
248
|
+
),
|
|
249
|
+
},
|
|
250
|
+
"timeout": {
|
|
251
|
+
"type": "integer",
|
|
252
|
+
"description": (
|
|
253
|
+
"Timeout in seconds for the DRC check. If not specified, "
|
|
254
|
+
"uses the default timeout from settings."
|
|
255
|
+
),
|
|
256
|
+
},
|
|
257
|
+
"host": {
|
|
258
|
+
"type": "string",
|
|
259
|
+
"description": (
|
|
260
|
+
"API host for the DRC server. If not specified, uses "
|
|
261
|
+
"the default host from settings."
|
|
262
|
+
),
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
"required": ["path"],
|
|
266
|
+
}
|
|
267
|
+
),
|
|
268
|
+
),
|
|
269
|
+
Tool(
|
|
270
|
+
name="check_connectivity",
|
|
271
|
+
description=(
|
|
272
|
+
"Run a local connectivity check on a GDS file. This verifies that "
|
|
273
|
+
"all layers are properly connected and identifies any connectivity "
|
|
274
|
+
"violations. This is a fast, local check (does not require uploading "
|
|
275
|
+
"to a remote server). Use this to quickly check for disconnected "
|
|
276
|
+
"components. Returns XML results showing connectivity issues."
|
|
277
|
+
),
|
|
278
|
+
inputSchema=_add_project_param(
|
|
279
|
+
{
|
|
280
|
+
"type": "object",
|
|
281
|
+
"properties": {
|
|
282
|
+
"path": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"description": (
|
|
285
|
+
"Path to the GDS file to check. Can be absolute or "
|
|
286
|
+
"relative to the project directory."
|
|
287
|
+
),
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
"required": ["path"],
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
),
|
|
294
|
+
Tool(
|
|
295
|
+
name="check_lvs",
|
|
296
|
+
description=(
|
|
297
|
+
"Run LVS (Layout vs. Schematic) verification on a cell against a "
|
|
298
|
+
"reference netlist. This compares the physical layout to the "
|
|
299
|
+
"schematic representation to ensure they match. Returns XML results "
|
|
300
|
+
"showing any mismatches between layout and schematic."
|
|
301
|
+
),
|
|
302
|
+
inputSchema=_add_project_param(
|
|
303
|
+
{
|
|
304
|
+
"type": "object",
|
|
305
|
+
"properties": {
|
|
306
|
+
"cell": {
|
|
307
|
+
"type": "string",
|
|
308
|
+
"description": "Name of the cell to verify",
|
|
309
|
+
},
|
|
310
|
+
"netpath": {
|
|
311
|
+
"type": "string",
|
|
312
|
+
"description": (
|
|
313
|
+
"Path to the reference netlist file to compare against"
|
|
314
|
+
),
|
|
315
|
+
},
|
|
316
|
+
"cellargs": {
|
|
317
|
+
"type": "string",
|
|
318
|
+
"description": (
|
|
319
|
+
"Optional cell arguments as a JSON string. "
|
|
320
|
+
"Default is empty string."
|
|
321
|
+
),
|
|
322
|
+
"default": "",
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
"required": ["cell", "netpath"],
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
),
|
|
329
|
+
]
|
|
330
|
+
|
|
331
|
+
# Phase 3: SPICE Workflow Tools (to be implemented)
|
|
332
|
+
SPICE_TOOLS: list[Tool] = []
|
|
333
|
+
|
|
334
|
+
# Phase 4: Simulation & Advanced Tools (to be implemented)
|
|
335
|
+
ADVANCED_TOOLS: list[Tool] = []
|
|
336
|
+
|
|
337
|
+
# All tools (Phase 1 + Project tools)
|
|
338
|
+
TOOLS: list[Tool] = [
|
|
339
|
+
*PROJECT_TOOLS, # Project discovery tools (always available)
|
|
340
|
+
*CORE_TOOLS,
|
|
341
|
+
*VERIFICATION_TOOLS,
|
|
342
|
+
*SPICE_TOOLS,
|
|
343
|
+
*ADVANCED_TOOLS,
|
|
344
|
+
]
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def get_all_tools() -> list[Tool]:
|
|
348
|
+
"""Get all available MCP tools.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
List of all tool definitions
|
|
352
|
+
"""
|
|
353
|
+
return TOOLS
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def get_tool_by_name(name: str) -> Tool | None:
|
|
357
|
+
"""Get a tool by name.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
name: Tool name
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
Tool definition or None if not found
|
|
364
|
+
"""
|
|
365
|
+
for tool in TOOLS:
|
|
366
|
+
if tool.name == name:
|
|
367
|
+
return tool
|
|
368
|
+
return None
|