Skip to content

Conversation

@jeevanpillay
Copy link
Contributor

@jeevanpillay jeevanpillay commented May 28, 2025

Summary by CodeRabbit

  • New Features

    • Introduced comprehensive Figma MCP Server support, enabling real-time design automation and JavaScript code execution in Figma via a WebSocket-based architecture.
    • Added a Figma plugin with a user interface for connecting to the MCP server, executing design commands, and retrieving document state.
    • Implemented a WebSocket Mock MCP server for testing and development, supporting multiple concurrent clients and message broadcasting.
    • Provided new MCP tools for interacting with Figma and WebSocket servers, including state retrieval, code execution, and server status checks.
  • Documentation

    • Added detailed user and developer guides for Figma integration, plugin usage, and server architecture.
    • Updated roadmap and meta documentation to reflect the new Figma MCP Server and WebSocket integration.
  • Bug Fixes

    • Improved error handling and logging throughout server startup, shutdown, and communication processes.
  • Tests

    • Added extensive unit, integration, and end-to-end tests for both Figma and WebSocket Mock MCP servers, ensuring robust client-server interactions and tool functionality.
  • Chores

    • Updated configuration files and added sample configs for new servers.
    • Introduced utility scripts and test clients for rapid development and testing workflows.

- Introduced a new Figma MCP server configuration in `servers.yaml` with API token handling and timeout settings.
- Updated `pyproject.toml` to include `aiohttp` as a dependency for Figma API integration.
- Enhanced the configuration loader to inject environment variables for Figma API token.
- Updated server orchestrator to recognize and manage the new Figma server type.
…time design manipulation

- Developed a comprehensive Figma MCP server enabling real-time design manipulation through WebSocket communication with a Figma plugin.
- Enhanced server capabilities with tools for document management, element creation, and modification.
- Updated project configuration to include WebSocket dependencies and streamlined server setup.
- Introduced a Figma plugin with a user interface for creating and managing design elements directly from Figma.
- Improved documentation to reflect new features and usage instructions for the plugin and server integration.
…ents

- Integrated WebSocket communication for real-time interaction between the Figma plugin and the server.
- Updated the plugin UI to provide a more comprehensive user experience, including selection monitoring and improved error handling.
- Modified server configuration to support WebSocket connections and adjusted timeout settings.
- Enhanced the manifest to allow network access for local development, facilitating communication with the server.
- Refactored code for better organization and maintainability, including utility functions for error handling and message processing.
- Changed WebSocket server URL from ws://localhost:9002 to ws://localhost:9003 in the plugin UI and configuration files.
- Updated the Figma MCP server to use port 8003 for consistency with the new WebSocket settings.
- Enhanced the manifest reasoning to clarify the purpose of the WebSocket communication.
- Added functionality to request and handle plugin status via WebSocket communication.
- Updated the Figma MCP server to provide essential server status information for the plugin.
- Enhanced the UI to log plugin status responses, improving user feedback and error handling.
- Modified server configuration to include websocket port and plugin channel settings for better integration.
- Removed unnecessary dependencies from the server configuration.
- Enhanced logging during server lifecycle events, including detailed error tracebacks for better debugging.
- Implemented manual startup logic to ensure server initialization is robust across different transport types.
- Added logging for environment configuration and server parameters to aid in troubleshooting.
- Reduced complexity in the Figma MCP plugin by removing advanced features and focusing on basic communication with the server.
- Updated the plugin UI to streamline user interaction and enhance clarity.
- Refactored server code to support a simplified architecture, removing unnecessary dependencies and tools.
- Improved documentation to reflect the changes and provide clear instructions for the simplified plugin usage.
…t integration

- Updated the Figma MCP plugin to support detailed document information retrieval and design command execution via WebSocket communication.
- Improved the plugin UI to facilitate real-time interaction, including status indicators and enhanced user feedback.
- Refactored server code to handle WebSocket connections, enabling robust communication with the Figma plugin for design operations.
- Expanded documentation to reflect new features, including installation instructions and usage examples for the updated plugin and server functionalities.
- Transitioned the Figma MCP plugin to operate as a socket server, enhancing real-time communication capabilities with the MCP server.
- Implemented manual connection controls in the plugin UI, allowing users to configure host and port settings for improved flexibility.
- Refactored server code to utilize socket communication instead of WebSocket, streamlining the connection process and improving error handling.
- Updated documentation to reflect new features, including connection management and configuration instructions for the plugin and server.
… command handling

- Deleted the bridge server implementation from the Figma MCP plugin, simplifying the architecture.
- Introduced a new command handling mechanism to process MCP commands directly within the plugin.
- Updated the plugin UI to reflect changes in connection management and command execution.
- Enhanced documentation to clarify the new command handling process and plugin functionality.
- Removed the previous bridge server implementation, simplifying the plugin's architecture.
- Implemented a WebSocket server directly within the plugin for real-time communication with the MCP server.
- Updated the plugin UI to manage WebSocket server controls, including start/stop functionality and connection testing.
- Enhanced documentation to reflect the new WebSocket server architecture and usage instructions.
- Refactored server code to utilize WebSocket connections, improving command handling and error management.
- Deleted the Figma MCP server implementation and associated files, simplifying the project structure.
- Removed references to Figma in configuration files and dependencies, including the `pyproject.toml` and `uv.lock`.
- Updated orchestration tools to eliminate Figma-related mappings and server types.
- Cleaned up server configuration files to reflect the removal of the Figma server, enhancing maintainability.
- Introduced a new WebSocket mock server entry point in the `pyproject.toml` for enhanced testing capabilities.
- Updated server access configurations to include the WebSocket mock server, improving modularity.
- Enhanced orchestration tools to support the new WebSocket mock server, facilitating multi-server management.
- Refactored the Figma MCP plugin to operate as a WebSocket client, enabling real-time communication with the MCP server.
- Updated the plugin UI to manage connection controls, including connect/disconnect functionality and server URL configuration.
- Enhanced the WebSocket client implementation to handle connection status, message simulation, and command execution.
- Added new server configuration for the Figma MCP server in `servers.yaml`, supporting design automation workflows.
- Improved documentation to reflect the new client architecture and usage instructions for connecting to the MCP server.
…ementation

- Removed the WebSocket client architecture from the plugin, streamlining the codebase.
- Updated the plugin UI to eliminate connection management features, focusing on core functionality.
- Refactored design command execution to operate independently of WebSocket communication.
- Enhanced documentation to reflect the removal of WebSocket features and provide clearer usage instructions.
…uration updates

- Added a new WebSocket mock server configuration in `servers.yaml` for testing WebSocket communications.
- Updated the WebSocket mock server implementation to always start automatically upon MCP server startup, removing the need for manual start/stop tools.
- Enhanced the server's retry logic for starting on available ports, improving reliability.
- Removed obsolete auto-start configuration options from the server and related tools, streamlining the codebase.
- Updated documentation to reflect the new automatic startup behavior and configuration options.
- Removed the auto-start WebSocket configuration from `servers.yaml`, as the WebSocket server now starts automatically with the MCP server.
- Simplified the server startup logic by eliminating manual startup handling and integrating background startup for the WebSocket server.
- Updated the Figma MCP server tools to remove obsolete start/stop functions, enhancing code clarity.
- Enhanced documentation to reflect the new automatic startup behavior and configuration changes.
- Added logging for received messages in the plugin to improve debugging.
- Implemented new message types in the UI for better communication feedback, including acknowledgment of plugin registration and WebSocket connection status.
- Updated the UI to reflect connection test results and disconnection warnings, enhancing user awareness of plugin status.
- Added signal handlers for graceful WebSocket server shutdown on termination signals.
- Enhanced the server's shutdown logic to keep the WebSocket server running during normal client disconnections.
- Improved cleanup procedures for the WebSocket event loop and pending tasks during shutdown.
- Introduced tracking for the WebSocket thread and loop for better resource management.
- Improved logging for WebSocket connection and disconnection events, providing clearer feedback in the UI.
- Added acknowledgment messages for connection and disconnection events to improve communication reliability.
- Introduced new message handlers for 'pong' and document-related responses, enhancing server-client interaction.
- Enhanced error logging for better debugging of message handling issues.
- Changed the broadcast status message from "broadcast_sent" to "command_broadcast" for clarity in the Figma server's messaging.
- Added a new fixture for sample Figma server configuration to streamline testing.
- Enhanced WebSocket server status reporting by including total clients connected.
- Updated end-to-end tests to reflect automatic server startup and improved error handling scenarios, ensuring accurate testing of server lifecycle and client interactions.
- Deleted the Figma WebSocket test client script to streamline the codebase.
- Updated the Figma MCP server tools to include only essential functionalities, replacing obsolete methods with `get_state` and `execute_command`.
- Enhanced documentation to reflect the changes in available tools and server capabilities.
- Refactored the Figma plugin to support executing arbitrary JavaScript code, enhancing its capabilities for design automation.
- Updated messaging between the plugin and server to handle code execution requests and responses.
- Replaced design command execution with a more flexible code execution model, allowing users to run custom scripts.
- Enhanced error handling and logging for code execution results, improving debugging and user feedback.
- Updated the plugin UI to reflect changes in functionality, including a new button for testing code execution.
- Streamlined the execution of JavaScript code by directly evaluating it in the main context, improving compatibility with Figma API calls.
- Enhanced output capturing and error handling, providing clearer feedback on execution results.
- Removed the sandbox context in favor of a more straightforward execution model, simplifying the code structure and improving performance.
@vercel
Copy link

vercel bot commented May 28, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mcp ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 29, 2025 3:19am

@coderabbitai
Copy link

coderabbitai bot commented May 28, 2025

Walkthrough

This update introduces a complete Figma integration for the Lightfast MCP platform, including a new Figma MCP server, a Figma plugin with a WebSocket client, comprehensive documentation, configuration, and extensive tests. The server enables real-time design automation by executing JavaScript and design commands in Figma via WebSocket. Supporting infrastructure, error handling, and mock servers for testing are included.

Changes

File(s) / Path(s) Change Summary
addons/figma/.gitignore, README.md, code.ts, manifest.json, package.json, tsconfig.json, ui.html, websocket-server.js Added Figma plugin source, config, UI, WebSocket client, and local mock server for development and testing.
config/servers.yaml Added Figma and WebSocket mock server configurations.
pyproject.toml Added dependencies, entry points, and tasks for Figma and WebSocket mock servers.
quick_test.py Added script to test WebSocket server connectivity.
src/lightfast_mcp/core/base_server.py Enhanced server startup logging and traceback for errors.
src/lightfast_mcp/exceptions.py Added Figma-specific MCP exception classes.
src/lightfast_mcp/servers/init.py Exposed Figma and WebSocket mock servers in package API.
src/lightfast_mcp/servers/figma/README.md, init.py, server.py, tools.py, websocket_server.py Implemented Figma MCP server, WebSocket server, tools, and documentation.
src/lightfast_mcp/servers/figma_mcp_server.py Added Figma MCP server entry point with CLI and config parsing.
src/lightfast_mcp/servers/websocket_mock/README.md, init.py, server.py, tools.py, websocket_server.py Implemented WebSocket Mock MCP server, tools, server logic, and documentation.
src/lightfast_mcp/servers/websocket_mock_server.py Added entry point for WebSocket Mock MCP server with config loading.
src/tools/orchestration/config_loader.py Enhanced config loader to inject environment variables for certain server types.
src/tools/orchestration/server_orchestrator.py Allowed subprocess startup for Figma server; improved safe server stopping.
tests/conftest.py Added pytest fixture for sample Figma config.
tests/e2e/test_websocket_mock_e2e.py Added end-to-end tests for WebSocket Mock MCP server.
tests/integration/test_websocket_mock_integration.py Added integration tests for WebSocket Mock MCP server and client interactions.
tests/unit/test_figma_server.py Added unit and integration tests for Figma MCP server and components.
tests/unit/test_modular_servers.py Integrated Figma MCP server into modular server unit tests.
tests/unit/test_websocket_mock_server.py Added unit tests for WebSocket Mock MCP server and tools.
tests/utils/websocket_test_client.py Added interactive and automated WebSocket test client utility.
docs/src/content/docs/figma.mdx, meta.json, roadmap.mdx Added Figma integration documentation, updated roadmap and docs index.

Sequence Diagram(s)

sequenceDiagram
    participant Claude as AI Client
    participant MCP as Figma MCP Server (HTTP)
    participant WS as Figma WebSocket Server
    participant Plugin as Figma Plugin (WebSocket Client)
    participant Figma as Figma Editor

    Claude->>MCP: HTTP request (get_state/execute_code)
    MCP->>WS: Send command/request to plugin
    WS->>Plugin: WebSocket message (command)
    Plugin->>Figma: Execute JS/design command via Figma API
    Plugin->>WS: WebSocket response (result/document info)
    WS->>MCP: Response/result
    MCP->>Claude: HTTP response (result/state)
Loading

Poem

🐇
A hop, a leap, a plugin new,
Figma and Claude now rendezvous!
WebSockets hum, commands take flight,
Designs appear in pixel light.
With tests and docs, the groundwork laid—
A rabbit’s dream of tools well made!

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🔭 Outside diff range comments (1)
pyproject.toml (1)

44-56: ⚠️ Potential issue

Broken entry-points – referenced modules do not exist

The new script targets use the _mcp_server suffix, but the actual Python packages live under ...servers.websocket_mock.server and ...servers.figma.server.

Trying to run uv run lightfast-websocket-mock-server or uv run lightfast-figma-server will therefore raise ModuleNotFoundError.

Proposed diff:

-lightfast-websocket-mock-server = "lightfast_mcp.servers.websocket_mock_server:main"
-lightfast-figma-server = "lightfast_mcp.servers.figma_mcp_server:main"
+lightfast-websocket-mock-server = "lightfast_mcp.servers.websocket_mock.server:main"
+lightfast-figma-server = "lightfast_mcp.servers.figma.server:main"

(You already added the correct module paths further down for the “direct modular server” aliases; mirror those here.)

♻️ Duplicate comments (1)
src/lightfast_mcp/servers/figma/websocket_server.py (1)

442-449: Same unused request_id issue in _handle_code_execution_response

Duplicate of the previous linter finding – either use it (e.g., in the log line) or drop it.

-        request_id = data.get("request_id")
+        # request_id is available for correlation but currently unused
+#       request_id = data.get("request_id")
🧰 Tools
🪛 Ruff (0.11.9)

446-446: Local variable request_id is assigned to but never used

Remove assignment to unused variable request_id

(F841)

🪛 GitHub Actions: CI/CD Pipeline

[error] 446-446: ESLint: Local variable request_id is assigned to but never used. (F841)

🧹 Nitpick comments (29)
tests/conftest.py (1)

56-65: Enhance test fixtures
The sample_figma_config fixture aligns with existing patterns and will simplify Figma server tests. Consider also adding a sample_websocket_mock_config fixture (with type="websocket_mock", websocket_host, and websocket_port) to standardize setup for the new WebSocket Mock server tests.

quick_test.py (1)

12-13: Consider making the WebSocket URL configurable.

While the hardcoded URL is acceptable for a quick test script, consider making it configurable via command-line arguments or environment variables for better flexibility.

+import sys
+
+async def test(url: str = "ws://localhost:9003"):
    try:
        print("Attempting to connect...")
        websocket = await asyncio.wait_for(
-            websockets.connect("ws://localhost:9003"), timeout=5.0
+            websockets.connect(url), timeout=5.0
        )

And update the main block:

if __name__ == "__main__":
-    asyncio.run(test())
+    url = sys.argv[1] if len(sys.argv) > 1 else "ws://localhost:9003"
+    asyncio.run(test(url))
src/tools/orchestration/config_loader.py (1)

174-202: Consider improving modularity and addressing potential shallow copy limitation.

The implementation is functional but has some areas for improvement:

  1. Modularity: The hard-coded env_mappings dictionary (acknowledged by the TODO comment) limits extensibility for new server types.

  2. Shallow copy limitation: Using config.copy() creates a shallow copy, which could be problematic if the config contains nested dictionaries that also need environment variable injection.

Consider these improvements:

-    def _inject_environment_variables(self, config: dict[str, Any]) -> dict[str, Any]:
-        """Inject environment variables into configuration values."""
-        # Create a copy to avoid modifying the original
-        config = config.copy()
+    def _inject_environment_variables(self, config: dict[str, Any]) -> dict[str, Any]:
+        """Inject environment variables into configuration values."""
+        import copy
+        # Create a deep copy to avoid modifying the original and handle nested dicts
+        config = copy.deepcopy(config)

-        # Define environment variable mappings for different server types
-        # @todo more modular...
-        env_mappings = {
-            "github": {
-                "api_token": "GITHUB_TOKEN", 
-                "personal_access_token": "GITHUB_PERSONAL_ACCESS_TOKEN",
-            },
-            "openai": {"api_key": "OPENAI_API_KEY"},
-            "anthropic": {"api_key": "ANTHROPIC_API_KEY"},
-        }
+        # Load environment mappings from a more modular source
+        env_mappings = self._get_environment_mappings()

Also consider adding a separate method _get_environment_mappings() that could load mappings from a configuration file or registry pattern.

addons/figma/package.json (1)

1-59: LGTM! Well-structured package.json for Figma plugin development.

The configuration includes appropriate dependencies, comprehensive ESLint setup with TypeScript and Figma plugin support, and essential development scripts.

Consider pinning the ws dependency to a specific version instead of using a range:

-    "ws": "^8.18.2"
+    "ws": "8.18.2"

This ensures consistent behavior across different environments and aligns with the pinned package manager approach.

src/lightfast_mcp/servers/figma_mcp_server.py (1)

25-27: Consider using importlib for module resolution instead of sys.path manipulation.

While the current approach works, directly manipulating sys.path can be fragile. Consider using importlib or ensuring the package is properly installed instead.

Alternative approach:

-# Add the project root to the Python path
-project_root = Path(__file__).parent.parent.parent.parent
-sys.path.insert(0, str(project_root))
+# Ensure proper package imports - consider using importlib or proper installation
src/lightfast_mcp/servers/figma/README.md (3)

9-18: Add language specification to code block.

The architecture diagram code block should specify a language for better rendering and consistency.

-```
+```text
 ┌─────────────────┐    WebSocket     ┌─────────────────┐    MCP HTTP      ┌─────────────────┐
 │   Figma Plugin  │ ←──────────────→ │   MCP Server    │ ←──────────────→ │   AI Client     │
 │   (WebSocket    │   localhost:9003 │   (WebSocket    │   localhost:8003 │   (HTTP Client) │
 │    Client)      │                  │    Server)      │                  │                 │
 │ - Figma API     │                  │ - WebSocket Srv │                  │ - Tool Registry │
 │ - Document Ops  │                  │ - MCP Tools     │                  │ - AI Integration│
 │ - Design Cmds   │                  │ - State Mgmt    │                  │ - Conversation  │
 └─────────────────┘                  └─────────────────┘                  └─────────────────┘
-```
+```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

9-9: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

---

`292-299`: **Add language specification to directory structure.**

The directory structure code block should specify a language for better rendering.



```diff
-```
+```text
 figma/
 ├── __init__.py          # Module exports
 ├── server.py            # Main MCP server implementation
 ├── websocket_server.py  # WebSocket server implementation
 ├── tools.py             # MCP tools implementation
 └── README.md           # This file
-```
+```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

292-292: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

</details>

</details>

---

`200-270`: **Consider using proper headings instead of emphasis.**

The WebSocket protocol message type sections use emphasis (**bold**) instead of proper markdown headings, which affects document structure and accessibility.



```diff
-**Plugin Info**
+#### Plugin Info

-**Document Update**
+#### Document Update

-**Design Command Response**
+#### Design Command Response

-**Welcome**
+#### Welcome

-**Execute Design Command**
+#### Execute Design Command

-**Ping**
+#### Ping
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

202-202: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


214-214: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


227-227: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


241-241: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


254-254: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


264-264: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

src/lightfast_mcp/servers/websocket_mock/server.py (1)

64-103: Deduplicate retry logic & make port probing clearer

The current loop repeats identical retry_count bookkeeping in both the if success … else … branch and the except branch, leading to duplicated code paths and harder maintenance.

A cleaner pattern:

max_retries = 3
while self.websocket_server.port < start_port + max_retries:
    try:
        if await self.websocket_server.start():
            logger.info("✅ WebSocket server started")
            break
    except Exception as e:
        logger.warning(f"⚠️ Attempt failed on port {self.websocket_server.port}: {e}")
    self.websocket_server.port += 1
else:
    raise RuntimeError("Could not start WebSocket server")

This eliminates the double counter and simultaneously documents the probing strategy (increment port until max_retries exhausted).

addons/figma/websocket-server.js (1)

53-101: Add explicit UTF-8 validation / length checks for incoming JSON

JSON.parse(data.toString()) assumes small, valid, UTF-8 payloads.
For robustness (and to reduce DoS vectors) consider:

  1. Rejecting messages above a reasonable size (e.g. 64 KB).
  2. Catching malformed UTF-8 before JSON.parse.
  3. Using a streamed JSON parser if very large payloads are expected.
src/lightfast_mcp/servers/websocket_mock/README.md (2)

360-369: Specify a language for the fenced directory-tree block

markdownlint flagged the “Architecture” code block because the opening triple-backtick has no language specifier (MD040).
Although this is only documentation, adding a language such as text (or bash if you prefer) prevents some renderers from falling back to generic formatting and removes the lint warning.

-```
+```text
 websocket_mock/
 ├── __init__.py          # Module exports
 ...
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

362-362: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


415-436: Minor wording & punctuation improvements

There are a handful of small grammar issues (“repeated verbs”, missing articles / commas) that distract from an otherwise excellent README.
Consider running the file through a linter like codespell or markdownlint with rule MD036 disabled, or quickly editing the following:

  • L.423 “WebSocket Connection Drops – Check network connectivity” (remove second verb repetition)
  • L.431 “WebSocket Connection Drops – Verify firewall settings, monitor server logs” (comma before and)

No functional impact, purely polish.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

415-415: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


424-424: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


432-432: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

tests/integration/test_websocket_mock_integration.py (1)

39-43: Double-stop of the WebSocket server can hide errors

websocket_server is stopped in the fixture’s finally, but individual tests (e.g. test_mcp_server_websocket_integration) also call await websocket_server.stop() inside their own finally.
Most implementations tolerate multiple stop() calls, yet if the method ever raises when the server is already closed the whole test session will fail unexpectedly.

Either:

  1. Remove the extra stop() in the test, or
  2. Guard it with if websocket_server.is_running:.
addons/figma/code.ts (1)

125-136: Duplicate helper performs same work – consolidate

sendDocumentInfoToUI and sendDocumentInfoForServer are identical except for the optional request_id.
Simplify maintenance by having one delegate to the other:

-async function sendDocumentInfoToUI() {
-  ...
-}
-
-async function sendDocumentInfoForServer(requestId?: string) {
+async function sendDocumentInfoForServer(requestId?: string) {
   ...
 }
+
+// Back-compat wrapper
+const sendDocumentInfoToUI = () => sendDocumentInfoForServer(undefined);
src/lightfast_mcp/servers/figma/server.py (1)

178-205: Retry loop ignores asyncio-cancelled errors

If await self.websocket_server.start() is cancelled (e.g. during rapid shutdown), the except block treats it as an ordinary exception and retries, masking the cancellation.
Consider explicitly re-raising asyncio.CancelledError to allow graceful exit.

except asyncio.CancelledError:
    raise
except Exception as e:
    ...
tests/unit/test_websocket_mock_server.py (1)

60-71: Minor simplification: replace the custom async wrapper with AsyncMock

Since AsyncMock is await-able, you can mock websockets.serve directly:

-with patch(
-    "lightfast_mcp.servers.websocket_mock.websocket_server.websockets.serve"
-) as mock_serve:
-    mock_server = AsyncMock()
-
-    async def mock_serve_func(*args, **kwargs):
-        return mock_server
-
-    mock_serve.side_effect = mock_serve_func
+with patch(
+    "lightfast_mcp.servers.websocket_mock.websocket_server.websockets.serve",
+    new_callable=AsyncMock,
+) as mock_serve:
+    mock_server = AsyncMock()
+    mock_serve.return_value = mock_server

This shortens the setup and avoids an unnecessary nested coroutine.

tests/unit/test_figma_server.py (2)

208-216: Resolve Ruff F841 – _server is never used

The local server variable is only created to trigger the background-thread logic and is not referenced later, so Ruff flags it. Prefix it with _ (or remove it) to silence the warning without altering behaviour:

-                server = FigmaMCPServer(sample_figma_config)
+                _server = FigmaMCPServer(sample_figma_config)
🧰 Tools
🪛 Ruff (0.11.9)

210-210: Local variable server is assigned to but never used

Remove assignment to unused variable server

(F841)

🪛 GitHub Actions: CI/CD Pipeline

[error] 210-210: ESLint: Local variable server is assigned to but never used. (F841)


221-224: Same unused variable issue in signal-handler test

Apply the same rename here:

-                server = FigmaMCPServer(sample_figma_config)
+                _server = FigmaMCPServer(sample_figma_config)

This will clear the second F841 lint error reported by CI.

🧰 Tools
🪛 Ruff (0.11.9)

221-221: Local variable server is assigned to but never used

Remove assignment to unused variable server

(F841)

🪛 GitHub Actions: CI/CD Pipeline

[error] 221-221: ESLint: Local variable server is assigned to but never used. (F841)

addons/figma/ui.html (2)

192-218: Add a style for the “disconnecting” state

disconnectFromServer() calls updateConnectionStatus('disconnecting', …) but no corresponding CSS class exists, so the banner loses its background/colour when disconnecting:

@@
 .connection-status.warning {
     background: #fff3cd;
     color: #856404;
     border: 1px solid #ffeaa7;
 }
+
+.connection-status.disconnecting {
+    background: #fff3cd;
+    color: #856404;
+    border: 1px solid #ffeaa7;
+}

290-295: Prevent unbounded log growth

log() keeps appending to textContent, which can balloon memory usage during long sessions. Consider capping the number of stored lines:

 function log(message) {
     const output = document.getElementById('output');
     const timestamp = new Date().toLocaleTimeString();
-    output.textContent += `\n[${timestamp}] ${message}`;
+    const lines = (output.textContent + `\n[${timestamp}] ${message}`).split('\n');
+    const MAX_LINES = 1000;
+    output.textContent = lines.slice(-MAX_LINES).join('\n');
     output.scrollTop = output.scrollHeight;
 }

A rolling buffer keeps the UI responsive without losing recent context.

addons/figma/README.md (2)

88-88: Use a clearer preposition in the test step

“Start the MCP server on ws://localhost:9003” reads a little awkwardly.
Using “at” (or “on port”) is more idiomatic for network endpoints.

-1. Start the MCP server on `ws://localhost:9003`
+1. Start the MCP server at `ws://localhost:9003`
🧰 Tools
🪛 LanguageTool

[uncategorized] ~88-~88: The preposition ‘to’ seems more likely in this position.
Context: ...`` ### Testing 1. Start the MCP server on ws://localhost:9003 2. Load the plugi...

(AI_HYDRA_LEO_REPLACE_ON_TO)


124-124: Add missing determiner for smoother reading

An article is expected before “plugin UI”.

-6. Verify WebSocket server settings in plugin UI 
+6. Verify the WebSocket server settings in the plugin UI 
🧰 Tools
🪛 LanguageTool

[uncategorized] ~124-~124: You might be missing the article “the” here.
Context: ... 6. Verify WebSocket server settings in plugin UI

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

src/lightfast_mcp/servers/figma/tools.py (1)

32-44: ctx parameter is never used – rename to _ctx or leverage it

Both tool functions accept ctx but ignore it, which may confuse static analysis and future readers.

-async def get_state(ctx: Context) -> str:
+async def get_state(_ctx: Context) -> str:

Apply the same change to execute_code.
Alternatively, make use of ctx (e.g., for logging request origin).

src/lightfast_mcp/servers/websocket_mock/websocket_server.py (2)

67-84: Duplicate _is_websocket_closed logic – consider centralising in shared util

The exact same helper already lives in servers/websocket_mock/tools.py. Maintaining two copies invites drift and inconsistencies when you eventually tweak the logic for new websockets releases.

-    def _is_websocket_closed(self, websocket) -> bool:
-        """Check if a WebSocket connection is closed (compatible with different websockets versions)."""
-        ...
+    # TODO: import from lightfast_mcp.servers.websocket_mock.tools

A tiny helper is better kept once and imported where needed.


99-128: Make server tuning parameters configurable

Hard-coding ping_interval, ping_timeout, and close_timeout makes it tough to adapt to slow networks or aggressive keep-alive requirements in production.

Expose them via __init__(..., *, ping_interval=30, ping_timeout=10, close_timeout=10) with sane defaults and pass through to websockets.serve.

tests/e2e/test_websocket_mock_e2e.py (3)

130-141: Repeated connection boiler-plate – extract into fixture for clarity

The manual loop that opens N clients appears multiple times. A small async fixture such as multiple_clients(uri, count) would:

  • reduce duplication
  • guarantee proper cleanup
  • centralise the welcome-message skip logic

Not critical, but it’ll make these sizeable tests easier to digest.


150-160: Tight 1 s timeouts may cause CI flakiness

await asyncio.wait_for(client.recv(), timeout=1.0) is aggressive on busy runners. Consider bumping to 3-5 s (or reading pytest’s --timeout setting) to avoid intermittent failures.


61-66: Comment does not match implementation

The step comment says “server should be running due to auto-start” but auto-start is disabled. Update the comment to avoid confusion for future maintainers.

tests/e2e/test_figma_e2e.py (1)

20-36: Global tools._current_server not reset between tests

Each test sets tools._current_server implicitly via the FigmaMCPServer constructor but never restores None. Should a test fail early, later tests might interact with a half-configured server, causing cascading failures.

Add a fixture or try/finally to save & restore the original value:

orig_server = tools._current_server
tools._current_server = server
try:
    ...
finally:
    tools._current_server = orig_server

This keeps global state predictable.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 023ebdd and f6a2645.

⛔ Files ignored due to path filters (1)
  • addons/figma/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (37)
  • addons/figma/.gitignore (1 hunks)
  • addons/figma/README.md (1 hunks)
  • addons/figma/code.ts (1 hunks)
  • addons/figma/manifest.json (1 hunks)
  • addons/figma/package.json (1 hunks)
  • addons/figma/tsconfig.json (1 hunks)
  • addons/figma/ui.html (1 hunks)
  • addons/figma/websocket-server.js (1 hunks)
  • config/servers.yaml (1 hunks)
  • pyproject.toml (4 hunks)
  • quick_test.py (1 hunks)
  • src/lightfast_mcp/core/base_server.py (3 hunks)
  • src/lightfast_mcp/exceptions.py (1 hunks)
  • src/lightfast_mcp/servers/__init__.py (1 hunks)
  • src/lightfast_mcp/servers/figma/README.md (1 hunks)
  • src/lightfast_mcp/servers/figma/__init__.py (1 hunks)
  • src/lightfast_mcp/servers/figma/server.py (1 hunks)
  • src/lightfast_mcp/servers/figma/tools.py (1 hunks)
  • src/lightfast_mcp/servers/figma/websocket_server.py (1 hunks)
  • src/lightfast_mcp/servers/figma_mcp_server.py (1 hunks)
  • src/lightfast_mcp/servers/websocket_mock/README.md (1 hunks)
  • src/lightfast_mcp/servers/websocket_mock/__init__.py (1 hunks)
  • src/lightfast_mcp/servers/websocket_mock/server.py (1 hunks)
  • src/lightfast_mcp/servers/websocket_mock/tools.py (1 hunks)
  • src/lightfast_mcp/servers/websocket_mock/websocket_server.py (1 hunks)
  • src/lightfast_mcp/servers/websocket_mock_server.py (1 hunks)
  • src/tools/orchestration/config_loader.py (2 hunks)
  • src/tools/orchestration/server_orchestrator.py (1 hunks)
  • tests/conftest.py (1 hunks)
  • tests/e2e/test_figma_e2e.py (1 hunks)
  • tests/e2e/test_websocket_mock_e2e.py (1 hunks)
  • tests/integration/test_figma_integration.py (1 hunks)
  • tests/integration/test_websocket_mock_integration.py (1 hunks)
  • tests/unit/test_figma_server.py (1 hunks)
  • tests/unit/test_modular_servers.py (6 hunks)
  • tests/unit/test_websocket_mock_server.py (1 hunks)
  • tests/utils/websocket_test_client.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/lightfast_mcp/core/base_server.py (1)
src/tools/common/logging.py (2)
  • info (104-106)
  • error (112-126)
src/lightfast_mcp/servers/websocket_mock/__init__.py (1)
src/lightfast_mcp/servers/websocket_mock/server.py (1)
  • WebSocketMockMCPServer (13-139)
src/lightfast_mcp/servers/figma/__init__.py (1)
src/lightfast_mcp/servers/figma/server.py (1)
  • FigmaMCPServer (13-260)
tests/conftest.py (1)
src/lightfast_mcp/core/base_server.py (1)
  • ServerConfig (20-41)
src/lightfast_mcp/servers/__init__.py (3)
src/lightfast_mcp/servers/figma/server.py (1)
  • FigmaMCPServer (13-260)
src/lightfast_mcp/servers/mock/server.py (1)
  • MockMCPServer (13-82)
src/lightfast_mcp/servers/websocket_mock/server.py (1)
  • WebSocketMockMCPServer (13-139)
src/tools/orchestration/config_loader.py (1)
src/tools/common/logging.py (1)
  • info (104-106)
tests/unit/test_websocket_mock_server.py (4)
src/lightfast_mcp/core/base_server.py (1)
  • ServerConfig (20-41)
src/lightfast_mcp/servers/websocket_mock/server.py (5)
  • WebSocketMockMCPServer (13-139)
  • _register_tools (40-58)
  • _on_startup (60-104)
  • _on_shutdown (106-119)
  • _perform_health_check (121-139)
src/lightfast_mcp/servers/websocket_mock/websocket_server.py (11)
  • WebSocketClient (25-44)
  • WebSocketMockServer (47-407)
  • get_server_info (381-407)
  • start (99-132)
  • stop (134-160)
  • _handle_ping (244-253)
  • _handle_echo (255-264)
  • _handle_get_stats (311-336)
  • _handle_simulate_delay (338-352)
  • _handle_error_test (354-379)
  • to_dict (34-44)
src/lightfast_mcp/servers/websocket_mock/tools.py (5)
  • set_current_server (23-26)
  • get_websocket_server_status (48-83)
  • send_websocket_message (86-178)
  • get_websocket_clients (181-223)
  • test_websocket_connection (226-329)
src/lightfast_mcp/servers/figma/tools.py (4)
src/lightfast_mcp/exceptions.py (3)
  • FigmaCommandError (70-73)
  • FigmaConnectionError (64-67)
  • FigmaMCPError (58-61)
src/tools/common/logging.py (2)
  • info (104-106)
  • error (112-126)
tests/integration/test_websocket_mock_integration.py (1)
  • websocket_server (37-42)
src/lightfast_mcp/servers/figma/websocket_server.py (1)
  • send_command_to_plugin (460-486)
tests/integration/test_figma_integration.py (4)
src/lightfast_mcp/core/base_server.py (1)
  • ServerConfig (20-41)
src/lightfast_mcp/servers/figma/server.py (1)
  • FigmaMCPServer (13-260)
src/lightfast_mcp/servers/figma/websocket_server.py (5)
  • FigmaClient (25-46)
  • FigmaWebSocketServer (49-540)
  • start (103-159)
  • stop (161-187)
  • to_dict (35-46)
tests/conftest.py (1)
  • sample_figma_config (57-65)
src/lightfast_mcp/servers/websocket_mock/websocket_server.py (2)
src/lightfast_mcp/servers/websocket_mock/tools.py (1)
  • _is_websocket_closed (29-45)
src/tools/common/logging.py (4)
  • warning (108-110)
  • info (104-106)
  • error (112-126)
  • debug (100-102)
🪛 Biome (1.9.4)
addons/figma/code.ts

[error] 265-265: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

addons/figma/websocket-server.js

[error] 134-135: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 138-141: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🪛 LanguageTool
src/lightfast_mcp/servers/figma/README.md

[uncategorized] ~182-~182: A punctuation mark might be missing here.
Context: ...ion | |--------|---------|-------------| | figma_host | localhost | Host for ...

(AI_EN_LECTOR_MISSING_PUNCTUATION)


[uncategorized] ~363-~363: Use a comma before “and” if it connects two independent clauses (unless they are closely connected and short).
Context: ...Executing** - Verify plugin is connected and active - Check plugin logs for errors -...

(COMMA_COMPOUND_SENTENCE_2)

addons/figma/README.md

[uncategorized] ~88-~88: The preposition ‘to’ seems more likely in this position.
Context: ...`` ### Testing 1. Start the MCP server on ws://localhost:9003 2. Load the plugi...

(AI_HYDRA_LEO_REPLACE_ON_TO)


[uncategorized] ~124-~124: You might be missing the article “the” here.
Context: ... 6. Verify WebSocket server settings in plugin UI

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

src/lightfast_mcp/servers/websocket_mock/README.md

[uncategorized] ~74-~74: You might be missing the article “a” here.
Context: ...clients - get_websocket_clients - Get list of connected clients - `test_websocket_...

(AI_EN_LECTOR_MISSING_DETERMINER_A)


[uncategorized] ~100-~100: A punctuation mark might be missing here.
Context: ...ion | |--------|---------|-------------| | websocket_host | localhost | Host ...

(AI_EN_LECTOR_MISSING_PUNCTUATION)


[grammar] ~399-~399: You’ve repeated a verb. Did you mean to only write one of them?
Context: ...unication testing ### 🔧 Development & Debugging - Debug WebSocket communication issues - Protot...

(REPEATED_VERBS)

🪛 markdownlint-cli2 (0.17.2)
src/lightfast_mcp/servers/figma/README.md

9-9: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


202-202: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


214-214: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


227-227: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


241-241: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


254-254: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


264-264: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


292-292: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


345-345: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


354-354: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


362-362: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

src/lightfast_mcp/servers/websocket_mock/README.md

120-120: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


128-128: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


137-137: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


145-145: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


152-152: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


159-159: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


167-167: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


177-177: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


191-191: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


201-201: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


211-211: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


362-362: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


415-415: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


424-424: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


432-432: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

🪛 Ruff (0.11.9)
tests/unit/test_figma_server.py

210-210: Local variable server is assigned to but never used

Remove assignment to unused variable server

(F841)


221-221: Local variable server is assigned to but never used

Remove assignment to unused variable server

(F841)

src/lightfast_mcp/servers/figma/websocket_server.py

399-399: Local variable request_id is assigned to but never used

Remove assignment to unused variable request_id

(F841)


446-446: Local variable request_id is assigned to but never used

Remove assignment to unused variable request_id

(F841)

🪛 GitHub Actions: CI/CD Pipeline
tests/unit/test_figma_server.py

[error] 210-210: ESLint: Local variable server is assigned to but never used. (F841)


[error] 221-221: ESLint: Local variable server is assigned to but never used. (F841)

src/lightfast_mcp/servers/figma/websocket_server.py

[error] 399-399: ESLint: Local variable request_id is assigned to but never used. (F841)


[error] 446-446: ESLint: Local variable request_id is assigned to but never used. (F841)

🔇 Additional comments (47)
addons/figma/.gitignore (1)

1-9: Exclude typical build artifacts and logs
The .gitignore patterns correctly ignore log files, node_modules, output directories (out/, dist/), and generated plugin code (code.js), preventing accidental commits of development artifacts.

src/lightfast_mcp/servers/figma/__init__.py (1)

1-6: Expose Figma server as public API
The module-level docstring, import of FigmaMCPServer, and __all__ export establish a clear public interface for the Figma MCP server package.

src/lightfast_mcp/servers/websocket_mock/__init__.py (1)

1-6: Expose WebSocket Mock server as public API
The initializer properly documents and exports WebSocketMockMCPServer, making it easy to import and register in the MCP framework.

src/lightfast_mcp/servers/__init__.py (1)

1-14: Aggregate MCP server exports
The package initializer cleanly imports and lists all MCP server classes—including the newly added Figma and WebSocket Mock servers—in __all__, centralizing server access and improving import ergonomics.

src/tools/orchestration/server_orchestrator.py (1)

440-441: Good defensive programming practice.

The addition of a safety check before deleting from _running_servers prevents potential KeyError exceptions that could occur in race conditions or if stop_server is called multiple times on the same server. This improves robustness of the server lifecycle management.

src/lightfast_mcp/core/base_server.py (4)

82-82: Good addition for debugging server lifecycle.

The entry log message helps track when the server lifespan context manager is entered, which is valuable for debugging server startup issues and understanding the server lifecycle flow.


99-101: Excellent improvement for error diagnostics.

Adding detailed traceback logging during startup exceptions significantly improves debugging capabilities. The traceback information will help developers quickly identify the root cause of startup failures.


186-189: Helpful startup information logging.

The detailed startup log provides valuable runtime information including server name, transport type, and URL/stdio mode. This improves operational visibility and makes it easier to verify server configuration.


191-191: Clarified comment improves understanding.

The updated comment clearly explains that FastMCP manages the event loop, which helps developers understand the transport handling responsibilities.

quick_test.py (1)

9-21: Well-structured WebSocket test implementation.

The test function properly handles timeouts, exceptions, and graceful connection closure. The 5-second timeouts are appropriate for a quick connectivity test.

config/servers.yaml (3)

29-43: Well-structured Figma server configuration.

The configuration follows consistent patterns with existing servers and properly separates the MCP server port (8003) from the Figma WebSocket service port (9003). The required "Figma" app dependency is appropriate.


44-57: Consistent WebSocket mock server configuration.

The configuration maintains consistency with other server entries and uses appropriate port separation (8004 for MCP, 9004 for WebSocket). No required apps makes sense for a mock server.


29-57:

❓ Verification inconclusive

Verify port assignments don't conflict with existing services.

Please ensure the assigned ports (8003, 8004, 9003, 9004) don't conflict with other services in your environment or with any default ports used by the applications.


🏁 Script executed:

#!/bin/bash
# Description: Check if any of the new ports are commonly used by other services
# Expected: Information about potential port conflicts

echo "Checking port assignments for potential conflicts..."
echo "New ports: 8003 (Figma MCP), 8004 (WebSocket Mock MCP), 9003 (Figma WebSocket), 9004 (WebSocket Mock WebSocket)"
echo ""

# Check common port assignments
echo "Common port usage for assigned ports:"
echo "Port 8003: Often used by development servers"
echo "Port 8004: Often used by development servers" 
echo "Port 9003: Less commonly used, good choice"
echo "Port 9004: Less commonly used, good choice"
echo ""

# Check if ports are currently in use (this will work on Unix-like systems)
for port in 8003 8004 9003 9004; do
    if command -v netstat >/dev/null 2>&1; then
        result=$(netstat -ln 2>/dev/null | grep ":$port " || echo "Port $port appears to be available")
        echo "Port $port: $result"
    elif command -v ss >/dev/null 2>&1; then
        result=$(ss -ln 2>/dev/null | grep ":$port " || echo "Port $port appears to be available")
        echo "Port $port: $result"
    else
        echo "Port $port: Cannot check availability (netstat/ss not available)"
    fi
done

Length of output: 1674


Action Required: Manually Verify Port Availability
The sandbox check couldn’t determine if ports 8003, 8004, 9003, and 9004 are already in use. Before merging, please confirm these ports don’t conflict with any services in your deployment environment or with defaults used by Figma or your WebSocket mocks.

• File: config/servers.yaml (lines 29–57)
• Ports to verify: 8003 (Figma MCP), 8004 (WebSocket Mock MCP), 9003 (Figma WebSocket), 9004 (WebSocket Mock WebSocket)

Recommended commands on your host:

# Linux/macOS
lsof -i:8003,8004,9003,9004
# or
netstat -plnt | grep -E ':(8003|8004|9003|9004)\b'

Adjust ports or update configurations if any are in use.

addons/figma/tsconfig.json (1)

1-23: LGTM! Well-configured TypeScript setup for Figma plugin development.

The configuration follows best practices with strict type checking, appropriate ES2020 target, and proper inclusion of Figma plugin types. The output directory correctly aligns with the manifest.json main field.

addons/figma/manifest.json (1)

1-20: LGTM! Well-structured Figma plugin manifest with appropriate WebSocket permissions.

The manifest correctly configures network access for WebSocket communication with the local MCP server. The reasoning field clearly explains the purpose of the network permissions.

Please verify that the plugin ID 1509155185436575957 is properly registered with Figma and matches your development environment.

src/tools/orchestration/config_loader.py (1)

154-156: LGTM! Environment variable injection enhances configuration flexibility.

The placement of environment variable injection after setting the server type is logical and ensures proper processing order.

src/lightfast_mcp/servers/websocket_mock_server.py (2)

20-64: LGTM! Well-structured configuration handling.

The main function demonstrates good practices:

  • Environment configuration support for orchestrator integration
  • Proper JSON parsing with error handling
  • Graceful fallback to default configuration
  • Clear logging of configuration source

67-81: Good default configuration setup.

The default configuration is well-structured with appropriate defaults and clear documentation in comments.

src/lightfast_mcp/servers/figma_mcp_server.py (2)

43-91: Comprehensive CLI argument parsing implementation.

The argument parser is well-structured with helpful descriptions, examples, and appropriate defaults. Good use of argparse features.


146-154: Excellent error handling with conditional verbosity.

The exception handling properly distinguishes between user interruption and actual errors, with optional verbose traceback output.

src/lightfast_mcp/exceptions.py (1)

58-85: Excellent exception hierarchy following established patterns.

The new Figma exception classes:

  • Follow the same pattern as existing Blender exceptions
  • Have proper inheritance (FigmaTimeoutError extends FigmaConnectionError)
  • Include clear, descriptive docstrings
  • Maintain consistency with project conventions
src/lightfast_mcp/servers/figma/README.md (1)

1-404: Comprehensive and well-structured documentation.

This README provides excellent coverage of:

  • Clear architecture diagrams and explanations
  • Detailed feature descriptions
  • Complete configuration options
  • WebSocket protocol specifications
  • Troubleshooting guidance
  • Development instructions

The documentation effectively explains the server's purpose, capabilities, and usage patterns.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~182-~182: A punctuation mark might be missing here.
Context: ...ion | |--------|---------|-------------| | figma_host | localhost | Host for ...

(AI_EN_LECTOR_MISSING_PUNCTUATION)


[uncategorized] ~363-~363: Use a comma before “and” if it connects two independent clauses (unless they are closely connected and short).
Context: ...Executing** - Verify plugin is connected and active - Check plugin logs for errors -...

(COMMA_COMPOUND_SENTENCE_2)

🪛 markdownlint-cli2 (0.17.2)

9-9: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


202-202: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


214-214: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


227-227: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


241-241: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


254-254: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


264-264: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


292-292: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)


345-345: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


354-354: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)


362-362: Emphasis used instead of a heading
null

(MD036, no-emphasis-as-heading)

tests/integration/test_websocket_mock_integration.py (1)

23-34:

❌ Incorrect review comment

Use an ephemeral port to avoid CI flakiness

Hard-coding websocket_port = 9998 risks random “address already in use” failures when the test suite runs in parallel (or on developer machines with that port occupied).

-            "websocket_port": 9998,  # Use different port for integration tests
+            "websocket_port": 0,     # 0 => let OS choose a free port

Because the fixtures already refer to server.port after start-up, the remainder of the tests need no changes—the actual port will be filled in automatically.


🏁 Script executed:

#!/bin/bash
# Locate the definition of ServerConfig
rg -n "class ServerConfig" -A 5

# Find where the "websocket_mock" type is handled or the server implementation
rg -n "websocket_mock" -A 5

# Verify how the integration test refers to server.port
rg -n "server\\.port" -A 2 tests/integration/test_websocket_mock_integration.py

Length of output: 18300


🏁 Script executed:

#!/bin/bash
# Display the WebSocketMockServer class definition and its start method
rg -n "class WebSocketMockServer" -A 50 src/lightfast_mcp/servers/websocket_mock/websocket_server.py

Length of output: 2189


🏁 Script executed:

#!/bin/bash
# Inspect WebSocketMockServer start method
rg -n "def start" -A 40 src/lightfast_mcp/servers/websocket_mock/websocket_server.py

# Inspect WebSocketMockMCPServer start method
rg -n "def start" -A 80 src/lightfast_mcp/servers/websocket_mock/server.py

Length of output: 1665


🏁 Script executed:

#!/bin/bash
# Show the beginning of the MCP server implementation to inspect port handling
sed -n '1,200p' src/lightfast_mcp/servers/websocket_mock/server.py

Length of output: 6257


🏁 Script executed:

#!/bin/bash
# Inspect the server_config fixture and related setup in integration tests
rg -n "def server_config" -A 20 tests/integration/test_websocket_mock_integration.py

# Show how the server fixture uses server_config and starts the server
rg -n "pytest.fixture" -A 20 tests/integration/test_websocket_mock_integration.py

Length of output: 2786


🏁 Script executed:

#!/bin/bash
# Search for get_server_info method in websocket_server.py
rg -n "def get_server_info" -A 20 src/lightfast_mcp/servers/websocket_mock/websocket_server.py

Length of output: 993


Ignore ephemeral port recommendation

The WebSocketMockServer.start method never updates self.port after binding to an OS‐assigned port, so passing "websocket_port": 0 would leave server.port at 0 and all integration tests that do ws://host:port would fail. The hard-coded ports in the fixtures and server_config must remain unless you also update WebSocketMockServer.start to read back the assigned port (for example via self.server.sockets[0].getsockname()[1]).

Likely an incorrect or invalid review comment.

addons/figma/code.ts (1)

327-329: figma.on('close') is deprecated

The Figma plugin API removed the 'close' event. The recommended pattern is to perform cleanup in the promise returned by figma.closePlugin() or in the finally block of long-running tasks.

Leaving this listener in may have no effect or could throw in future Figma releases.

src/lightfast_mcp/servers/websocket_mock/tools.py (6)

8-26: LGTM! Good imports and server reference pattern.

The imports are well-organized and the global server reference pattern is appropriate for MCP tool modules.


29-46: Excellent defensive programming for WebSocket compatibility.

This function handles different versions of the websockets library gracefully with appropriate fallbacks and safe error handling.


48-84: Well-structured status function with comprehensive information.

Good error checking, detailed status reporting, and proper handling of missing configuration.


86-179: Excellent message sending implementation with robust error handling.

The function properly handles both targeted and broadcast messaging with concurrent operations, comprehensive error tracking, and detailed result reporting.


181-224: Good client information retrieval with consistent error handling.

The function follows established patterns and provides comprehensive client information including real-time connection status.


226-330: Comprehensive testing function with multiple test scenarios.

The function supports various test types and properly reuses existing functionality. The stress test parameters are reasonable for testing purposes.

tests/integration/test_figma_integration.py (10)

1-24: Well-organized imports and test class structure.

Good use of integration test markers and proper import organization for testing the Figma MCP server components.


25-54: Excellent lifecycle test with proper isolation.

The test properly patches background operations and comprehensively tests the server startup/shutdown cycle with health checks.


55-123: Comprehensive tools integration test with realistic mocks.

The test creates well-structured mock clients and thoroughly tests the integration between tools and the WebSocket server.


124-167: Thorough design command testing with proper verification.

The test properly verifies command execution flow and parameter passing for both targeted and broadcast scenarios.


168-197: Good document state retrieval test.

The test follows established patterns and properly verifies the document state request flow to the WebSocket server.


198-227: Excellent error handling coverage across scenarios.

The test comprehensively covers error conditions and verifies graceful degradation when components are unavailable.


228-246: Good isolated WebSocket server lifecycle test.

The test properly verifies the WebSocket server's independent behavior and safe operations.


247-283: Comprehensive FigmaClient dataclass testing.

The test thoroughly covers initialization, default values, and serialization functionality of the FigmaClient dataclass.


284-324: Excellent comprehensive health check scenario testing.

The test covers all combinations of server states and exception conditions, ensuring robust health monitoring.


325-431: Excellent comprehensive integration testing coverage.

The remaining tests thoroughly cover error scenarios, configuration variations, and concurrent operations, providing robust integration test coverage.

tests/utils/websocket_test_client.py (4)

18-86: Well-designed WebSocket test client with excellent error handling.

The class provides comprehensive WebSocket testing capabilities with proper async handling, clear user feedback, and robust error management.


87-116: Consistent and well-structured message method implementations.

All message methods follow consistent patterns with appropriate parameters and good use of timestamps and test identifiers.


117-214: Excellent interactive session implementation with good UX.

The interactive functionality provides a user-friendly interface with proper command parsing, help text, and graceful error handling.


216-282: Comprehensive test scenarios with excellent automation.

The automated test scenarios provide thorough coverage and the main function offers a clean command-line interface with sensible defaults.

tests/unit/test_modular_servers.py (3)

179-269: Excellent Figma server test class following established patterns.

The test class comprehensively covers the Figma server functionality with proper patching and follows the same excellent patterns as existing server tests.


304-358: Good integration of Figma server into existing test patterns.

The integration tests properly incorporate the Figma server while maintaining consistency with existing server testing approaches.


390-467: Consistent validation and string representation test coverage.

The updates properly extend existing test patterns to include Figma server validation and string representation testing.

Comment on lines +40 to +59
def _register_tools(self):
"""Register WebSocket mock server tools."""
if not self.mcp:
return

# Register tools from the tools module
self.mcp.tool()(tools.get_websocket_server_status)
self.mcp.tool()(tools.send_websocket_message)
self.mcp.tool()(tools.get_websocket_clients)
self.mcp.tool()(tools.test_websocket_connection)

# Update the server info with available tools
self.info.tools = [
"get_websocket_server_status",
"send_websocket_message",
"get_websocket_clients",
"test_websocket_connection",
]
logger.info(f"Registered {len(self.info.tools)} tools: {self.info.tools}")

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider deferring tool registration until self.mcp is definitely set

_register_tools() bails out early when self.mcp is falsy.
If BaseServer calls _register_tools() before injecting the MCP instance, none of the tools will ever be registered.
Please double-check the call-order in BaseServer; if uncertain, move the registration logic into _on_startup() (after the MCP is available) to guarantee predictable behaviour.

🤖 Prompt for AI Agents
In src/lightfast_mcp/servers/websocket_mock/server.py around lines 40 to 59, the
_register_tools method exits early if self.mcp is not set, which may cause tools
not to be registered if called before MCP injection. To fix this, move the tool
registration logic from _register_tools into the _on_startup method, ensuring it
runs only after self.mcp is definitely initialized, thus guaranteeing all tools
are registered predictably.

Comment on lines +17 to +20
SERVER_TYPE: ClassVar[str] = "websocket_mock"
SERVER_VERSION: ClassVar[str] = "1.0.0"
REQUIRED_DEPENDENCIES: ClassVar[list[str]] = ["websockets"]
REQUIRED_APPS: ClassVar[list[str]] = []
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add ‘websockets’ to the package dependencies

The class declares REQUIRED_DEPENDENCIES = ["websockets"], but pyproject.toml currently does not list websockets under [project].dependencies.
Trying to start this server in a fresh environment will therefore raise ModuleNotFoundError.

Proposed diff (pyproject.toml shown for context):

 dependencies = [
     "fastmcp>=2.0.0",
     "rich",
     "pyyaml",
+    "websockets",  # Needed by WebSocketMockMCPServer
     "anthropic",
     "openai",
     "typer",
 ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SERVER_TYPE: ClassVar[str] = "websocket_mock"
SERVER_VERSION: ClassVar[str] = "1.0.0"
REQUIRED_DEPENDENCIES: ClassVar[list[str]] = ["websockets"]
REQUIRED_APPS: ClassVar[list[str]] = []
# pyproject.toml
[project]
dependencies = [
"fastmcp>=2.0.0",
"rich",
"pyyaml",
"websockets", # Needed by WebSocketMockMCPServer
"anthropic",
"openai",
"typer",
]
🤖 Prompt for AI Agents
In src/lightfast_mcp/servers/websocket_mock/server.py lines 17 to 20, the class
specifies "websockets" as a required dependency, but this package is missing
from pyproject.toml under [project].dependencies. To fix this, add "websockets"
to the dependencies list in pyproject.toml to ensure it is installed in fresh
environments and prevent ModuleNotFoundError when starting the server.

Comment on lines +106 to +181
function processCommand(command) {
const requestId = command.id || Date.now();

try {
switch (command.type) {
case 'ping':
return {
type: 'response',
requestId: requestId,
status: 'success',
result: {
message: 'pong',
timestamp: Date.now(),
server: 'figma-plugin-test',
serverRunning: true,
port: port
}
};

case 'get_document_info':
return {
type: 'response',
requestId: requestId,
status: 'success',
result: mockDocumentState
};

case 'execute_design_command':
const designCommand = command.params?.command || '';
console.log(`🎨 Executing design command: ${designCommand}`);

let result = {
message: 'Command received but not implemented',
command: designCommand
};

if (designCommand.toLowerCase().includes('create rectangle')) {
result = {
message: 'Rectangle created successfully',
command: designCommand,
created_node: {
id: `rect_${Date.now()}`,
name: 'AI Created Rectangle',
type: 'RECTANGLE'
}
};
} else if (designCommand.toLowerCase().includes('create circle')) {
result = {
message: 'Circle created successfully',
command: designCommand,
created_node: {
id: `circle_${Date.now()}`,
name: 'AI Created Circle',
type: 'ELLIPSE'
}
};
} else if (designCommand.toLowerCase().includes('create text')) {
result = {
message: 'Text created successfully',
command: designCommand,
created_node: {
id: `text_${Date.now()}`,
name: 'AI Created Text',
type: 'TEXT',
text: 'Hello from AI!'
}
};
}

return {
type: 'response',
requestId: requestId,
status: 'success',
result: result
};

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrap variable declarations inside switch-case blocks to avoid scope bleed

Biome flags designCommand and result because declarations inside a case share the parent scope; subsequent cases can accidentally access them.
Wrap each case in braces or move the declarations to const statements inside a block.

Suggested fix:

-        case 'execute_design_command':
-            const designCommand = command.params?.command || '';
-            console.log(`🎨 Executing design command: ${designCommand}`);
-            
-            let result = { 
+        case 'execute_design_command': {
+            const designCommand = command.params?.command || '';
+            console.log(`🎨 Executing design command: ${designCommand}`);
+
+            let result = {
                 message: 'Command received but not implemented',
                 command: designCommand
             };
             …
-                return {
+            return {
                 type: 'response',
                 requestId: requestId,
                 status: 'success',
                 result: result
             };
-            
+        }

The surrounding braces create a block scope, satisfying noSwitchDeclarations and preventing accidental leakage.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function processCommand(command) {
const requestId = command.id || Date.now();
try {
switch (command.type) {
case 'ping':
return {
type: 'response',
requestId: requestId,
status: 'success',
result: {
message: 'pong',
timestamp: Date.now(),
server: 'figma-plugin-test',
serverRunning: true,
port: port
}
};
case 'get_document_info':
return {
type: 'response',
requestId: requestId,
status: 'success',
result: mockDocumentState
};
case 'execute_design_command':
const designCommand = command.params?.command || '';
console.log(`🎨 Executing design command: ${designCommand}`);
let result = {
message: 'Command received but not implemented',
command: designCommand
};
if (designCommand.toLowerCase().includes('create rectangle')) {
result = {
message: 'Rectangle created successfully',
command: designCommand,
created_node: {
id: `rect_${Date.now()}`,
name: 'AI Created Rectangle',
type: 'RECTANGLE'
}
};
} else if (designCommand.toLowerCase().includes('create circle')) {
result = {
message: 'Circle created successfully',
command: designCommand,
created_node: {
id: `circle_${Date.now()}`,
name: 'AI Created Circle',
type: 'ELLIPSE'
}
};
} else if (designCommand.toLowerCase().includes('create text')) {
result = {
message: 'Text created successfully',
command: designCommand,
created_node: {
id: `text_${Date.now()}`,
name: 'AI Created Text',
type: 'TEXT',
text: 'Hello from AI!'
}
};
}
return {
type: 'response',
requestId: requestId,
status: 'success',
result: result
};
function processCommand(command) {
const requestId = command.id || Date.now();
try {
switch (command.type) {
case 'ping':
return {
type: 'response',
requestId: requestId,
status: 'success',
result: {
message: 'pong',
timestamp: Date.now(),
server: 'figma-plugin-test',
serverRunning: true,
port: port
}
};
case 'get_document_info':
return {
type: 'response',
requestId: requestId,
status: 'success',
result: mockDocumentState
};
case 'execute_design_command': {
const designCommand = command.params?.command || '';
console.log(`🎨 Executing design command: ${designCommand}`);
let result = {
message: 'Command received but not implemented',
command: designCommand
};
if (designCommand.toLowerCase().includes('create rectangle')) {
result = {
message: 'Rectangle created successfully',
command: designCommand,
created_node: {
id: `rect_${Date.now()}`,
name: 'AI Created Rectangle',
type: 'RECTANGLE'
}
};
} else if (designCommand.toLowerCase().includes('create circle')) {
result = {
message: 'Circle created successfully',
command: designCommand,
created_node: {
id: `circle_${Date.now()}`,
name: 'AI Created Circle',
type: 'ELLIPSE'
}
};
} else if (designCommand.toLowerCase().includes('create text')) {
result = {
message: 'Text created successfully',
command: designCommand,
created_node: {
id: `text_${Date.now()}`,
name: 'AI Created Text',
type: 'TEXT',
text: 'Hello from AI!'
}
};
}
return {
type: 'response',
requestId: requestId,
status: 'success',
result: result
};
}
// other cases...
}
} catch (err) {
// error handling
}
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 134-135: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 138-141: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Unsafe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🤖 Prompt for AI Agents
In addons/figma/websocket-server.js between lines 106 and 181, the variables
designCommand and result are declared directly inside a switch case, causing
scope leakage across cases. To fix this, wrap the entire code inside each case
block with curly braces to create a new block scope, ensuring that variable
declarations like designCommand and result are scoped only within their
respective cases and do not bleed into others.

Comment on lines +245 to +289
function executeCodeSync(code: string): any {
try {
console.log('Executing JavaScript code synchronously:', code);

// Capture console output
let output: string[] = [];
const originalLog = console.log;
console.log = (...args: any[]) => {
output.push(args.join(' '));
originalLog(...args);
};

let result: any = undefined;
let executed = true;
let error: string | undefined = undefined;

try {
// Execute code directly in the main context
// This is necessary for Figma API calls to work properly
eval(code);

// If code sets a result variable, use it
if (typeof result !== 'undefined') {
return {
code_executed: true,
original_code: code,
execution_result: {
executed: true,
result: result,
output: output.join('\n') || 'Code executed successfully'
},
timestamp: Date.now()
};
}

return {
code_executed: true,
original_code: code,
execution_result: {
executed: true,
result: 'Code executed successfully',
output: output.join('\n') || 'No output'
},
timestamp: Date.now()
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

eval poses security & stability risks – isolate or remove

executeCodeSync executes arbitrary strings with eval, exposing:

  • XSS / code-injection if untrusted input reaches the plugin.
  • Possible plugin termination or corrupted state (infinite loops, figma.closePlugin, etc.).
  • Future review headaches (TypeScript compiler flags this with noGlobalEval).

If executing arbitrary code is a hard requirement, at minimum:

  1. Wrap the evaluation in an iframe sandbox via figma.showUI and communicate results with postMessage.
  2. Use a whitelisted API approach—parse the string into a restricted AST and allow only safe operations.
  3. Add an execution timeout (e.g. Promise.race([worker, sleep(2500)])) to prevent hangs.

Otherwise, replace eval with specific helper functions that cover the required automation scenarios.

🧰 Tools
🪛 Biome (1.9.4)

[error] 265-265: eval() exposes to security risks and performance issues.

See the MDN web docs for more details.
Refactor the code so that it doesn't need to call eval().

(lint/security/noGlobalEval)

🤖 Prompt for AI Agents
In addons/figma/code.ts between lines 245 and 289, the use of eval to execute
arbitrary code poses security and stability risks. To fix this, remove the
direct eval call and instead run the code inside a sandboxed iframe using
figma.showUI, communicating results back via postMessage. Alternatively,
implement a whitelist-based parser that converts the code string into a safe AST
allowing only approved operations. Also, add an execution timeout mechanism to
prevent infinite loops or hangs. If arbitrary code execution is not essential,
replace eval with specific helper functions that perform the needed tasks
safely.

Comment on lines +148 to +150
logger.debug(
f"🔧 Server is_serving: {getattr(self.server, 'is_serving', 'unknown')()}"
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

getattr(..., 'is_serving', 'unknown')() can raise TypeError

When the websockets version does not expose is_serving,
getattr returns the string 'unknown'. Calling the result ('unknown'()) raises a TypeError, breaking server start-up logging.

-logger.debug(
-    f"🔧 Server is_serving: {getattr(self.server, 'is_serving', 'unknown')()}"
-)
+if hasattr(self.server, "is_serving"):
+    logger.debug(f"🔧 Server is_serving: {self.server.is_serving()}")
+else:
+    logger.debug("🔧 Server is_serving attribute not available on this websockets version")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.debug(
f"🔧 Server is_serving: {getattr(self.server, 'is_serving', 'unknown')()}"
)
if hasattr(self.server, "is_serving"):
logger.debug(f"🔧 Server is_serving: {self.server.is_serving()}")
else:
logger.debug("🔧 Server is_serving attribute not available on this websockets version")
🤖 Prompt for AI Agents
In src/lightfast_mcp/servers/figma/websocket_server.py around lines 148 to 150,
the code calls getattr(self.server, 'is_serving', 'unknown')() which raises a
TypeError if 'is_serving' is not present because it tries to call the string
'unknown' as a function. Fix this by first retrieving the attribute without
calling it, then check if it is callable before calling it; if not callable, use
a default value or the attribute itself for logging.

Comment on lines +146 to +151
for client in self.clients.values():
if not self._is_websocket_closed(client.websocket):
close_tasks.append(client.websocket.close())

if close_tasks:
await asyncio.gather(*close_tasks, return_exceptions=True)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential RuntimeError when closing clients during stop()

self.clients.values() returns a live view; if a client disconnects while you iterate (e.g. because await client.websocket.close() triggers the finally block in _handle_client) the dict size mutates and Python raises RuntimeError: dictionary changed size during iteration.

-            for client in self.clients.values():
+            # Snapshot to avoid RuntimeError if dict mutates during iteration
+            for client in list(self.clients.values()):

The same pattern appears in _handle_broadcast; consider applying the fix there too.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for client in self.clients.values():
if not self._is_websocket_closed(client.websocket):
close_tasks.append(client.websocket.close())
if close_tasks:
await asyncio.gather(*close_tasks, return_exceptions=True)
# Snapshot to avoid RuntimeError if dict mutates during iteration
for client in list(self.clients.values()):
if not self._is_websocket_closed(client.websocket):
close_tasks.append(client.websocket.close())
if close_tasks:
await asyncio.gather(*close_tasks, return_exceptions=True)
🤖 Prompt for AI Agents
In src/lightfast_mcp/servers/websocket_mock/websocket_server.py around lines 146
to 151, iterating directly over self.clients.values() can cause a RuntimeError
if the dictionary changes size during iteration due to client disconnects. To
fix this, iterate over a static list copy of the clients by wrapping
self.clients.values() with list() before the loop. Apply the same fix to the
similar iteration pattern in the _handle_broadcast method to prevent the same
issue there.

Comment on lines +278 to +286
# Send to all other clients
broadcast_tasks = []
for other_client in self.clients.values():
if other_client.id != client.id and not self._is_websocket_closed(
other_client.websocket
):
broadcast_tasks.append(
other_client.websocket.send(json.dumps(broadcast_data))
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Broadcast loop vulnerable to concurrent dict mutation & adds needless awaits

  1. Use a snapshot to avoid the mutation issue noted above.
  2. Skip closed peers before scheduling send, saving an unnecessary coroutine creation.
-        broadcast_tasks = []
-        for other_client in self.clients.values():
-            if other_client.id != client.id and not self._is_websocket_closed(
-                other_client.websocket
-            ):
-                broadcast_tasks.append(
-                    other_client.websocket.send(json.dumps(broadcast_data))
-                )
+        recipients = [
+            c
+            for c in list(self.clients.values())  # snapshot
+            if c.id != client.id and not self._is_websocket_closed(c.websocket)
+        ]
+        await asyncio.gather(
+            *[c.websocket.send(json.dumps(broadcast_data)) for c in recipients],
+            return_exceptions=True,
+        )

Remember to update the recipients count you send back to the sender.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Send to all other clients
broadcast_tasks = []
for other_client in self.clients.values():
if other_client.id != client.id and not self._is_websocket_closed(
other_client.websocket
):
broadcast_tasks.append(
other_client.websocket.send(json.dumps(broadcast_data))
)
# Send to all other clients
recipients = [
c
for c in list(self.clients.values()) # snapshot
if c.id != client.id and not self._is_websocket_closed(c.websocket)
]
await asyncio.gather(
*[c.websocket.send(json.dumps(broadcast_data)) for c in recipients],
return_exceptions=True,
)
🤖 Prompt for AI Agents
In src/lightfast_mcp/servers/websocket_mock/websocket_server.py around lines 278
to 286, the broadcast loop iterates directly over self.clients.values(), which
can cause errors if the dictionary is mutated concurrently. Fix this by
iterating over a snapshot copy of the clients, such as a list of the values.
Also, check if the websocket is closed before scheduling the send coroutine to
avoid creating unnecessary coroutines. Finally, update the recipients count sent
back to the sender to reflect the actual number of clients the message was
broadcast to.

Comment on lines +23 to +36
@pytest.fixture
def server_config(self):
"""Create a server configuration for E2E testing."""
return ServerConfig(
name="E2ETestWebSocketMockMCP",
description="E2E Test WebSocket Mock MCP Server",
config={
"type": "websocket_mock",
"websocket_host": "localhost",
"websocket_port": 9997, # Use different port for E2E tests
"auto_start_websocket": False, # Manual control for tests
},
)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

auto_start_websocket=False contradicts later expectations

The fixture explicitly disables auto-start, yet the first test asserts that the WebSocket server is already running right after startup (lines 61-70). Either:

  1. change the fixture to True, or
  2. adjust the assertions to start the server explicitly.

Mismatched assumptions will create flaky tests the moment startup behaviour changes.

🤖 Prompt for AI Agents
In tests/e2e/test_websocket_mock_e2e.py around lines 23 to 36, the server_config
fixture sets auto_start_websocket to False, but later tests expect the WebSocket
server to be running immediately after startup. To fix this, either change
auto_start_websocket to True in the fixture to match the test assumptions or
modify the tests to explicitly start the WebSocket server before asserting it is
running, ensuring consistency between setup and test expectations.

- Included the `websockets` library in the project dependencies to support WebSocket server functionality.
- Updated the `uv.lock` file to reflect the new dependency.
- Registered the `figma` server type in the `ServerOrchestrator` for enhanced orchestration capabilities.
- Removed unnecessary retrieval of `request_id` from incoming data in the document info and code execution response handlers, simplifying the code.
- Updated unit tests to reflect variable name changes for consistency and clarity.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (1)
src/lightfast_mcp/servers/figma/websocket_server.py (1)

149-150: ⚠️ Potential issue

getattr(..., 'is_serving', 'unknown')() can raise TypeError

When the underlying websockets version lacks is_serving, the fallback string 'unknown'
is invoked as a callable, crashing the server right after start-up.

-            logger.debug(
-                f"🔧 Server is_serving: {getattr(self.server, 'is_serving', 'unknown')()}"
-            )
+            if hasattr(self.server, "is_serving"):
+                logger.debug(f"🔧 Server is_serving: {self.server.is_serving()}")
+            else:
+                logger.debug("🔧 Server is_serving attribute not available on this websockets version")
🧹 Nitpick comments (1)
src/lightfast_mcp/servers/figma/websocket_server.py (1)

200-201: websocket.state may not exist on all versions

Accessing websocket.state without a feature check can break on newer websockets versions.
Guard the debug line with hasattr to keep logs useful without risking an AttributeError.

-        logger.debug(f"🔧 Client {client_id} WebSocket state: {websocket.state}")
+        if hasattr(websocket, "state"):
+            logger.debug(f"🔧 Client {client_id} WebSocket state: {websocket.state}")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d95f5dc and b61a78a.

📒 Files selected for processing (2)
  • src/lightfast_mcp/servers/figma/websocket_server.py (1 hunks)
  • tests/unit/test_figma_server.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/unit/test_figma_server.py (5)
src/lightfast_mcp/core/base_server.py (1)
  • ServerConfig (20-41)
src/lightfast_mcp/servers/figma/server.py (5)
  • FigmaMCPServer (13-260)
  • _check_application (63-69)
  • _on_startup (71-87)
  • _on_shutdown (89-98)
  • _perform_health_check (100-120)
src/lightfast_mcp/servers/figma/websocket_server.py (7)
  • FigmaClient (25-46)
  • FigmaWebSocketServer (49-538)
  • get_server_info (511-538)
  • start (103-159)
  • send_command_to_plugin (458-484)
  • stop (161-187)
  • to_dict (35-46)
tests/conftest.py (1)
  • sample_figma_config (57-65)
src/lightfast_mcp/servers/figma/tools.py (2)
  • set_current_server (26-29)
  • get_state (32-137)
🪛 GitHub Actions: CI/CD Pipeline
tests/unit/test_figma_server.py

[error] 90-90: AssertionError: Expected tool 'execute_command' not found in registered tools ['get_state', 'execute_code'].


[error] 315-315: AttributeError: module 'lightfast_mcp.servers.figma.tools' has no attribute 'execute_command'. Did you mean: 'execute_code'?


[error] 334-334: AttributeError: module 'lightfast_mcp.servers.figma.tools' has no attribute 'execute_command'. Did you mean: 'execute_code'?


[error] 358-358: AttributeError: module 'lightfast_mcp.servers.figma.tools' has no attribute 'execute_command'. Did you mean: 'execute_code'?


[error] 386-386: AttributeError: module 'lightfast_mcp.servers.figma.tools' has no attribute 'execute_command'. Did you mean: 'execute_code'?


[error] 433-433: AssertionError: Expected message handler 'execute_design_command' not found in server.message_handlers.


[error] 538-538: AssertionError: Expected tool 'execute_command' not found in registered tools ['get_state', 'execute_code'].

Comment on lines +417 to +434
"""Test that default message handlers are registered."""
server = FigmaWebSocketServer()

expected_handlers = [
"ping",
"pong",
"get_document_info",
"execute_design_command",
"get_server_status",
"plugin_info",
"document_update",
"document_info_response",
"design_command_response",
]

for handler in expected_handlers:
assert handler in server.message_handlers

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Handler name discrepancy (execute_design_command)

FigmaWebSocketServer.message_handlers currently registers execute_code; the test expects execute_design_command, causing assertions to fail.

Update the expectation or register an alias in production code:

-            "execute_design_command",
+            "execute_code",

(or register the alias as shown in the websocket-server diff below).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""Test that default message handlers are registered."""
server = FigmaWebSocketServer()
expected_handlers = [
"ping",
"pong",
"get_document_info",
"execute_design_command",
"get_server_status",
"plugin_info",
"document_update",
"document_info_response",
"design_command_response",
]
for handler in expected_handlers:
assert handler in server.message_handlers
"""Test that default message handlers are registered."""
server = FigmaWebSocketServer()
expected_handlers = [
"ping",
"pong",
"get_document_info",
"execute_code",
"get_server_status",
"plugin_info",
"document_update",
"document_info_response",
"design_command_response",
]
for handler in expected_handlers:
assert handler in server.message_handlers
🧰 Tools
🪛 GitHub Actions: CI/CD Pipeline

[error] 433-433: AssertionError: Expected message handler 'execute_design_command' not found in server.message_handlers.

🤖 Prompt for AI Agents
In tests/unit/test_figma_server.py around lines 417 to 434, the test expects the
handler name "execute_design_command" but the actual registered handler in
FigmaWebSocketServer.message_handlers is "execute_code". To fix this, either
update the test's expected_handlers list to use "execute_code" instead of
"execute_design_command" or modify the production code to register
"execute_design_command" as an alias for "execute_code" so both names are
recognized.

Comment on lines 88 to 101
"""Register default message handlers."""
self.message_handlers.update(
{
"ping": self._handle_ping,
"pong": self._handle_pong,
"get_document_info": self._handle_get_document_info,
"execute_code": self._handle_execute_code,
"get_server_status": self._handle_get_server_status,
"plugin_info": self._handle_plugin_info,
"document_update": self._handle_document_update,
"document_info_response": self._handle_document_info_response,
"code_execution_response": self._handle_code_execution_response,
}
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add alias for execute_design_command to avoid breaking callers/tests

Some callers (and the plugin codebase, per tests) use the older message name execute_design_command.
Register it as a thin wrapper to _handle_execute_code to maintain backward compatibility.

@@
                 "execute_code": self._handle_execute_code,
+                # Back-compat: older clients/tests still send this
+                "execute_design_command": self._handle_execute_code,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""Register default message handlers."""
self.message_handlers.update(
{
"ping": self._handle_ping,
"pong": self._handle_pong,
"get_document_info": self._handle_get_document_info,
"execute_code": self._handle_execute_code,
"get_server_status": self._handle_get_server_status,
"plugin_info": self._handle_plugin_info,
"document_update": self._handle_document_update,
"document_info_response": self._handle_document_info_response,
"code_execution_response": self._handle_code_execution_response,
}
)
"""Register default message handlers."""
self.message_handlers.update(
{
"ping": self._handle_ping,
"pong": self._handle_pong,
"get_document_info": self._handle_get_document_info,
"execute_code": self._handle_execute_code,
# Back-compat: older clients/tests still send this
"execute_design_command": self._handle_execute_code,
"get_server_status": self._handle_get_server_status,
"plugin_info": self._handle_plugin_info,
"document_update": self._handle_document_update,
"document_info_response": self._handle_document_info_response,
"code_execution_response": self._handle_code_execution_response,
}
)
🤖 Prompt for AI Agents
In src/lightfast_mcp/servers/figma/websocket_server.py around lines 88 to 101,
add an alias for the message handler "execute_design_command" that points to the
existing _handle_execute_code method. This will maintain backward compatibility
for callers and tests that still use the older message name. Update the
self.message_handlers dictionary to include "execute_design_command":
self._handle_execute_code alongside the existing handlers.

- Introduced new images for the Figma plugin installation and interface, enhancing documentation and user guidance.
- These images provide visual support for users to better understand the plugin setup and usage.
…in Figma tools

- Simplified JSON error responses in `get_state` and `execute_code` functions by consolidating error handling logic.
- Introduced a new `get_figma_server_status` function to provide detailed status information about the Figma WebSocket server, including running status and connected clients.
- Enhanced logging for error scenarios to improve debugging and monitoring capabilities.
…ve error handling

- Added stub functions for removed design command tools, providing clear error messages and alternatives for users.
- Updated the `get_figma_server_status` function to return a dictionary when called directly, enhancing usability.
- Modified WebSocket server to handle deprecated design command responses, ensuring backward compatibility with informative warnings.
- Adjusted unit tests to reflect changes in available tools, replacing `execute_command` with `execute_code` for consistency.
- Deleted end-to-end and integration test files for the Figma MCP server to streamline the test suite.
- Updated unit tests to reflect changes in function names and error handling for code execution.
- Ensured consistency in testing methodologies across the codebase by replacing `execute_command` with `execute_code` in relevant tests.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/lightfast_mcp/servers/figma/server.py (2)

44-49: ⚠️ Potential issue

Tools are still not registered – _register_tools() must be invoked
The FigmaMCPServer never calls its own _register_tools() method, so MCP clients will not see get_state or execute_code.
This was raised in a previous review and remains unresolved.

@@ class FigmaMCPServer(BaseServer):
     # Start WebSocket server immediately in a background task
     self._start_websocket_server_background()
 
     # Register signal handler for proper cleanup on process termination
     self._register_signal_handlers()
+
+        # Expose MCP tools
+        self._register_tools()          # <-- add this

161-174: 🛠️ Refactor suggestion

Event-loop cleanup after loop.stop() can dead-lock / raise
Cancelling tasks after the loop has already been stopped (and possibly closed) is unsafe and can raise RuntimeError: Event loop stopped before Future completed, exactly the issue flagged in the earlier review.
Move the cancellation logic before the loop.stop() call, or schedule it inside the running loop, then simply return in the finally block.

src/lightfast_mcp/servers/figma/websocket_server.py (1)

151-152: ⚠️ Potential issue

getattr(..., 'is_serving', 'unknown')() still triggers TypeError
Calling the fallback string 'unknown' as a function recreates the crash noted last time.

-logger.debug(
-    f"🔧 Server is_serving: {getattr(self.server, 'is_serving', 'unknown')()}"
-)
+if hasattr(self.server, "is_serving"):
+    logger.debug(f"🔧 Server is_serving: {self.server.is_serving()}")
+else:
+    logger.debug("🔧 Server is_serving attribute not available on this websockets version")
🧹 Nitpick comments (2)
src/lightfast_mcp/servers/figma/server.py (1)

161-165: loop may be undefined in finally
If an exception occurs before loop is assigned, the reference inside finally will raise UnboundLocalError.
Guard with loop = locals().get("loop") (or similar) before use.

src/lightfast_mcp/servers/figma/tools.py (1)

164-170: Allow caller to target a specific plugin
Currently the first connected client is arbitrarily chosen. Expose an optional plugin_id parameter to avoid non-deterministic behaviour in multi-plugin sessions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca35a47 and 469b807.

📒 Files selected for processing (5)
  • src/lightfast_mcp/servers/figma/server.py (1 hunks)
  • src/lightfast_mcp/servers/figma/tools.py (1 hunks)
  • src/lightfast_mcp/servers/figma/websocket_server.py (1 hunks)
  • tests/unit/test_figma_server.py (1 hunks)
  • tests/unit/test_modular_servers.py (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/test_modular_servers.py
  • tests/unit/test_figma_server.py
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Python 3.13
  • GitHub Check: Python 3.10
  • GitHub Check: Python 3.12
  • GitHub Check: Python 3.11
  • GitHub Check: Integration Tests
🔇 Additional comments (2)
src/lightfast_mcp/servers/figma/server.py (1)

225-263: Signal registration might run outside the main thread
Python raises ValueError if signal.signal() is called from a non-main thread.
Please ensure the server instance is always created in the main thread or gate this registration:

import threading, signal
if threading.current_thread() is threading.main_thread():
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGINT, signal_handler)
src/lightfast_mcp/servers/figma/websocket_server.py (1)

510-544:

❌ Incorrect review comment

Potential payload mismatch – code ends up nested under params
send_command_to_plugin() wraps the supplied params dict, so the Figma plugin will receive:

{ "type": "execute_code", "params": { "code": "..." } }

Yet _handle_execute_code (and typical plugin samples) expect "code" at the top level.
Confirm the plugin side deserialises params.code; otherwise, send the code directly:

success = await ws_server.send_command_to_plugin(
-    target_client.id, "execute_code", {"code": code_to_execute}
+    target_client.id, "execute_code", None  # code at top level
)

🏁 Script executed:

#!/bin/bash
# Locate the definition of send_command_to_plugin and inspect its implementation
rg -n "async def send_command_to_plugin" -A20 src/lightfast_mcp/servers/figma/websocket_server.py

Length of output: 887


🏁 Script executed:

#!/bin/bash
# Search for plugin-side handler definitions and usage of execute_code
rg -n "def _handle_execute_code" -A5
rg -n "execute_code" -A5

Length of output: 20116


🏁 Script executed:

#!/bin/bash
# Show the dispatch loop where handlers are applied in websocket_server.py around where handlers mapping is defined
rg -n "handlers = {" -A30 -B5 src/lightfast_mcp/servers/figma/websocket_server.py

Length of output: 171


No changes needed – nested params.code is already supported
The server uniformly wraps payloads under "params", and the Figma UI code explicitly handles message.params?.code (falling back from message.code) when forwarding to the plugin. The existing flow already works end-to-end:

  • Server’s send_command_to_plugin / broadcast_to_plugins always sends
    { type, params: { code }, … }
  • addons/figma/ui.html uses message.code || message.params?.code
  • Plugin script (addons/figma/code.ts) then receives code at top level

Changing to a bare top-level code field would break this consistency and isn’t necessary.

Likely an incorrect or invalid review comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants