Skip to content

Commit ced941c

Browse files
committed
fix(mcp): make the SSE push subscription intrinsic to the tools query
The 5min stale time assumed push, but useMcpToolsEvents was only mounted in the settings page and the tool picker — other consumers of the tools query (dynamic args, tool selector, canvas block via useMcpToolsQuery/useMcpTools) had no subscription, so list_changed updates could stay invisible for the full window. Mount useMcpToolsEvents inside useMcpToolsQuery so every tools consumer gets real-time push from the shared, reference-counted connection — no consumer is left re-probing. Removed the now-redundant explicit mounts from the settings page and tool-input.
1 parent 0b0658c commit ced941c

4 files changed

Lines changed: 17 additions & 6 deletions

File tree

  • apps/sim

apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import {
3939
useDeleteMcpServer,
4040
useForceRefreshMcpTools,
4141
useMcpServers,
42-
useMcpToolsEvents,
4342
useMcpToolsQuery,
4443
useRefreshMcpServer,
4544
useStoredMcpTools,
@@ -196,9 +195,6 @@ export function MCP() {
196195
} = useMcpServers(workspaceId)
197196
const { data: mcpToolsData = [], toolsStateByServer } = useMcpToolsQuery(workspaceId)
198197
const { data: storedTools = [], refetch: refetchStoredTools } = useStoredMcpTools(workspaceId)
199-
// Real-time refresh via the shared list_changed → SSE push (same subscription the workflow
200-
// editor uses), so tool changes reflect here without re-probing on every visit.
201-
useMcpToolsEvents(workspaceId)
202198
const forceRefreshToolsMutation = useForceRefreshMcpTools()
203199
const forceRefreshTools = forceRefreshToolsMutation.mutate
204200
const createServerMutation = useCreateMcpServer()

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ import {
7979
useCreateMcpServer,
8080
useForceRefreshMcpTools,
8181
useMcpServers,
82-
useMcpToolsEvents,
8382
useStoredMcpTools,
8483
} from '@/hooks/queries/mcp'
8584
import { useWorkflowState, useWorkflows } from '@/hooks/queries/workflows'
@@ -570,7 +569,6 @@ export const ToolInput = memo(function ToolInput({
570569
const { data: mcpServers = [], isLoading: mcpServersLoading } = useMcpServers(workspaceId)
571570
const { data: storedMcpTools = [] } = useStoredMcpTools(workspaceId)
572571
const forceRefreshMcpTools = useForceRefreshMcpTools().mutate
573-
useMcpToolsEvents(workspaceId)
574572
const { navigateToSettings } = useSettingsNavigation()
575573
const createMcpServer = useCreateMcpServer()
576574
const { startOauthForServer } = useMcpOauthPopup({ workspaceId })

apps/sim/hooks/queries/mcp.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,25 @@ function mockServers(servers: McpServer[]) {
103103
})
104104
}
105105

106+
// jsdom has no EventSource; useMcpToolsQuery mounts the shared SSE subscription.
107+
class FakeEventSource {
108+
onopen: (() => void) | null = null
109+
onerror: (() => void) | null = null
110+
constructor(public url: string) {}
111+
addEventListener(): void {}
112+
close(): void {}
113+
}
114+
106115
describe('useMcpToolsQuery', () => {
107116
beforeEach(() => {
108117
vi.clearAllMocks()
118+
;(globalThis as unknown as { EventSource: unknown }).EventSource = FakeEventSource
109119
})
110120

111121
afterEach(() => {
112122
vi.restoreAllMocks()
123+
;(globalThis as unknown as Record<string, unknown>).__mcp_sse_connections = undefined
124+
;(globalThis as unknown as Record<string, unknown>).__mcp_sse_subscribed = undefined
113125
})
114126

115127
it('does not auto-discover disconnected or errored OAuth servers', async () => {

apps/sim/hooks/queries/mcp.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ function isServerEligibleForDiscovery(server: McpServer, workspaceId: string): b
146146
export function useMcpToolsQuery(workspaceId: string) {
147147
const queryClient = useQueryClient()
148148
const { data: servers, isLoading: serversLoading } = useMcpServers(workspaceId)
149+
// Push is intrinsic to consuming the tools query: every surface that reads tools (settings,
150+
// tool picker, dynamic args, tool selector, canvas block) gets real-time `list_changed`
151+
// refresh via the shared, reference-counted subscription — so the 5-min stale time is always
152+
// push-backed and no consumer is left re-probing.
153+
useMcpToolsEvents(workspaceId)
149154

150155
/**
151156
* Skip disabled rows, rows retained from a previous workspace, and OAuth rows

0 commit comments

Comments
 (0)