d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
426 lines
12 KiB
TypeScript
426 lines
12 KiB
TypeScript
import { createLogger } from '@sim/logger'
|
|
import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
import { ApiClientError } from '@/lib/api/client/errors'
|
|
import { requestJson } from '@/lib/api/client/request'
|
|
import {
|
|
createWorkflowMcpServerContract,
|
|
createWorkflowMcpToolContract,
|
|
type DeployedWorkflow,
|
|
deleteWorkflowMcpServerContract,
|
|
deleteWorkflowMcpToolContract,
|
|
getWorkflowMcpServerContract,
|
|
listWorkflowMcpDeployedWorkflowsContract,
|
|
listWorkflowMcpServersContract,
|
|
listWorkflowMcpToolsContract,
|
|
updateWorkflowMcpServerContract,
|
|
updateWorkflowMcpToolContract,
|
|
type WorkflowMcpServer,
|
|
type WorkflowMcpTool,
|
|
} from '@/lib/api/contracts/workflow-mcp-servers'
|
|
|
|
const logger = createLogger('WorkflowMcpServerQueries')
|
|
|
|
export type { DeployedWorkflow }
|
|
|
|
/**
|
|
* Query key factories for Workflow MCP Server queries
|
|
*/
|
|
export const workflowMcpServerKeys = {
|
|
all: ['workflow-mcp-servers'] as const,
|
|
servers: (workspaceId: string) => [...workflowMcpServerKeys.all, 'servers', workspaceId] as const,
|
|
server: (workspaceId: string, serverId: string) =>
|
|
[...workflowMcpServerKeys.servers(workspaceId), serverId] as const,
|
|
tools: (workspaceId: string, serverId: string) =>
|
|
[...workflowMcpServerKeys.server(workspaceId, serverId), 'tools'] as const,
|
|
deployedWorkflows: (workspaceId: string) =>
|
|
[...workflowMcpServerKeys.all, 'deployed-workflows', workspaceId] as const,
|
|
}
|
|
|
|
export type { WorkflowMcpServer, WorkflowMcpTool }
|
|
|
|
export const WORKFLOW_MCP_SERVERS_LIST_STALE_TIME = 60 * 1000
|
|
export const WORKFLOW_MCP_SERVER_DETAIL_STALE_TIME = 30 * 1000
|
|
export const WORKFLOW_MCP_TOOLS_STALE_TIME = 30 * 1000
|
|
export const WORKFLOW_MCP_DEPLOYED_WORKFLOWS_STALE_TIME = 30 * 1000
|
|
|
|
/**
|
|
* Fetch workflow MCP servers for a workspace
|
|
*/
|
|
async function fetchWorkflowMcpServers(
|
|
workspaceId: string,
|
|
signal?: AbortSignal
|
|
): Promise<WorkflowMcpServer[]> {
|
|
try {
|
|
const data = await requestJson(listWorkflowMcpServersContract, {
|
|
query: { workspaceId },
|
|
signal,
|
|
})
|
|
return data.data.servers
|
|
} catch (error) {
|
|
if (error instanceof ApiClientError && error.status === 404) {
|
|
return []
|
|
}
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch workflow MCP servers
|
|
*/
|
|
export function useWorkflowMcpServers(workspaceId: string) {
|
|
return useQuery({
|
|
queryKey: workflowMcpServerKeys.servers(workspaceId),
|
|
queryFn: ({ signal }) => fetchWorkflowMcpServers(workspaceId, signal),
|
|
enabled: !!workspaceId,
|
|
retry: false,
|
|
staleTime: WORKFLOW_MCP_SERVERS_LIST_STALE_TIME,
|
|
placeholderData: keepPreviousData,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Fetch a single workflow MCP server with its tools
|
|
*/
|
|
async function fetchWorkflowMcpServer(
|
|
workspaceId: string,
|
|
serverId: string,
|
|
signal?: AbortSignal
|
|
): Promise<{ server: WorkflowMcpServer; tools: WorkflowMcpTool[] }> {
|
|
const data = await requestJson(getWorkflowMcpServerContract, {
|
|
params: { id: serverId },
|
|
query: { workspaceId },
|
|
signal,
|
|
})
|
|
|
|
return {
|
|
server: data.data.server,
|
|
tools: data.data.tools,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch a single workflow MCP server
|
|
*/
|
|
export function useWorkflowMcpServer(workspaceId: string, serverId: string | null) {
|
|
return useQuery({
|
|
queryKey: workflowMcpServerKeys.server(workspaceId, serverId || ''),
|
|
queryFn: ({ signal }) => fetchWorkflowMcpServer(workspaceId, serverId!, signal),
|
|
enabled: !!workspaceId && !!serverId,
|
|
retry: false,
|
|
staleTime: WORKFLOW_MCP_SERVER_DETAIL_STALE_TIME,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Fetch tools for a workflow MCP server
|
|
*/
|
|
async function fetchWorkflowMcpTools(
|
|
workspaceId: string,
|
|
serverId: string,
|
|
signal?: AbortSignal
|
|
): Promise<WorkflowMcpTool[]> {
|
|
try {
|
|
const data = await requestJson(listWorkflowMcpToolsContract, {
|
|
params: { id: serverId },
|
|
query: { workspaceId },
|
|
signal,
|
|
})
|
|
return data.data.tools
|
|
} catch (error) {
|
|
if (error instanceof ApiClientError && error.status === 404) {
|
|
return []
|
|
}
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch tools for a workflow MCP server
|
|
*/
|
|
export function useWorkflowMcpTools(workspaceId: string, serverId: string | null) {
|
|
return useQuery({
|
|
queryKey: workflowMcpServerKeys.tools(workspaceId, serverId || ''),
|
|
queryFn: ({ signal }) => fetchWorkflowMcpTools(workspaceId, serverId!, signal),
|
|
enabled: !!workspaceId && !!serverId,
|
|
retry: false,
|
|
staleTime: WORKFLOW_MCP_TOOLS_STALE_TIME,
|
|
placeholderData: keepPreviousData,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Create workflow MCP server mutation
|
|
*/
|
|
interface CreateWorkflowMcpServerParams {
|
|
workspaceId: string
|
|
name: string
|
|
description?: string
|
|
isPublic?: boolean
|
|
workflowIds?: string[]
|
|
}
|
|
|
|
export function useCreateWorkflowMcpServer() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
workspaceId,
|
|
name,
|
|
description,
|
|
isPublic,
|
|
workflowIds,
|
|
}: CreateWorkflowMcpServerParams) => {
|
|
const data = await requestJson(createWorkflowMcpServerContract, {
|
|
body: { workspaceId, name, description, isPublic, workflowIds },
|
|
})
|
|
|
|
logger.info(`Created workflow MCP server: ${name}`)
|
|
return data.data.server
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.servers(variables.workspaceId),
|
|
})
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Update workflow MCP server mutation
|
|
*/
|
|
interface UpdateWorkflowMcpServerParams {
|
|
workspaceId: string
|
|
serverId: string
|
|
name?: string
|
|
description?: string
|
|
isPublic?: boolean
|
|
}
|
|
|
|
export function useUpdateWorkflowMcpServer() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
workspaceId,
|
|
serverId,
|
|
name,
|
|
description,
|
|
isPublic,
|
|
}: UpdateWorkflowMcpServerParams) => {
|
|
const data = await requestJson(updateWorkflowMcpServerContract, {
|
|
params: { id: serverId },
|
|
query: { workspaceId },
|
|
body: { name, description, isPublic },
|
|
})
|
|
|
|
logger.info(`Updated workflow MCP server: ${serverId}`)
|
|
return data.data.server
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
return Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.servers(variables.workspaceId),
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.server(variables.workspaceId, variables.serverId),
|
|
}),
|
|
])
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Delete workflow MCP server mutation
|
|
*/
|
|
interface DeleteWorkflowMcpServerParams {
|
|
workspaceId: string
|
|
serverId: string
|
|
}
|
|
|
|
export function useDeleteWorkflowMcpServer() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ workspaceId, serverId }: DeleteWorkflowMcpServerParams) => {
|
|
const data = await requestJson(deleteWorkflowMcpServerContract, {
|
|
params: { id: serverId },
|
|
query: { workspaceId },
|
|
})
|
|
|
|
logger.info(`Deleted workflow MCP server: ${serverId}`)
|
|
return data
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.servers(variables.workspaceId),
|
|
})
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Add tool to workflow MCP server mutation
|
|
*/
|
|
interface AddWorkflowMcpToolParams {
|
|
workspaceId: string
|
|
serverId: string
|
|
workflowId: string
|
|
toolName?: string
|
|
toolDescription?: string
|
|
parameterDescriptionOverrides?: Record<string, string>
|
|
}
|
|
|
|
export function useAddWorkflowMcpTool() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
workspaceId,
|
|
serverId,
|
|
workflowId,
|
|
toolName,
|
|
toolDescription,
|
|
parameterDescriptionOverrides,
|
|
}: AddWorkflowMcpToolParams) => {
|
|
const data = await requestJson(createWorkflowMcpToolContract, {
|
|
params: { id: serverId },
|
|
query: { workspaceId },
|
|
body: { workflowId, toolName, toolDescription, parameterDescriptionOverrides },
|
|
})
|
|
|
|
logger.info(`Added tool to workflow MCP server: ${serverId}`)
|
|
return data.data.tool
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
return Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.servers(variables.workspaceId),
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.server(variables.workspaceId, variables.serverId),
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.tools(variables.workspaceId, variables.serverId),
|
|
}),
|
|
])
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Update tool mutation
|
|
*/
|
|
interface UpdateWorkflowMcpToolParams {
|
|
workspaceId: string
|
|
serverId: string
|
|
toolId: string
|
|
toolName?: string
|
|
toolDescription?: string
|
|
parameterDescriptionOverrides?: Record<string, string>
|
|
/**
|
|
* Full edited schema sent by the Settings edit modal, which has no Start-block context to compute
|
|
* sparse overrides; the server diffs it against the deployed base to extract the overrides.
|
|
*/
|
|
parameterSchema?: Record<string, unknown>
|
|
}
|
|
|
|
export function useUpdateWorkflowMcpTool() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async ({
|
|
workspaceId,
|
|
serverId,
|
|
toolId,
|
|
...updates
|
|
}: UpdateWorkflowMcpToolParams) => {
|
|
const data = await requestJson(updateWorkflowMcpToolContract, {
|
|
params: { id: serverId, toolId },
|
|
query: { workspaceId },
|
|
body: updates,
|
|
})
|
|
|
|
logger.info(`Updated tool ${toolId} in workflow MCP server: ${serverId}`)
|
|
return data.data.tool
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.tools(variables.workspaceId, variables.serverId),
|
|
})
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Delete tool mutation
|
|
*/
|
|
interface DeleteWorkflowMcpToolParams {
|
|
workspaceId: string
|
|
serverId: string
|
|
toolId: string
|
|
}
|
|
|
|
export function useDeleteWorkflowMcpTool() {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ workspaceId, serverId, toolId }: DeleteWorkflowMcpToolParams) => {
|
|
const data = await requestJson(deleteWorkflowMcpToolContract, {
|
|
params: { id: serverId, toolId },
|
|
query: { workspaceId },
|
|
})
|
|
|
|
logger.info(`Deleted tool ${toolId} from workflow MCP server: ${serverId}`)
|
|
return data
|
|
},
|
|
onSettled: (_data, _error, variables) => {
|
|
return Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.servers(variables.workspaceId),
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.server(variables.workspaceId, variables.serverId),
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: workflowMcpServerKeys.tools(variables.workspaceId, variables.serverId),
|
|
}),
|
|
])
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Fetch deployed workflows for a workspace
|
|
*/
|
|
async function fetchDeployedWorkflows(
|
|
workspaceId: string,
|
|
signal?: AbortSignal
|
|
): Promise<DeployedWorkflow[]> {
|
|
const { data } = await requestJson(listWorkflowMcpDeployedWorkflowsContract, {
|
|
query: { workspaceId },
|
|
signal,
|
|
})
|
|
|
|
return data
|
|
.filter((w) => w.isDeployed)
|
|
.map((w) => ({
|
|
id: w.id,
|
|
name: w.name,
|
|
description: w.description ?? null,
|
|
isDeployed: w.isDeployed,
|
|
}))
|
|
}
|
|
|
|
/**
|
|
* Hook to fetch deployed workflows for a workspace
|
|
*/
|
|
export function useDeployedWorkflows(workspaceId: string) {
|
|
return useQuery({
|
|
queryKey: workflowMcpServerKeys.deployedWorkflows(workspaceId),
|
|
queryFn: ({ signal }) => fetchDeployedWorkflows(workspaceId, signal),
|
|
enabled: !!workspaceId,
|
|
staleTime: WORKFLOW_MCP_DEPLOYED_WORKFLOWS_STALE_TIME,
|
|
placeholderData: keepPreviousData,
|
|
})
|
|
}
|