Skip to content

Commit 9321743

Browse files
author
priyanshu.solanki
committed
added a workflow as mcp
1 parent 7b5405e commit 9321743

File tree

35 files changed

+5518
-201
lines changed

35 files changed

+5518
-201
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { db } from '@sim/db'
2+
import { permissions, workflowMcpServer, workspace } from '@sim/db/schema'
3+
import { eq, and, sql } from 'drizzle-orm'
4+
import { type NextRequest, NextResponse } from 'next/server'
5+
import { checkHybridAuth } from '@/lib/auth/hybrid'
6+
import { getBaseUrl } from '@/lib/core/utils/urls'
7+
import { createLogger } from '@/lib/logs/console/logger'
8+
9+
const logger = createLogger('McpDiscoverAPI')
10+
11+
export const dynamic = 'force-dynamic'
12+
13+
/**
14+
* GET - Discover all published MCP servers available to the authenticated user
15+
*
16+
* This endpoint allows external MCP clients to discover available servers
17+
* using just their API key, without needing to know workspace IDs.
18+
*
19+
* Authentication: API Key (X-API-Key header) or Session
20+
*
21+
* Returns all published MCP servers from workspaces the user has access to.
22+
*/
23+
export async function GET(request: NextRequest) {
24+
try {
25+
// Authenticate the request
26+
const auth = await checkHybridAuth(request, { requireWorkflowId: false })
27+
28+
if (!auth.success || !auth.userId) {
29+
return NextResponse.json(
30+
{
31+
success: false,
32+
error: 'Authentication required. Provide X-API-Key header with your Sim API key.'
33+
},
34+
{ status: 401 }
35+
)
36+
}
37+
38+
const userId = auth.userId
39+
40+
// Get all workspaces the user has access to via permissions table
41+
const userWorkspacePermissions = await db
42+
.select({ entityId: permissions.entityId })
43+
.from(permissions)
44+
.where(
45+
and(
46+
eq(permissions.userId, userId),
47+
eq(permissions.entityType, 'workspace')
48+
)
49+
)
50+
51+
const workspaceIds = userWorkspacePermissions.map(w => w.entityId)
52+
53+
if (workspaceIds.length === 0) {
54+
return NextResponse.json({
55+
success: true,
56+
servers: [],
57+
message: 'No workspaces found for this user',
58+
})
59+
}
60+
61+
// Get all published MCP servers from user's workspaces with tool count
62+
const servers = await db
63+
.select({
64+
id: workflowMcpServer.id,
65+
name: workflowMcpServer.name,
66+
description: workflowMcpServer.description,
67+
workspaceId: workflowMcpServer.workspaceId,
68+
workspaceName: workspace.name,
69+
isPublished: workflowMcpServer.isPublished,
70+
publishedAt: workflowMcpServer.publishedAt,
71+
toolCount: sql<number>`(
72+
SELECT COUNT(*)::int
73+
FROM "workflow_mcp_tool"
74+
WHERE "workflow_mcp_tool"."server_id" = "workflow_mcp_server"."id"
75+
)`.as('tool_count'),
76+
})
77+
.from(workflowMcpServer)
78+
.leftJoin(workspace, eq(workflowMcpServer.workspaceId, workspace.id))
79+
.where(
80+
and(
81+
eq(workflowMcpServer.isPublished, true),
82+
sql`${workflowMcpServer.workspaceId} IN ${workspaceIds}`
83+
)
84+
)
85+
.orderBy(workflowMcpServer.name)
86+
87+
const baseUrl = getBaseUrl()
88+
89+
// Format response with connection URLs
90+
const formattedServers = servers.map(server => ({
91+
id: server.id,
92+
name: server.name,
93+
description: server.description,
94+
workspace: {
95+
id: server.workspaceId,
96+
name: server.workspaceName,
97+
},
98+
toolCount: server.toolCount || 0,
99+
publishedAt: server.publishedAt,
100+
urls: {
101+
http: `${baseUrl}/api/mcp/serve/${server.id}`,
102+
sse: `${baseUrl}/api/mcp/serve/${server.id}/sse`,
103+
},
104+
}))
105+
106+
logger.info(`User ${userId} discovered ${formattedServers.length} MCP servers`)
107+
108+
return NextResponse.json({
109+
success: true,
110+
servers: formattedServers,
111+
authentication: {
112+
method: 'API Key',
113+
header: 'X-API-Key',
114+
description: 'Include your Sim API key in the X-API-Key header for all MCP requests',
115+
},
116+
usage: {
117+
listTools: {
118+
method: 'POST',
119+
body: '{"jsonrpc":"2.0","id":1,"method":"tools/list"}',
120+
},
121+
callTool: {
122+
method: 'POST',
123+
body: '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"TOOL_NAME","arguments":{}}}',
124+
},
125+
},
126+
})
127+
} catch (error) {
128+
logger.error('Error discovering MCP servers:', error)
129+
return NextResponse.json(
130+
{ success: false, error: 'Failed to discover MCP servers' },
131+
{ status: 500 }
132+
)
133+
}
134+
}

0 commit comments

Comments
 (0)