improvement(mcp): improved mcp sse events notifs, update jira to handle files, fix UI issues in settings modal, fix org and workspace invitations when bundled (#3182)

* improvement(mcp): improved mcp sse events notifs, update jira to handle files, fix UI issues in settings modal, fix org and workspace invitations when bundled

* added back useMcpToolsEvents for event-driven discovery

* ack PR comments

* updated placeholder

* updated colors, error throwing in mcp modal

* ack comments

* updated error msg
This commit is contained in:
Waleed
2026-02-10 17:08:57 -08:00
committed by GitHub
parent f8e9614c9c
commit 6d16f216c8
48 changed files with 1097 additions and 365 deletions

View File

@@ -6,6 +6,7 @@ import type { NextRequest } from 'next/server'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
import { generateParameterSchemaForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
import { sanitizeToolName } from '@/lib/mcp/workflow-tool-schema'
import { hasValidStartBlock } from '@/lib/workflows/triggers/trigger-utils.server'
@@ -170,6 +171,11 @@ export const POST = withMcpAuth<RouteParams>('write')(
workflowRecord.description ||
`Execute ${workflowRecord.name} workflow`
const parameterSchema =
body.parameterSchema && Object.keys(body.parameterSchema).length > 0
? body.parameterSchema
: await generateParameterSchemaForWorkflow(body.workflowId)
const toolId = crypto.randomUUID()
const [tool] = await db
.insert(workflowMcpTool)
@@ -179,7 +185,7 @@ export const POST = withMcpAuth<RouteParams>('write')(
workflowId: body.workflowId,
toolName,
toolDescription,
parameterSchema: body.parameterSchema || {},
parameterSchema,
createdAt: new Date(),
updatedAt: new Date(),
})

View File

@@ -6,6 +6,7 @@ import type { NextRequest } from 'next/server'
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpPubSub } from '@/lib/mcp/pubsub'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
import { generateParameterSchemaForWorkflow } from '@/lib/mcp/workflow-mcp-sync'
import { sanitizeToolName } from '@/lib/mcp/workflow-tool-schema'
import { hasValidStartBlock } from '@/lib/workflows/triggers/trigger-utils.server'
@@ -156,6 +157,8 @@ export const POST = withMcpAuth('write')(
const toolDescription =
workflowRecord.description || `Execute ${workflowRecord.name} workflow`
const parameterSchema = await generateParameterSchemaForWorkflow(workflowRecord.id)
const toolId = crypto.randomUUID()
await db.insert(workflowMcpTool).values({
id: toolId,
@@ -163,7 +166,7 @@ export const POST = withMcpAuth('write')(
workflowId: workflowRecord.id,
toolName,
toolDescription,
parameterSchema: {},
parameterSchema,
createdAt: new Date(),
updatedAt: new Date(),
})

View File

@@ -446,15 +446,46 @@ export async function PUT(
})
.where(eq(workspaceInvitation.id, wsInvitation.id))
await tx.insert(permissions).values({
id: randomUUID(),
entityType: 'workspace',
entityId: wsInvitation.workspaceId,
userId: session.user.id,
permissionType: wsInvitation.permissions || 'read',
createdAt: new Date(),
updatedAt: new Date(),
})
const existingPermission = await tx
.select({ id: permissions.id, permissionType: permissions.permissionType })
.from(permissions)
.where(
and(
eq(permissions.entityId, wsInvitation.workspaceId),
eq(permissions.entityType, 'workspace'),
eq(permissions.userId, session.user.id)
)
)
.then((rows) => rows[0])
if (existingPermission) {
const PERMISSION_RANK = { read: 0, write: 1, admin: 2 } as const
type PermissionLevel = keyof typeof PERMISSION_RANK
const existingRank =
PERMISSION_RANK[existingPermission.permissionType as PermissionLevel] ?? 0
const newPermission = (wsInvitation.permissions || 'read') as PermissionLevel
const newRank = PERMISSION_RANK[newPermission] ?? 0
if (newRank > existingRank) {
await tx
.update(permissions)
.set({
permissionType: newPermission,
updatedAt: new Date(),
})
.where(eq(permissions.id, existingPermission.id))
}
} else {
await tx.insert(permissions).values({
id: randomUUID(),
entityType: 'workspace',
entityId: wsInvitation.workspaceId,
userId: session.user.id,
permissionType: wsInvitation.permissions || 'read',
createdAt: new Date(),
updatedAt: new Date(),
})
}
}
} else if (status === 'cancelled') {
await tx

View File

@@ -47,16 +47,9 @@ export async function POST(request: NextRequest) {
(await getJiraCloudId(validatedData.domain, validatedData.accessToken))
const formData = new FormData()
const filesOutput: Array<{ name: string; mimeType: string; data: string; size: number }> = []
for (const file of userFiles) {
const buffer = await downloadFileFromStorage(file, requestId, logger)
filesOutput.push({
name: file.name,
mimeType: file.type || 'application/octet-stream',
data: buffer.toString('base64'),
size: buffer.length,
})
const blob = new Blob([new Uint8Array(buffer)], {
type: file.type || 'application/octet-stream',
})
@@ -109,7 +102,7 @@ export async function POST(request: NextRequest) {
issueKey: validatedData.issueKey,
attachments,
attachmentIds,
files: filesOutput,
files: userFiles,
},
})
} catch (error) {