import { db } from '@sim/db' import { workflowMcpServer, workflowMcpTool } from '@sim/db/schema' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { and, eq, inArray, isNull, sql } from 'drizzle-orm' import type { NextRequest } from 'next/server' import { createWorkflowMcpServerBodySchema } from '@/lib/api/contracts/workflow-mcp-servers' import { withRouteHandler } from '@/lib/core/utils/with-route-handler' import { mcpBodyReadErrorResponse, readMcpJsonBodyWithLimit, withMcpAuth, } from '@/lib/mcp/middleware' import { performCreateWorkflowMcpServer } from '@/lib/mcp/orchestration' import { createMcpErrorResponse, createMcpSuccessResponse, mcpOrchestrationStatus, } from '@/lib/mcp/utils' const logger = createLogger('WorkflowMcpServersAPI') export const dynamic = 'force-dynamic' /** * GET - List all workflow MCP servers for the workspace */ export const GET = withRouteHandler( withMcpAuth('read')(async (request: NextRequest, { userId, workspaceId, requestId }) => { try { logger.info(`[${requestId}] Listing workflow MCP servers for workspace ${workspaceId}`) const servers = await db .select({ id: workflowMcpServer.id, workspaceId: workflowMcpServer.workspaceId, createdBy: workflowMcpServer.createdBy, name: workflowMcpServer.name, description: workflowMcpServer.description, isPublic: workflowMcpServer.isPublic, createdAt: workflowMcpServer.createdAt, updatedAt: workflowMcpServer.updatedAt, toolCount: sql`( SELECT COUNT(*)::int FROM "workflow_mcp_tool" WHERE "workflow_mcp_tool"."server_id" = "workflow_mcp_server"."id" AND "workflow_mcp_tool"."archived_at" IS NULL )`.as('tool_count'), }) .from(workflowMcpServer) .where( and(eq(workflowMcpServer.workspaceId, workspaceId), isNull(workflowMcpServer.deletedAt)) ) const serverIds = servers.map((s) => s.id) const tools = serverIds.length > 0 ? await db .select({ serverId: workflowMcpTool.serverId, toolName: workflowMcpTool.toolName, }) .from(workflowMcpTool) .where( and( inArray(workflowMcpTool.serverId, serverIds), isNull(workflowMcpTool.archivedAt) ) ) : [] const toolNamesByServer: Record = {} for (const tool of tools) { if (!toolNamesByServer[tool.serverId]) { toolNamesByServer[tool.serverId] = [] } toolNamesByServer[tool.serverId].push(tool.toolName) } const serversWithToolNames = servers.map((server) => ({ ...server, toolNames: toolNamesByServer[server.id] || [], })) logger.info( `[${requestId}] Listed ${servers.length} workflow MCP servers for workspace ${workspaceId}` ) return createMcpSuccessResponse({ servers: serversWithToolNames }) } catch (error) { logger.error(`[${requestId}] Error listing workflow MCP servers:`, error) return createMcpErrorResponse(toError(error), 'Failed to list workflow MCP servers', 500) } }) ) /** * POST - Create a new workflow MCP server */ export const POST = withRouteHandler( withMcpAuth('write')( async (request: NextRequest, { userId, userName, userEmail, workspaceId, requestId }) => { try { const rawBody = await readMcpJsonBodyWithLimit(request) const parsedBody = createWorkflowMcpServerBodySchema.safeParse(rawBody) if (!parsedBody.success) { return createMcpErrorResponse(parsedBody.error, 'Invalid request format', 400) } const body = parsedBody.data logger.info(`[${requestId}] Creating workflow MCP server:`, { name: body.name, workspaceId, workflowIds: body.workflowIds, }) const result = await performCreateWorkflowMcpServer({ workspaceId, userId, actorName: userName, actorEmail: userEmail, name: body.name, description: body.description, isPublic: body.isPublic, workflowIds: body.workflowIds, }) if (!result.success || !result.server) { return createMcpErrorResponse( new Error(result.error || 'Failed to create workflow MCP server'), result.error || 'Failed to create workflow MCP server', mcpOrchestrationStatus(result.errorCode) ) } const { server } = result const addedTools = result.addedTools || [] logger.info( `[${requestId}] Successfully created workflow MCP server: ${body.name} (ID: ${server.id})` ) return createMcpSuccessResponse({ server, addedTools }, 201) } catch (error) { const bodyErrorResponse = mcpBodyReadErrorResponse(error, request) if (bodyErrorResponse) return bodyErrorResponse logger.error(`[${requestId}] Error creating workflow MCP server:`, error) return createMcpErrorResponse(toError(error), 'Failed to create workflow MCP server', 500) } } ) )