diff --git a/packages/shared/src/flags.ts b/packages/shared/src/flags.ts
index 35899d7d87..d692da1fda 100644
--- a/packages/shared/src/flags.ts
+++ b/packages/shared/src/flags.ts
@@ -19,3 +19,5 @@ export const TASKS_PREWARM_SANDBOX_FLAG = "tasks-prewarm-sandbox";
export const GLM_MODEL_FLAG = "posthog-code-glm-model";
/** Spoken narration (agent speaks via the `speak` tool). Gated for a staged rollout. */
export const SPOKEN_NARRATION_FLAG = "posthog-code-spoken-narration";
+// Gates importing and relaying local MCP servers into cloud task runs.
+export const LOCAL_MCP_IMPORT_FLAG = "posthog-code-local-mcp-import";
diff --git a/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.test.tsx b/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.test.tsx
new file mode 100644
index 0000000000..db776d46bf
--- /dev/null
+++ b/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.test.tsx
@@ -0,0 +1,80 @@
+import type { LocalMcpCloudClassification } from "@posthog/core/local-mcp/localMcpImport";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { renderHook, waitFor } from "@testing-library/react";
+import type { ReactNode } from "react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+const mocks = vi.hoisted(() => ({
+ flagEnabled: false,
+ getCloudAvailability: vi.fn(),
+}));
+
+vi.mock("@posthog/di/react", () => ({
+ useServiceOptional: () => ({
+ getCloudAvailability: mocks.getCloudAvailability,
+ }),
+}));
+
+vi.mock("../feature-flags/useFeatureFlag", () => ({
+ useFeatureFlag: () => mocks.flagEnabled,
+}));
+
+import { useLocalMcpCloudServers } from "./useLocalMcpCloudServers";
+
+function wrapper({ children }: { children: ReactNode }) {
+ const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false } },
+ });
+ return (
+ {children}
+ );
+}
+
+const server: LocalMcpCloudClassification = {
+ name: "internal-tools",
+ availability: "requires_desktop",
+ reason: "stdio_transport",
+};
+
+describe("useLocalMcpCloudServers", () => {
+ beforeEach(() => {
+ mocks.flagEnabled = false;
+ mocks.getCloudAvailability.mockReset();
+ mocks.getCloudAvailability.mockResolvedValue([server]);
+ });
+
+ it("does not inspect local MCP servers while the feature flag is disabled", () => {
+ const { result } = renderHook(() => useLocalMcpCloudServers(true), {
+ wrapper,
+ });
+
+ expect(result.current).toEqual({ servers: [], isLoading: false });
+ expect(mocks.getCloudAvailability).not.toHaveBeenCalled();
+ });
+
+ it("returns local MCP servers while the feature flag is enabled", async () => {
+ mocks.flagEnabled = true;
+
+ const { result } = renderHook(() => useLocalMcpCloudServers(true), {
+ wrapper,
+ });
+
+ await waitFor(() =>
+ expect(result.current).toEqual({ servers: [server], isLoading: false }),
+ );
+ });
+
+ it("hides cached local MCP servers when the feature flag is disabled", async () => {
+ mocks.flagEnabled = true;
+ const { result, rerender } = renderHook(
+ () => useLocalMcpCloudServers(true),
+ { wrapper },
+ );
+ await waitFor(() => expect(result.current.servers).toEqual([server]));
+
+ mocks.flagEnabled = false;
+ rerender();
+
+ expect(result.current).toEqual({ servers: [], isLoading: false });
+ });
+});
diff --git a/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.ts b/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.ts
index 425ff33bbb..8ff7976ee6 100644
--- a/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.ts
+++ b/packages/ui/src/features/local-mcp/useLocalMcpCloudServers.ts
@@ -4,7 +4,9 @@ import type {
LocalMcpImportService,
} from "@posthog/core/local-mcp/localMcpImport";
import { useServiceOptional } from "@posthog/di/react";
+import { LOCAL_MCP_IMPORT_FLAG } from "@posthog/shared";
import { useQuery } from "@tanstack/react-query";
+import { useFeatureFlag } from "../feature-flags/useFeatureFlag";
// Stable identity so consumers' memo deps don't churn while data is absent.
const NO_SERVERS: LocalMcpCloudClassification[] = [];
@@ -19,7 +21,7 @@ export interface LocalMcpCloudServersResult {
/**
* The user's local (~/.claude.json) MCP servers classified by cloud
* availability. Empty on hosts without a local workspace (web/mobile — the
- * service is only bound on desktop).
+ * service is only bound on desktop) and while the feature flag is off.
*/
export function useLocalMcpCloudServers(
enabled: boolean,
@@ -27,13 +29,18 @@ export function useLocalMcpCloudServers(
const service = useServiceOptional(
LOCAL_MCP_IMPORT_SERVICE,
);
+ const flagEnabled = useFeatureFlag(LOCAL_MCP_IMPORT_FLAG);
+ const queryEnabled = enabled && flagEnabled && !!service;
const query = useQuery({
queryKey: ["local-mcp-cloud-availability"],
queryFn: () => (service ? service.getCloudAvailability() : NO_SERVERS),
- enabled: enabled && !!service,
+ enabled: queryEnabled,
staleTime: 30_000,
});
- return { servers: query.data ?? NO_SERVERS, isLoading: query.isLoading };
+ return {
+ servers: queryEnabled ? (query.data ?? NO_SERVERS) : NO_SERVERS,
+ isLoading: queryEnabled && query.isLoading,
+ };
}