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.
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: gfp-mcp
3
+ Version: 0.2.4
4
+ Summary: Model Context Protocol (MCP) server for GDSFactory+ photonic IC design
5
+ Author: GDSFactory+ Team
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/doplaydo/gfp-mcp
8
+ Project-URL: Repository, https://github.com/doplaydo/gfp-mcp
9
+ Project-URL: Documentation, https://github.com/doplaydo/gfp-mcp#readme
10
+ Project-URL: Changelog, https://github.com/doplaydo/gfp-mcp/blob/main/CHANGELOG.md
11
+ Project-URL: Issue Tracker, https://github.com/doplaydo/gfp-mcp/issues
12
+ Keywords: mcp,gdsfactory,photonics,ic-design,eda,model-context-protocol,photonic-ic,gds
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3 :: Only
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: mcp>=1.7.1
28
+ Requires-Dist: httpx>=0.25.0
29
+ Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
30
+ Requires-Dist: psutil>=5.9.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
33
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
36
+ Requires-Dist: bump-my-version>=0.26.0; extra == "dev"
37
+ Requires-Dist: build>=1.4.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # GDSFactory+ MCP Server
41
+
42
+ [![PyPI version](https://img.shields.io/pypi/v/gfp-mcp.svg)](https://pypi.org/project/gfp-mcp/)
43
+ [![Python versions](https://img.shields.io/pypi/pyversions/gfp-mcp.svg)](https://pypi.org/project/gfp-mcp/)
44
+ [![Tests](https://github.com/doplaydo/gfp-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/doplaydo/gfp-mcp/actions)
45
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
46
+
47
+ Model Context Protocol (MCP) server for GDSFactory+ that enables AI assistants like Claude to design and build photonic integrated circuits.
48
+
49
+ ## What is this?
50
+
51
+ This MCP server connects AI assistants to [GDSFactory+](https://gdsfactory.com), allowing you to design photonic ICs through natural language. Build components, run verification checks, and manage multiple projects directly from Claude Code or Claude Desktop.
52
+
53
+ ## Quick Start
54
+
55
+ ### 1. Install Prerequisites
56
+
57
+ - Python 3.10 or higher
58
+ - VSCode with the [GDSFactory+ extension](https://marketplace.visualstudio.com/items?itemName=gdsfactory.gdsfactoryplus) installed
59
+
60
+ ### 2. Install the MCP Server
61
+
62
+ **With uv (recommended):**
63
+
64
+ If you don't have `uv` installed, see the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).
65
+
66
+ ```bash
67
+ uv tool install gfp-mcp
68
+ ```
69
+
70
+ <details>
71
+ <summary>Ephemeral approach</summary>
72
+
73
+ Run without installing:
74
+
75
+ ```bash
76
+ uvx --from gfp-mcp gfp-mcp-serve
77
+ ```
78
+ </details>
79
+
80
+ <details>
81
+ <summary><strong>Alternative: pip install</strong></summary>
82
+
83
+ ```bash
84
+ pip install gfp-mcp
85
+ ```
86
+
87
+ </details>
88
+
89
+ ### 3. Connect to Your AI Assistant
90
+
91
+ <details>
92
+ <summary><strong>Claude Code</strong></summary>
93
+
94
+ Add to `.claude/settings.json`:
95
+
96
+ ```json
97
+ {
98
+ "mcpServers": {
99
+ "gdsfactoryplus": {
100
+ "command": "gfp-mcp-serve",
101
+ "args": [],
102
+ "env": {
103
+ "GFP_API_URL": "http://localhost:8787"
104
+ }
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ Or use the CLI:
111
+
112
+ ```bash
113
+ claude mcp add gdsfactoryplus -- gfp-mcp-serve
114
+ ```
115
+
116
+ </details>
117
+
118
+ <details>
119
+ <summary><strong>Claude Desktop</strong></summary>
120
+
121
+ Add to your config file:
122
+
123
+ **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
124
+
125
+ **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
126
+
127
+ **Linux**: `~/.config/Claude/claude_desktop_config.json`
128
+
129
+ ```json
130
+ {
131
+ "mcpServers": {
132
+ "gdsfactoryplus": {
133
+ "command": "gfp-mcp-serve",
134
+ "args": []
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ Restart Claude Desktop after adding the configuration.
141
+
142
+ </details>
143
+
144
+ <details>
145
+ <summary><strong>Cursor</strong></summary>
146
+
147
+ Coming soon. Follow the MCP integration instructions for Cursor when available.
148
+
149
+ </details>
150
+
151
+ ### 4. Start Designing
152
+
153
+ The MCP server automatically discovers running GDSFactory+ servers via the registry (`~/.gdsfactory/server-registry.json`). On startup, it will log all discovered projects.
154
+
155
+ Try these commands with your AI assistant:
156
+
157
+ - "List all available photonic components"
158
+ - "Build an MZI interferometer"
159
+ - "Show me details about the directional coupler"
160
+ - "Build multiple components: mzi, coupler, and bend_euler"
161
+ - "List all my GDSFactory+ projects"
162
+
163
+ ## Available Tools
164
+
165
+ - **build_cells** - Build one or more GDS cells by name (pass a list, can be single-item)
166
+ - **list_cells** - List all available photonic components
167
+ - **get_cell_info** - Get detailed component metadata
168
+ - **list_projects** - List all running GDSFactory+ server instances
169
+ - **get_project_info** - Get detailed information about a specific project
170
+ - **check_drc** - Run Design Rule Check verification (returns structured format with all violations including simplified location data for LLM-friendly troubleshooting)
171
+ - **check_connectivity** - Run connectivity verification
172
+ - **check_lvs** - Run Layout vs. Schematic verification
173
+
174
+ ## Multi-Project Support
175
+
176
+ The MCP server automatically discovers all running GDSFactory+ projects via the server registry (`~/.gdsfactory/server-registry.json`). The registry is the source of truth for available servers. Use the `list_projects` tool to see all running projects, then specify the project name when building components:
177
+
178
+ ```
179
+ User: "List all my GDSFactory+ projects"
180
+ Claude: [Uses list_projects tool to show all running servers]
181
+
182
+ User: "Build the mzi component in my_photonics_project"
183
+ Claude: [Routes request to the correct project]
184
+ ```
185
+
186
+ ## Troubleshooting
187
+
188
+ <details>
189
+ <summary><strong>Server not appearing in Claude</strong></summary>
190
+
191
+ 1. Verify installation: `gfp-mcp-serve --help`
192
+ 2. Check Claude Code logs: `claude --debug`
193
+ 3. Restart Claude Desktop/Code
194
+ 4. Ensure the GDSFactory+ VSCode extension is active and a project is open
195
+
196
+ </details>
197
+
198
+ <details>
199
+ <summary><strong>Connection refused errors</strong></summary>
200
+
201
+ The MCP server uses the registry (`~/.gdsfactory/server-registry.json`) to discover running servers.
202
+
203
+ 1. Use the `list_projects` tool in Claude to check available servers
204
+ 2. If no servers are found, ensure the GDSFactory+ VSCode extension is running with an active project:
205
+ - Open VSCode with the GDSFactory+ extension installed
206
+ - Open a GDSFactory+ project folder
207
+ - The extension automatically starts the server and registers it
208
+ 3. Check the MCP startup logs for discovered servers
209
+ 4. Verify the registry is accessible at `~/.gdsfactory/server-registry.json`
210
+ 5. For backward compatibility, you can set a specific server URL:
211
+
212
+ ```bash
213
+ export GFP_API_URL="http://localhost:YOUR_PORT"
214
+ ```
215
+
216
+ </details>
217
+
218
+ <details>
219
+ <summary><strong>Tool execution timeout</strong></summary>
220
+
221
+ Increase the timeout for long-running operations:
222
+
223
+ ```bash
224
+ export GFP_MCP_TIMEOUT=600 # 10 minutes
225
+ ```
226
+
227
+ </details>
@@ -0,0 +1,14 @@
1
+ gfp_mcp-0.2.4.dist-info/licenses/LICENSE,sha256=ixSuHdKKXzNJw_eTgAxHzaCNIds8k48hytA_eJgA8gQ,225
2
+ mcp_standalone/__init__.py,sha256=1-BT202aWn5Uwt-5bDHyGtBw3ObSKhK9ATzQlIXiJdw,1069
3
+ mcp_standalone/client.py,sha256=LWO1emsiUa4Dg9yXH0FO7-LHV2ngxOov_acwH7JnVFo,9902
4
+ mcp_standalone/config.py,sha256=bxJXYioVhx5FnS_dzvIEyVjaQbC91s6Pkl0tCRms3Ig,1225
5
+ mcp_standalone/mappings.py,sha256=LFjo8Q5olO3LQM4Jy4h6xa4CD3bMWlyG_RtfDJGvQx4,15928
6
+ mcp_standalone/registry.py,sha256=ozDrMPksWoyKAkNd9MH2g8FRoN0XEQKWnLwZsgDxWLc,6127
7
+ mcp_standalone/resources.py,sha256=upY93XVemc5ezx1p_YML57O6HYVahCs2RsYXn4Dw_X0,3517
8
+ mcp_standalone/server.py,sha256=FSjGSjwpe_D2idMrRrAMTq9pnwBToK2vZ1VtzPIMyeg,8788
9
+ mcp_standalone/tools.py,sha256=qQAI6-Xb0sWUTJyYz5a47830cerbjwO5euIRhDmBRUk,16468
10
+ gfp_mcp-0.2.4.dist-info/METADATA,sha256=v99aAe-JU0aQ7pmzICvHhoGLaKv4GoG4F9fNkxJHbyw,7369
11
+ gfp_mcp-0.2.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ gfp_mcp-0.2.4.dist-info/entry_points.txt,sha256=mgyus9dsB_8mjgnztuHNPqzPi-7HcPg1iYzfM5NMIjk,61
13
+ gfp_mcp-0.2.4.dist-info/top_level.txt,sha256=g2hRJHoDDPNtrNdXR70T7FR9Ev6DTRJiGW7ZvlvnXMc,15
14
+ gfp_mcp-0.2.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,4 @@
1
+ PROPRIETARY AND CONFIDENTIAL
2
+ THIS SOFTWARE IS THE SOLE PROPERTY AND COPYRIGHT (c) 2025 OF DOPLAYDO INC.
3
+ USE OR REPRODUCTION IN PART OR AS A WHOLE WITHOUT THE WRITTEN AGREEMENT OF DOPLAYDO IS PROHIBITED.
4
+ NOTICE VERSION: 0.0.2
@@ -23,6 +23,7 @@ from __future__ import annotations
23
23
 
24
24
  from .client import FastAPIClient
25
25
  from .config import MCPConfig
26
+ from .resources import get_all_resources, get_resource_content
26
27
  from .server import create_server, main, run_server
27
28
  from .tools import get_all_tools, get_tool_by_name
28
29
 
@@ -34,6 +35,8 @@ __all__ = [
34
35
  "run_server",
35
36
  "get_all_tools",
36
37
  "get_tool_by_name",
38
+ "get_all_resources",
39
+ "get_resource_content",
37
40
  ]
38
41
 
39
- __version__ = "0.1.0"
42
+ __version__ = "0.2.4"
mcp_standalone/client.py CHANGED
@@ -43,6 +43,21 @@ class FastAPIClient:
43
43
  self._client: httpx.AsyncClient | None = None
44
44
  self._registry = ServerRegistry()
45
45
 
46
+ def _has_available_servers(self) -> bool:
47
+ """Check if any servers are available in the registry."""
48
+ return len(self._registry.list_servers()) > 0
49
+
50
+ def _get_default_server_url(self) -> str | None:
51
+ """Get the first available server URL from registry if no base_url configured."""
52
+ if self.base_url:
53
+ return self.base_url
54
+
55
+ servers = self._registry.list_servers()
56
+ if servers:
57
+ return f"http://localhost:{servers[0].port}"
58
+
59
+ return None
60
+
46
61
  async def __aenter__(self) -> Self:
47
62
  """Enter async context."""
48
63
  await self.start()
@@ -55,12 +70,17 @@ class FastAPIClient:
55
70
  async def start(self) -> None:
56
71
  """Start the HTTP client with connection pooling."""
57
72
  if self._client is None:
73
+ base_url = self.base_url or "http://localhost"
74
+
58
75
  self._client = httpx.AsyncClient(
59
- base_url=self.base_url,
76
+ base_url=base_url,
60
77
  timeout=httpx.Timeout(self.timeout),
61
78
  limits=httpx.Limits(max_keepalive_connections=5, max_connections=10),
62
79
  )
63
- logger.debug("HTTP client started with base URL: %s", self.base_url)
80
+ logger.debug(
81
+ "HTTP client started with base URL: %s (resolved per-request if needed)",
82
+ self.base_url or "from registry",
83
+ )
64
84
 
65
85
  async def close(self) -> None:
66
86
  """Close the HTTP client."""
@@ -79,22 +99,46 @@ class FastAPIClient:
79
99
  Base URL for the request
80
100
 
81
101
  Raises:
82
- ValueError: If project not found in registry
102
+ ValueError: If base URL cannot be resolved
83
103
  """
84
- # If no project specified, use default base_url
85
- if project is None:
104
+ if project is not None:
105
+ server_info = self._registry.get_server_by_project(project)
106
+ if server_info is None:
107
+ available = self._registry.list_servers()
108
+ if available:
109
+ project_list = ", ".join([s.project_name for s in available[:3]])
110
+ if len(available) > 3:
111
+ project_list += f", ... and {len(available) - 3} more"
112
+ msg = (
113
+ f"Project '{project}' not found in registry. "
114
+ f"Available projects: {project_list}. "
115
+ "Use list_projects tool to see all running servers."
116
+ )
117
+ else:
118
+ msg = (
119
+ f"Project '{project}' not found. No GDSFactory+ servers are running. "
120
+ "Please open a GDSFactory+ project in VSCode with the extension installed."
121
+ )
122
+ raise ValueError(msg)
123
+
124
+ return f"http://localhost:{server_info.port}"
125
+
126
+ if self.base_url:
86
127
  return self.base_url
87
128
 
88
- # Look up project in registry
89
- server_info = self._registry.get_server_by_project(project)
90
- if server_info is None:
91
- msg = (
92
- f"Project '{project}' not found in registry. "
93
- "Make sure the server is running for this project."
129
+ default_url = self._get_default_server_url()
130
+ if default_url:
131
+ logger.info(
132
+ "No project specified, using first available server: %s", default_url
94
133
  )
95
- raise ValueError(msg)
134
+ return default_url
96
135
 
97
- return f"http://localhost:{server_info.port}"
136
+ msg = (
137
+ "No project specified and no GDSFactory+ servers are running. "
138
+ "Either: (1) Start a server by opening a GDSFactory+ project in VSCode, "
139
+ "(2) Specify a project parameter, or (3) Set GFP_API_URL environment variable."
140
+ )
141
+ raise ValueError(msg)
98
142
 
99
143
  async def request(
100
144
  self,
@@ -125,7 +169,6 @@ class FastAPIClient:
125
169
  if self._client is None:
126
170
  await self.start()
127
171
 
128
- # Resolve the base URL for this request
129
172
  base_url = self._resolve_base_url(project)
130
173
 
131
174
  last_error = None
@@ -143,7 +186,6 @@ class FastAPIClient:
143
186
  base_url,
144
187
  )
145
188
 
146
- # Build full URL with the resolved base URL
147
189
  full_url = f"{base_url}{path}"
148
190
 
149
191
  response = await self._client.request( # type: ignore[union-attr]
@@ -155,7 +197,6 @@ class FastAPIClient:
155
197
  )
156
198
  response.raise_for_status()
157
199
 
158
- # Try to parse JSON, fall back to text
159
200
  try:
160
201
  return response.json()
161
202
  except (ValueError, TypeError):
@@ -165,19 +206,16 @@ class FastAPIClient:
165
206
  last_error = e
166
207
  logger.warning("Request failed (attempt %d): %s", attempt + 1, e)
167
208
 
168
- # Don't retry on client errors (4xx)
169
209
  if (
170
210
  isinstance(e, httpx.HTTPStatusError)
171
211
  and 400 <= e.response.status_code < 500
172
212
  ):
173
213
  raise
174
214
 
175
- # Exponential backoff for retries
176
215
  if attempt < MCPConfig.MAX_RETRIES - 1:
177
216
  await asyncio.sleep(backoff)
178
217
  backoff *= 2
179
218
 
180
- # All retries failed
181
219
  logger.error("All %d attempts failed", MCPConfig.MAX_RETRIES)
182
220
  raise last_error # type: ignore[misc]
183
221
 
@@ -218,6 +256,10 @@ class FastAPIClient:
218
256
  async def health_check(self, project: str | None = None) -> bool:
219
257
  """Check if the FastAPI server is reachable.
220
258
 
259
+ .. deprecated::
260
+ This method is deprecated and no longer called during MCP startup.
261
+ Use list_projects() for server discovery instead.
262
+
221
263
  Args:
222
264
  project: Optional project name to check specific server
223
265
 
mcp_standalone/config.py CHANGED
@@ -15,32 +15,28 @@ class MCPConfig:
15
15
  that proxies requests to the FastAPI backend.
16
16
  """
17
17
 
18
- # FastAPI base URL (default: http://localhost:8787)
19
- API_URL: Final[str] = os.getenv("GFP_API_URL", "http://localhost:8787")
18
+ API_URL: Final[str | None] = os.getenv("GFP_API_URL")
20
19
 
21
- # Timeout for tool calls in seconds (default: 300 = 5 minutes)
22
20
  TIMEOUT: Final[int] = int(os.getenv("GFP_MCP_TIMEOUT", "300"))
23
21
 
24
- # Enable debug logging
25
22
  DEBUG: Final[bool] = os.getenv("GFP_MCP_DEBUG", "false").lower() in (
26
23
  "true",
27
24
  "1",
28
25
  "yes",
29
26
  )
30
27
 
31
- # Retry configuration
32
28
  MAX_RETRIES: Final[int] = 3
33
29
  RETRY_BACKOFF: Final[float] = 0.5 # Initial backoff in seconds
34
30
 
35
31
  @classmethod
36
- def get_api_url(cls, override: str | None = None) -> str:
32
+ def get_api_url(cls, override: str | None = None) -> str | None:
37
33
  """Get the FastAPI base URL.
38
34
 
39
35
  Args:
40
36
  override: Optional URL to override the environment variable
41
37
 
42
38
  Returns:
43
- The API base URL
39
+ The API base URL or None if not configured
44
40
  """
45
41
  return override or cls.API_URL
46
42