From 8f9dbe1f6c825ab44c610aeafa7bba30de972bd4 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Tue, 21 Jul 2026 14:37:38 -0700 Subject: [PATCH 1/3] Split devtools client into headless core and React packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split @player-devtools/client into two packages so headless consumers (the MCP server) never load React: - core → @player-devtools/client: headless createExtensionClient + state reducer. No React deps. - react → @player-devtools/client-react: the React Panel + hooks. Consumers repointed: flipper-plugin uses client-react; MCP uses the headless client core, decoupling it from the UI barrel. Also: - Fix MCP transport for Node < 22: provide a `ws` WebSocket to reconnecting-websocket via a guarded globalThis polyfill (it has no global WebSocket, so npx-run MCP failed to connect). ws deps live in the BUILD only, per repo convention. - Keep INITIAL_FLOW (a UI placeholder) in the Panel, not exported from core; move @player-ui/player to core test_deps since only the reducer test uses it now. --- devtools/client/README.md | 15 +++++---- devtools/client/core/BUILD | 23 +++++++++++++ devtools/client/{ => core}/package.json | 0 devtools/client/core/src/constants/index.ts | 9 ++++++ devtools/client/{ => core}/src/index.ts | 1 - .../src/state/__tests__/reducer.test.ts | 26 ++++++++++++++- .../client/{ => core}/src/state/client.ts | 0 .../client/{ => core}/src/state/reducer.ts | 0 devtools/client/{ => react}/BUILD | 5 +-- devtools/client/react/package.json | 11 +++++++ .../src/helpers/__tests__/flowDiff.test.ts | 0 .../{ => react}/src/helpers/flowDiff.ts | 0 devtools/client/react/src/index.ts | 1 + .../client/{ => react}/src/panel/index.tsx | 24 +++++++++++++- .../client/{ => react}/src/plugins/index.ts | 0 .../client/{ => react}/src/state/index.ts | 2 +- devtools/client/src/constants/index.ts | 32 ------------------- devtools/flipper-plugin/BUILD | 2 +- devtools/flipper-plugin/package.json | 2 +- devtools/flipper-plugin/src/index.tsx | 2 +- devtools/mcp/BUILD | 2 ++ .../invokeAction.integration.test.ts | 4 +-- devtools/mcp/src/transport.ts | 7 ++++ pnpm-lock.yaml | 29 +++++++++++++---- pnpm-workspace.yaml | 3 +- 25 files changed, 142 insertions(+), 58 deletions(-) create mode 100644 devtools/client/core/BUILD rename devtools/client/{ => core}/package.json (100%) create mode 100644 devtools/client/core/src/constants/index.ts rename devtools/client/{ => core}/src/index.ts (70%) rename devtools/client/{ => core}/src/state/__tests__/reducer.test.ts (94%) rename devtools/client/{ => core}/src/state/client.ts (100%) rename devtools/client/{ => core}/src/state/reducer.ts (100%) rename devtools/client/{ => react}/BUILD (87%) create mode 100644 devtools/client/react/package.json rename devtools/client/{ => react}/src/helpers/__tests__/flowDiff.test.ts (100%) rename devtools/client/{ => react}/src/helpers/flowDiff.ts (100%) create mode 100644 devtools/client/react/src/index.ts rename devtools/client/{ => react}/src/panel/index.tsx (94%) rename devtools/client/{ => react}/src/plugins/index.ts (100%) rename devtools/client/{ => react}/src/state/index.ts (93%) delete mode 100644 devtools/client/src/constants/index.ts diff --git a/devtools/client/README.md b/devtools/client/README.md index 7202336..d2a7070 100644 --- a/devtools/client/README.md +++ b/devtools/client/README.md @@ -1,19 +1,22 @@ -# @player-devtools/client +# Player UI Devtools client -The `@player-devtools/client` exposes the Panel with the ReactPlayer, which is responsible for running content sent by Player devtool plugins on the inspected Player UI instance. +The devtools client is split into two packages so headless consumers never load React: -The `Panel` is the shared devtools UI surface, hosted by each client: the [browser extension](https://github.com/player-ui/browser-devtools) for web and the [Flipper plugin](../flipper-plugin) for mobile. The agent-facing [MCP server](../mcp) consumes the same Player devtools instrumentation without rendering the `Panel`. +- [`core/`](./core) — `@player-devtools/client`: the headless client (`createExtensionClient`, state reducer). No React. Consumed by the agent-facing [MCP server](../mcp). +- [`react/`](./react) — `@player-devtools/client-react`: the React `Panel` with the ReactPlayer, responsible for rendering content sent by Player devtool plugins on the inspected Player UI instance. + +The `Panel` is the shared devtools UI surface, hosted by each client: the [browser extension](https://github.com/player-ui/browser-devtools) for web and the [Flipper plugin](../flipper-plugin) for mobile. The agent-facing [MCP server](../mcp) consumes the same Player devtools instrumentation via the headless `@player-devtools/client` core, without rendering the `Panel`. ## Installation The Devtools client is available as an npm package. You can install it using npm or yarn: ```bash -npm install @player-devtools/client +npm install @player-devtools/client-react ``` ```bash -yarn add @player-devtools/client +yarn add @player-devtools/client-react ``` ## Overview @@ -29,7 +32,7 @@ For a more comprehensive understanding of the architecture of the Devtools clien The Devtools client is a React component that receives content from devtools plugins running in the Player UI used by the inspected page. It can be used in your React application like any other React component. ```jsx -import { Panel } from "@player-devtools/client"; +import { Panel } from "@player-devtools/client-react"; import type { MessengerOptions } from "@player-devtools/messenger"; import browser from "webextension-polyfill"; diff --git a/devtools/client/core/BUILD b/devtools/client/core/BUILD new file mode 100644 index 0000000..2e785c7 --- /dev/null +++ b/devtools/client/core/BUILD @@ -0,0 +1,23 @@ +load("@npm//:defs.bzl", "npm_link_all_packages") +load("@rules_player//javascript:defs.bzl", "js_pipeline") +load("//helpers:defs.bzl", "tsup_config", "vitest_config") + +npm_link_all_packages(name = "node_modules") + +tsup_config(name = "tsup_config") + +vitest_config(name = "vitest_config") + +js_pipeline( + package_name = "@player-devtools/client", + deps = [ + ":node_modules/@player-devtools/messenger", + ":node_modules/@player-devtools/types", + ":node_modules/@player-devtools/utils", + "//:node_modules/immer", + "//:node_modules/dequal", + ], + test_deps = [ + "//:node_modules/@player-ui/player", + ], +) diff --git a/devtools/client/package.json b/devtools/client/core/package.json similarity index 100% rename from devtools/client/package.json rename to devtools/client/core/package.json diff --git a/devtools/client/core/src/constants/index.ts b/devtools/client/core/src/constants/index.ts new file mode 100644 index 0000000..7e1a5f6 --- /dev/null +++ b/devtools/client/core/src/constants/index.ts @@ -0,0 +1,9 @@ +import type { ExtensionState } from "@player-devtools/types"; + +export const INITIAL_EXTENSION_STATE: ExtensionState = { + current: { + player: null, + plugin: null, + }, + players: {}, +}; diff --git a/devtools/client/src/index.ts b/devtools/client/core/src/index.ts similarity index 70% rename from devtools/client/src/index.ts rename to devtools/client/core/src/index.ts index 531c6a1..175aa31 100644 --- a/devtools/client/src/index.ts +++ b/devtools/client/core/src/index.ts @@ -1,2 +1 @@ -export { Panel } from "./panel"; export { createExtensionClient, type ExtensionClient } from "./state/client"; diff --git a/devtools/client/src/state/__tests__/reducer.test.ts b/devtools/client/core/src/state/__tests__/reducer.test.ts similarity index 94% rename from devtools/client/src/state/__tests__/reducer.test.ts rename to devtools/client/core/src/state/__tests__/reducer.test.ts index 9fafd1c..fc62891 100644 --- a/devtools/client/src/state/__tests__/reducer.test.ts +++ b/devtools/client/core/src/state/__tests__/reducer.test.ts @@ -9,8 +9,32 @@ import type { ExtensionSelectedPlayerEvent, ExtensionSelectedPluginEvent, } from "@player-devtools/types"; +import type { Flow } from "@player-ui/player"; import { reducer } from "../reducer"; -import { INITIAL_EXTENSION_STATE, INITIAL_FLOW } from "../../constants"; +import { INITIAL_EXTENSION_STATE } from "../../constants"; + +/** Minimal flow fixture used to exercise the reducer's flow handling. */ +const INITIAL_FLOW: Flow = { + id: "initial-flow", + views: [ + { + id: "view-1", + type: "text", + value: "connecting...", + }, + ], + navigation: { + BEGIN: "FLOW_1", + FLOW_1: { + startState: "VIEW_1", + VIEW_1: { + state_type: "VIEW", + ref: "view-1", + transitions: {}, + }, + }, + }, +}; const mockPlayerInitTransaction: Transaction = { id: 1, diff --git a/devtools/client/src/state/client.ts b/devtools/client/core/src/state/client.ts similarity index 100% rename from devtools/client/src/state/client.ts rename to devtools/client/core/src/state/client.ts diff --git a/devtools/client/src/state/reducer.ts b/devtools/client/core/src/state/reducer.ts similarity index 100% rename from devtools/client/src/state/reducer.ts rename to devtools/client/core/src/state/reducer.ts diff --git a/devtools/client/BUILD b/devtools/client/react/BUILD similarity index 87% rename from devtools/client/BUILD rename to devtools/client/react/BUILD index 9bc0e08..4923a3b 100644 --- a/devtools/client/BUILD +++ b/devtools/client/react/BUILD @@ -9,8 +9,9 @@ tsup_config(name = "tsup_config") vitest_config(name = "vitest_config") js_pipeline( - package_name = "@player-devtools/client", + package_name = "@player-devtools/client-react", deps = [ + ":node_modules/@player-devtools/client", ":node_modules/@player-devtools/messenger", ":node_modules/@player-devtools/types", ":node_modules/@player-devtools/utils", @@ -27,6 +28,6 @@ js_pipeline( "//:node_modules/react", "//:node_modules/react-error-boundary", "//:node_modules/dequal", - "//:node_modules/@devtools-ds/themes" + "//:node_modules/@devtools-ds/themes", ], ) diff --git a/devtools/client/react/package.json b/devtools/client/react/package.json new file mode 100644 index 0000000..99b49e5 --- /dev/null +++ b/devtools/client/react/package.json @@ -0,0 +1,11 @@ +{ + "name": "@player-devtools/client-react", + "version": "0.0.0-PLACEHOLDER", + "main": "src/index.ts", + "dependencies": { + "@player-devtools/client": "workspace:*", + "@player-devtools/messenger": "workspace:*", + "@player-devtools/types": "workspace:*", + "@player-devtools/utils": "workspace:*" + } +} diff --git a/devtools/client/src/helpers/__tests__/flowDiff.test.ts b/devtools/client/react/src/helpers/__tests__/flowDiff.test.ts similarity index 100% rename from devtools/client/src/helpers/__tests__/flowDiff.test.ts rename to devtools/client/react/src/helpers/__tests__/flowDiff.test.ts diff --git a/devtools/client/src/helpers/flowDiff.ts b/devtools/client/react/src/helpers/flowDiff.ts similarity index 100% rename from devtools/client/src/helpers/flowDiff.ts rename to devtools/client/react/src/helpers/flowDiff.ts diff --git a/devtools/client/react/src/index.ts b/devtools/client/react/src/index.ts new file mode 100644 index 0000000..8628fc5 --- /dev/null +++ b/devtools/client/react/src/index.ts @@ -0,0 +1 @@ +export { Panel } from "./panel"; diff --git a/devtools/client/src/panel/index.tsx b/devtools/client/react/src/panel/index.tsx similarity index 94% rename from devtools/client/src/panel/index.tsx rename to devtools/client/react/src/panel/index.tsx index 1e62ed8..5a46bdc 100644 --- a/devtools/client/src/panel/index.tsx +++ b/devtools/client/react/src/panel/index.tsx @@ -24,11 +24,33 @@ import { import { ThemeProvider, useDarkMode } from "@devtools-ds/themes"; -import { INITIAL_FLOW } from "../constants"; import { PLAYER_PLUGINS, PUBSUB_PLUGIN } from "../plugins"; import { useExtensionState } from "../state"; import { flowDiff } from "../helpers/flowDiff"; +/** Placeholder flow rendered until a real Player flow arrives. */ +const INITIAL_FLOW: Flow = { + id: "initial-flow", + views: [ + { + id: "view-1", + type: "text", + value: "connecting...", + }, + ], + navigation: { + BEGIN: "FLOW_1", + FLOW_1: { + startState: "VIEW_1", + VIEW_1: { + state_type: "VIEW", + ref: "view-1", + transitions: {}, + }, + }, + }, +}; + const fallbackRender: ErrorBoundary["props"]["fallbackRender"] = ({ error, }) => { diff --git a/devtools/client/src/plugins/index.ts b/devtools/client/react/src/plugins/index.ts similarity index 100% rename from devtools/client/src/plugins/index.ts rename to devtools/client/react/src/plugins/index.ts diff --git a/devtools/client/src/state/index.ts b/devtools/client/react/src/state/index.ts similarity index 93% rename from devtools/client/src/state/index.ts rename to devtools/client/react/src/state/index.ts index b2bcb00..ad40fb2 100644 --- a/devtools/client/src/state/index.ts +++ b/devtools/client/react/src/state/index.ts @@ -1,7 +1,7 @@ import type { CommunicationLayerMethods } from "@player-devtools/types"; import { useEffect, useMemo, useSyncExternalStore } from "react"; -import { createExtensionClient } from "./client"; +import { createExtensionClient } from "@player-devtools/client"; /** * Thin React adapter over `createExtensionClient`. diff --git a/devtools/client/src/constants/index.ts b/devtools/client/src/constants/index.ts deleted file mode 100644 index 02c6a7b..0000000 --- a/devtools/client/src/constants/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { ExtensionState } from "@player-devtools/types"; -import type { Flow } from "@player-ui/player"; - -export const INITIAL_FLOW: Flow = { - id: "initial-flow", - views: [ - { - id: "view-1", - type: "text", - value: "connecting...", - }, - ], - navigation: { - BEGIN: "FLOW_1", - FLOW_1: { - startState: "VIEW_1", - VIEW_1: { - state_type: "VIEW", - ref: "view-1", - transitions: {}, - }, - }, - }, -}; - -export const INITIAL_EXTENSION_STATE: ExtensionState = { - current: { - player: null, - plugin: null, - }, - players: {}, -}; diff --git a/devtools/flipper-plugin/BUILD b/devtools/flipper-plugin/BUILD index a6d1957..82dc5c1 100644 --- a/devtools/flipper-plugin/BUILD +++ b/devtools/flipper-plugin/BUILD @@ -11,7 +11,7 @@ npm_link_all_packages(name = "node_modules") vitest_config(name = "vitest_config") deps = [ - ":node_modules/@player-devtools/client", + ":node_modules/@player-devtools/client-react", ":node_modules/@player-devtools/types", "//:node_modules/dlv", diff --git a/devtools/flipper-plugin/package.json b/devtools/flipper-plugin/package.json index f86f1fc..40205d9 100644 --- a/devtools/flipper-plugin/package.json +++ b/devtools/flipper-plugin/package.json @@ -6,7 +6,7 @@ "version": "0.0.0-PLACEHOLDER", "main": "dist/index.js", "dependencies": { - "@player-devtools/client": "workspace:*", + "@player-devtools/client-react": "workspace:*", "@player-devtools/types": "workspace:*" }, "flipperBundlerEntry": "src/index.tsx", diff --git a/devtools/flipper-plugin/src/index.tsx b/devtools/flipper-plugin/src/index.tsx index f8ea705..1ea454d 100644 --- a/devtools/flipper-plugin/src/index.tsx +++ b/devtools/flipper-plugin/src/index.tsx @@ -11,7 +11,7 @@ import type { MessengerEvent, TransactionMetadata, } from "@player-devtools/types"; -import { Panel } from "@player-devtools/client"; +import { Panel } from "@player-devtools/client-react"; import { Button, ChakraProvider, diff --git a/devtools/mcp/BUILD b/devtools/mcp/BUILD index 98e2cb5..2aaad94 100644 --- a/devtools/mcp/BUILD +++ b/devtools/mcp/BUILD @@ -15,8 +15,10 @@ deps = [ ":node_modules/@player-devtools/messenger", ":node_modules/@player-devtools/types", "//:node_modules/@modelcontextprotocol/sdk", + "//:node_modules/@types/ws", "//:node_modules/flipper-server", "//:node_modules/flipper-server-client", + "//:node_modules/ws", "//:node_modules/zod", ] diff --git a/devtools/mcp/src/tools/__tests__/invokeAction.integration.test.ts b/devtools/mcp/src/tools/__tests__/invokeAction.integration.test.ts index 97e242e..76ead21 100644 --- a/devtools/mcp/src/tools/__tests__/invokeAction.integration.test.ts +++ b/devtools/mcp/src/tools/__tests__/invokeAction.integration.test.ts @@ -10,9 +10,7 @@ import type { } from "@player-devtools/types"; // NOTE: this test drives the devtools/MCP side with a real Messenger rather -// than `createExtensionClient`, because that factory currently lives in the -// `@player-devtools/client` barrel alongside the React `Panel`, which can't be -// imported in this node test env (it pulls in UI-only CJS deps). The MCP +// than `createExtensionClient` because it exercises the bus directly — the MCP // handler's playerId→target resolution is unit-tested in select.test.ts; what // THIS test proves is the other half: real DevtoolsPlugins on a shared bus only // handle actions addressed to their own playerID. The frame built below is diff --git a/devtools/mcp/src/transport.ts b/devtools/mcp/src/transport.ts index ed43f2d..08c73a2 100644 --- a/devtools/mcp/src/transport.ts +++ b/devtools/mcp/src/transport.ts @@ -8,6 +8,7 @@ import * as net from "net"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; +import { WebSocket as WsWebSocket } from "ws"; import type { CommunicationLayerMethods, ExtensionSupportedEvents, @@ -15,6 +16,12 @@ import type { TransactionMetadata, } from "@player-devtools/types"; +// polyfill WebSocket for Node < 22 +if (typeof (globalThis as { WebSocket?: unknown }).WebSocket === "undefined") { + (globalThis as { WebSocket?: unknown }).WebSocket = + WsWebSocket as unknown as typeof WebSocket; +} + type MessageCallback = ( message: TransactionMetadata & MessengerEvent, ) => void; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2665546..89f5d64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -392,23 +392,38 @@ importers: specifier: ^3.0.0 version: 3.25.76 - devtools/client: + devtools/client/core: dependencies: '@player-devtools/messenger': specifier: workspace:* - version: link:../messenger/core + version: link:../../messenger/core '@player-devtools/types': specifier: workspace:* - version: link:../types/core + version: link:../../types/core '@player-devtools/utils': specifier: workspace:* - version: link:../utils/core + version: link:../../utils/core - devtools/flipper-plugin: + devtools/client/react: dependencies: '@player-devtools/client': specifier: workspace:* - version: link:../client + version: link:../core + '@player-devtools/messenger': + specifier: workspace:* + version: link:../../messenger/core + '@player-devtools/types': + specifier: workspace:* + version: link:../../types/core + '@player-devtools/utils': + specifier: workspace:* + version: link:../../utils/core + + devtools/flipper-plugin: + dependencies: + '@player-devtools/client-react': + specifier: workspace:* + version: link:../client/react '@player-devtools/types': specifier: workspace:* version: link:../types/core @@ -417,7 +432,7 @@ importers: dependencies: '@player-devtools/client': specifier: workspace:* - version: link:../client + version: link:../client/core '@player-devtools/messenger': specifier: workspace:* version: link:../messenger/core diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 2f082e9..072c3a4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,7 +1,8 @@ packages: - cli - docs/* - - devtools/client + - devtools/client/core + - devtools/client/react - devtools/flipper-plugin - devtools/mcp - devtools/plugin/core From e2e0aee2970103d48d4e57e285b4cc2c798516d5 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Tue, 21 Jul 2026 14:58:16 -0700 Subject: [PATCH 2/3] Extract Flipper transport into @player-devtools/client-flipper Move FlipperServerTransport out of the MCP module so it can be reused by other client contexts (e.g. an Electron app): - New package @player-devtools/client-flipper (devtools/client/flipper) holds FlipperServerTransport (moved from devtools/mcp/src/transport.ts), including the Node <22 `ws` WebSocket polyfill. - The Transport interface (CommunicationLayerMethods + connect/close) moves to @player-devtools/types as the shared contract. MCPServer now depends only on that interface, not on the concrete transport. - client-flipper is imported solely by devtools/mcp/bin/run, which composes it with MCPServer. Nothing in mcp/src references it. - mcp barrel exports only MCPServer; mcp sheds flipper-server, flipper-server-client, ws, and @types/ws (now client-flipper's). --- devtools/client/flipper/BUILD | 20 +++++++++++++++++++ devtools/client/flipper/package.json | 8 ++++++++ devtools/client/flipper/src/index.ts | 1 + .../{mcp => client/flipper}/src/transport.ts | 9 +-------- devtools/mcp/BUILD | 5 +---- devtools/mcp/bin/run | 5 ++++- devtools/mcp/package.json | 1 + devtools/mcp/src/index.ts | 1 - devtools/mcp/src/server.ts | 2 +- devtools/types/core/src/index.ts | 12 +++++++++++ pnpm-lock.yaml | 9 +++++++++ pnpm-workspace.yaml | 1 + 12 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 devtools/client/flipper/BUILD create mode 100644 devtools/client/flipper/package.json create mode 100644 devtools/client/flipper/src/index.ts rename devtools/{mcp => client/flipper}/src/transport.ts (97%) diff --git a/devtools/client/flipper/BUILD b/devtools/client/flipper/BUILD new file mode 100644 index 0000000..c71f7eb --- /dev/null +++ b/devtools/client/flipper/BUILD @@ -0,0 +1,20 @@ +load("@npm//:defs.bzl", "npm_link_all_packages") +load("@rules_player//javascript:defs.bzl", "js_pipeline") +load("//helpers:defs.bzl", "tsup_config", "vitest_config") + +npm_link_all_packages(name = "node_modules") + +tsup_config(name = "tsup_config") + +vitest_config(name = "vitest_config") + +js_pipeline( + package_name = "@player-devtools/client-flipper", + deps = [ + ":node_modules/@player-devtools/types", + "//:node_modules/@types/ws", + "//:node_modules/flipper-server", + "//:node_modules/flipper-server-client", + "//:node_modules/ws", + ], +) diff --git a/devtools/client/flipper/package.json b/devtools/client/flipper/package.json new file mode 100644 index 0000000..3d08f33 --- /dev/null +++ b/devtools/client/flipper/package.json @@ -0,0 +1,8 @@ +{ + "name": "@player-devtools/client-flipper", + "version": "0.0.0-PLACEHOLDER", + "main": "src/index.ts", + "dependencies": { + "@player-devtools/types": "workspace:*" + } +} diff --git a/devtools/client/flipper/src/index.ts b/devtools/client/flipper/src/index.ts new file mode 100644 index 0000000..e829866 --- /dev/null +++ b/devtools/client/flipper/src/index.ts @@ -0,0 +1 @@ +export { FlipperServerTransport } from "./transport"; diff --git a/devtools/mcp/src/transport.ts b/devtools/client/flipper/src/transport.ts similarity index 97% rename from devtools/mcp/src/transport.ts rename to devtools/client/flipper/src/transport.ts index 08c73a2..59e3e4f 100644 --- a/devtools/mcp/src/transport.ts +++ b/devtools/client/flipper/src/transport.ts @@ -14,6 +14,7 @@ import type { ExtensionSupportedEvents, MessengerEvent, TransactionMetadata, + Transport, } from "@player-devtools/types"; // polyfill WebSocket for Node < 22 @@ -38,14 +39,6 @@ type FlipperExecuteMessage = { }; }; -/** Transport interface — implemented by each connection adapter */ -export interface Transport extends CommunicationLayerMethods { - /** Connect to the underlying transport */ - connect(): Promise; - /** Tear down the underlying transport */ - close(): Promise; -} - /** * Flipper headless transport * diff --git a/devtools/mcp/BUILD b/devtools/mcp/BUILD index 2aaad94..ff22e07 100644 --- a/devtools/mcp/BUILD +++ b/devtools/mcp/BUILD @@ -12,13 +12,10 @@ vitest_config(name = "vitest_config") deps = [ ":node_modules/@player-devtools/client", + ":node_modules/@player-devtools/client-flipper", ":node_modules/@player-devtools/messenger", ":node_modules/@player-devtools/types", "//:node_modules/@modelcontextprotocol/sdk", - "//:node_modules/@types/ws", - "//:node_modules/flipper-server", - "//:node_modules/flipper-server-client", - "//:node_modules/ws", "//:node_modules/zod", ] diff --git a/devtools/mcp/bin/run b/devtools/mcp/bin/run index cf74dd5..c36e51a 100644 --- a/devtools/mcp/bin/run +++ b/devtools/mcp/bin/run @@ -1,6 +1,9 @@ #!/usr/bin/env node -const { FlipperServerTransport, MCPServer } = require("@player-devtools/mcp"); +const { MCPServer } = require("@player-devtools/mcp"); +const { + FlipperServerTransport, +} = require("@player-devtools/client-flipper"); const transport = new FlipperServerTransport(); const server = new MCPServer(transport); diff --git a/devtools/mcp/package.json b/devtools/mcp/package.json index 8961d11..5fdf729 100644 --- a/devtools/mcp/package.json +++ b/devtools/mcp/package.json @@ -11,6 +11,7 @@ ], "dependencies": { "@player-devtools/client": "workspace:*", + "@player-devtools/client-flipper": "workspace:*", "@player-devtools/messenger": "workspace:*", "@player-devtools/types": "workspace:*" }, diff --git a/devtools/mcp/src/index.ts b/devtools/mcp/src/index.ts index 66684f8..34693d3 100644 --- a/devtools/mcp/src/index.ts +++ b/devtools/mcp/src/index.ts @@ -1,2 +1 @@ export { MCPServer } from "./server"; -export { type Transport, FlipperServerTransport } from "./transport"; diff --git a/devtools/mcp/src/server.ts b/devtools/mcp/src/server.ts index 05308f1..1782183 100644 --- a/devtools/mcp/src/server.ts +++ b/devtools/mcp/src/server.ts @@ -2,11 +2,11 @@ import { createExtensionClient, type ExtensionClient, } from "@player-devtools/client"; +import type { Transport } from "@player-devtools/types"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import type { Transport } from "./transport"; import { TOOL_DEFS, type ToolDef } from "./tools"; export class MCPServer { diff --git a/devtools/types/core/src/index.ts b/devtools/types/core/src/index.ts index 7c28777..1f1fed5 100644 --- a/devtools/types/core/src/index.ts +++ b/devtools/types/core/src/index.ts @@ -249,6 +249,18 @@ export type CommunicationLayerMethods = Pick< "sendMessage" | "addListener" | "removeListener" >; +/** + * A communication layer with a managed lifecycle — implemented by each + * connection adapter (e.g. the Flipper transport) and consumed by clients + * that need to connect/tear down the underlying transport. + */ +export interface Transport extends CommunicationLayerMethods { + /** Connect to the underlying transport */ + connect(): Promise; + /** Tear down the underlying transport */ + close(): Promise; +} + /** Interface representing the Devtools Plugins Store. */ export interface DevtoolsPluginsStore { /** Plugins data. */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 89f5d64..66875b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -404,6 +404,12 @@ importers: specifier: workspace:* version: link:../../utils/core + devtools/client/flipper: + dependencies: + '@player-devtools/types': + specifier: workspace:* + version: link:../../types/core + devtools/client/react: dependencies: '@player-devtools/client': @@ -433,6 +439,9 @@ importers: '@player-devtools/client': specifier: workspace:* version: link:../client/core + '@player-devtools/client-flipper': + specifier: workspace:* + version: link:../client/flipper '@player-devtools/messenger': specifier: workspace:* version: link:../messenger/core diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 072c3a4..afeeead 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ packages: - cli - docs/* - devtools/client/core + - devtools/client/flipper - devtools/client/react - devtools/flipper-plugin - devtools/mcp From 6eab1bd1235c38c91ce401a6fb0db1cc33412c97 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Tue, 21 Jul 2026 16:01:48 -0700 Subject: [PATCH 3/3] ci: bump base executor to xlarge and unpin bazel-docker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Canary pipeline #166 failed with the Bazel server killed mid-build (error code 14, 'Socket closed') — memory pressure on the runner while building the Android/toolchain targets. Bump the base executor from large to xlarge to give Bazel more headroom, and drop the pinned bazel-docker:9 tag to track the latest image. --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2de3de1..665aa67 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,9 +15,9 @@ orbs: executors: base: docker: - - image: docker.io/playerui/bazel-docker:9 + - image: docker.io/playerui/bazel-docker working_directory: ~/devtools - resource_class: large + resource_class: xlarge environment: TZ: "/usr/share/zoneinfo/America/Los_Angeles" minimal: