Skip to content

Commit 3d4b9f0

Browse files
authored
feat(mcp): added support for mcp servers (#1296)
* update infra and remove railway * feat(mcp): add mcp support * consolidate mcp utils * UI improvements, more MCP stuff * cleanup placeholders * reran migrations * general improvements * fix server side mcp exec * more improvements, fixed search in environment settings tab * persist subblock values for mcp block * style fixes * udpdate all text-primary to text-muted-foreground for visibility in dark mode * Revert "update infra and remove railway" This reverts commit dbf2b15. * make MCP servers workspace-scoped * cleanup & remove unused dep * consolidated utils, DRY * added tests * better error messages, confirmed that permissions works correctly * additional improvements * remove extraneous comments * reran migrations * lint * style changes * fix: prevent config mutation in MCP client URL retry logic Fixed an issue where the MCP client was mutating the shared configuration object's URL during retry attempts. This could cause configuration corruption if the same config object was reused elsewhere. * resolve PR comments * ack PR comments
1 parent c48039f commit 3d4b9f0

File tree

193 files changed

+14250
-603
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

193 files changed

+14250
-603
lines changed

apps/sim/app/(auth)/reset-password/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function ResetPasswordContent() {
101101
</CardContent>
102102
<CardFooter>
103103
<p className='w-full text-center text-gray-500 text-sm'>
104-
<Link href='/login' className='text-primary hover:underline'>
104+
<Link href='/login' className='text-muted-foreground hover:underline'>
105105
Back to login
106106
</Link>
107107
</p>

apps/sim/app/api/__test-utils__/setup.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
*/
44
import { afterEach, beforeEach, vi } from 'vitest'
55

6-
// Mock Next.js implementations
76
vi.mock('next/headers', () => ({
87
cookies: () => ({
98
get: vi.fn().mockReturnValue({ value: 'test-session-token' }),
@@ -13,7 +12,6 @@ vi.mock('next/headers', () => ({
1312
}),
1413
}))
1514

16-
// Mock auth utilities
1715
vi.mock('@/lib/auth/session', () => ({
1816
getSession: vi.fn().mockResolvedValue({
1917
user: {
@@ -24,13 +22,10 @@ vi.mock('@/lib/auth/session', () => ({
2422
}),
2523
}))
2624

27-
// Configure Vitest environment
2825
beforeEach(() => {
29-
// Clear all mocks before each test
3026
vi.clearAllMocks()
3127
})
3228

3329
afterEach(() => {
34-
// Ensure all mocks are restored after each test
3530
vi.restoreAllMocks()
3631
})

apps/sim/app/api/__test-utils__/utils.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -944,35 +944,29 @@ export interface TestSetupOptions {
944944
export function setupComprehensiveTestMocks(options: TestSetupOptions = {}) {
945945
const { auth = { authenticated: true }, database = {}, storage, authApi, features = {} } = options
946946

947-
// Setup basic infrastructure mocks
948947
setupCommonApiMocks()
949948
mockUuid()
950949
mockCryptoUuid()
951950

952-
// Setup authentication
953951
const authMocks = mockAuth(auth.user)
954952
if (auth.authenticated) {
955953
authMocks.setAuthenticated(auth.user)
956954
} else {
957955
authMocks.setUnauthenticated()
958956
}
959957

960-
// Setup database
961958
const dbMocks = createMockDatabase(database)
962959

963-
// Setup storage if needed
964960
let storageMocks
965961
if (storage) {
966962
storageMocks = createStorageProviderMocks(storage)
967963
}
968964

969-
// Setup auth API if needed
970965
let authApiMocks
971966
if (authApi) {
972967
authApiMocks = createAuthApiMocks(authApi)
973968
}
974969

975-
// Setup feature-specific mocks
976970
const featureMocks: any = {}
977971
if (features.workflowUtils) {
978972
featureMocks.workflowUtils = mockWorkflowUtils()
@@ -1008,12 +1002,10 @@ export function createMockDatabase(options: MockDatabaseOptions = {}) {
10081002

10091003
let selectCallCount = 0
10101004

1011-
// Helper to create error
10121005
const createDbError = (operation: string, message?: string) => {
10131006
return new Error(message || `Database ${operation} error`)
10141007
}
10151008

1016-
// Create chainable select mock
10171009
const createSelectChain = () => ({
10181010
from: vi.fn().mockReturnThis(),
10191011
leftJoin: vi.fn().mockReturnThis(),
@@ -1038,7 +1030,6 @@ export function createMockDatabase(options: MockDatabaseOptions = {}) {
10381030
}),
10391031
})
10401032

1041-
// Create insert chain
10421033
const createInsertChain = () => ({
10431034
values: vi.fn().mockImplementation(() => ({
10441035
returning: vi.fn().mockImplementation(() => {
@@ -1056,7 +1047,6 @@ export function createMockDatabase(options: MockDatabaseOptions = {}) {
10561047
})),
10571048
})
10581049

1059-
// Create update chain
10601050
const createUpdateChain = () => ({
10611051
set: vi.fn().mockImplementation(() => ({
10621052
where: vi.fn().mockImplementation(() => {
@@ -1068,7 +1058,6 @@ export function createMockDatabase(options: MockDatabaseOptions = {}) {
10681058
})),
10691059
})
10701060

1071-
// Create delete chain
10721061
const createDeleteChain = () => ({
10731062
where: vi.fn().mockImplementation(() => {
10741063
if (deleteOptions.throwError) {
@@ -1078,7 +1067,6 @@ export function createMockDatabase(options: MockDatabaseOptions = {}) {
10781067
}),
10791068
})
10801069

1081-
// Create transaction mock
10821070
const createTransactionMock = () => {
10831071
return vi.fn().mockImplementation(async (callback: any) => {
10841072
if (transactionOptions.throwError) {
@@ -1200,7 +1188,6 @@ export function setupKnowledgeMocks(
12001188
mocks.generateEmbedding = vi.fn().mockResolvedValue([0.1, 0.2, 0.3])
12011189
}
12021190

1203-
// Mock the knowledge utilities
12041191
vi.doMock('@/app/api/knowledge/utils', () => mocks)
12051192

12061193
return mocks
@@ -1218,35 +1205,30 @@ export function setupFileApiMocks(
12181205
) {
12191206
const { authenticated = true, storageProvider = 's3', cloudEnabled = true } = options
12201207

1221-
// Setup basic mocks
12221208
setupCommonApiMocks()
12231209
mockUuid()
12241210
mockCryptoUuid()
12251211

1226-
// Setup auth
12271212
const authMocks = mockAuth()
12281213
if (authenticated) {
12291214
authMocks.setAuthenticated()
12301215
} else {
12311216
authMocks.setUnauthenticated()
12321217
}
12331218

1234-
// Setup file system mocks
12351219
mockFileSystem({
12361220
writeFileSuccess: true,
12371221
readFileContent: 'test content',
12381222
existsResult: true,
12391223
})
12401224

1241-
// Setup storage provider mocks (this will mock @/lib/uploads)
12421225
let storageMocks
12431226
if (storageProvider) {
12441227
storageMocks = createStorageProviderMocks({
12451228
provider: storageProvider,
12461229
isCloudEnabled: cloudEnabled,
12471230
})
12481231
} else {
1249-
// If no storage provider specified, just mock the base functions
12501232
vi.doMock('@/lib/uploads', () => ({
12511233
getStorageProvider: vi.fn().mockReturnValue('local'),
12521234
isUsingCloudStorage: vi.fn().mockReturnValue(cloudEnabled),

apps/sim/app/api/auth/oauth/connections/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { jwtDecode } from 'jwt-decode'
33
import { type NextRequest, NextResponse } from 'next/server'
44
import { getSession } from '@/lib/auth'
55
import { createLogger } from '@/lib/logs/console/logger'
6+
import { generateRequestId } from '@/lib/utils'
67
import { db } from '@/db'
78
import { account, user } from '@/db/schema'
89

@@ -18,7 +19,7 @@ interface GoogleIdToken {
1819
* Get all OAuth connections for the current user
1920
*/
2021
export async function GET(request: NextRequest) {
21-
const requestId = crypto.randomUUID().slice(0, 8)
22+
const requestId = generateRequestId()
2223

2324
try {
2425
// Get the session

apps/sim/app/api/auth/oauth/credentials/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createLogger } from '@/lib/logs/console/logger'
66
import type { OAuthService } from '@/lib/oauth/oauth'
77
import { parseProvider } from '@/lib/oauth/oauth'
88
import { getUserEntityPermissions } from '@/lib/permissions/utils'
9+
import { generateRequestId } from '@/lib/utils'
910
import { db } from '@/db'
1011
import { account, user, workflow } from '@/db/schema'
1112

@@ -23,7 +24,7 @@ interface GoogleIdToken {
2324
* Get credentials for a specific provider
2425
*/
2526
export async function GET(request: NextRequest) {
26-
const requestId = crypto.randomUUID().slice(0, 8)
27+
const requestId = generateRequestId()
2728

2829
try {
2930
// Get query params

apps/sim/app/api/auth/oauth/disconnect/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { and, eq, like, or } from 'drizzle-orm'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { getSession } from '@/lib/auth'
44
import { createLogger } from '@/lib/logs/console/logger'
5+
import { generateRequestId } from '@/lib/utils'
56
import { db } from '@/db'
67
import { account } from '@/db/schema'
78

@@ -13,7 +14,7 @@ const logger = createLogger('OAuthDisconnectAPI')
1314
* Disconnect an OAuth provider for the current user
1415
*/
1516
export async function POST(request: NextRequest) {
16-
const requestId = crypto.randomUUID().slice(0, 8)
17+
const requestId = generateRequestId()
1718

1819
try {
1920
// Get the session

apps/sim/app/api/auth/oauth/microsoft/file/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { eq } from 'drizzle-orm'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { getSession } from '@/lib/auth'
44
import { createLogger } from '@/lib/logs/console/logger'
5+
import { generateRequestId } from '@/lib/utils'
56
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
67
import { db } from '@/db'
78
import { account } from '@/db/schema'
@@ -14,7 +15,7 @@ const logger = createLogger('MicrosoftFileAPI')
1415
* Get a single file from Microsoft OneDrive
1516
*/
1617
export async function GET(request: NextRequest) {
17-
const requestId = crypto.randomUUID().slice(0, 8)
18+
const requestId = generateRequestId()
1819
try {
1920
// Get the session
2021
const session = await getSession()

apps/sim/app/api/auth/oauth/microsoft/files/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { eq } from 'drizzle-orm'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { getSession } from '@/lib/auth'
44
import { createLogger } from '@/lib/logs/console/logger'
5+
import { generateRequestId } from '@/lib/utils'
56
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
67
import { db } from '@/db'
78
import { account } from '@/db/schema'
@@ -14,7 +15,7 @@ const logger = createLogger('MicrosoftFilesAPI')
1415
* Get Excel files from Microsoft OneDrive
1516
*/
1617
export async function GET(request: NextRequest) {
17-
const requestId = crypto.randomUUID().slice(0, 8) // Generate a short request ID for correlation
18+
const requestId = generateRequestId()
1819

1920
try {
2021
// Get the session

apps/sim/app/api/auth/oauth/token/route.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server'
22
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
33
import { checkHybridAuth } from '@/lib/auth/hybrid'
44
import { createLogger } from '@/lib/logs/console/logger'
5+
import { generateRequestId } from '@/lib/utils'
56
import { getCredential, refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
67

78
export const dynamic = 'force-dynamic'
@@ -14,7 +15,7 @@ const logger = createLogger('OAuthTokenAPI')
1415
* and workflow-based authentication (for server-side requests)
1516
*/
1617
export async function POST(request: NextRequest) {
17-
const requestId = crypto.randomUUID().slice(0, 8)
18+
const requestId = generateRequestId()
1819

1920
logger.info(`[${requestId}] OAuth token API POST request received`)
2021

@@ -59,7 +60,7 @@ export async function POST(request: NextRequest) {
5960
* Get the access token for a specific credential
6061
*/
6162
export async function GET(request: NextRequest) {
62-
const requestId = crypto.randomUUID().slice(0, 8) // Short request ID for correlation
63+
const requestId = generateRequestId()
6364

6465
try {
6566
// Get the credential ID from the query params

apps/sim/app/api/auth/oauth/wealthbox/item/route.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { eq } from 'drizzle-orm'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { getSession } from '@/lib/auth'
44
import { createLogger } from '@/lib/logs/console/logger'
5+
import { generateRequestId } from '@/lib/utils'
56
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
67
import { db } from '@/db'
78
import { account } from '@/db/schema'
@@ -14,7 +15,7 @@ const logger = createLogger('WealthboxItemAPI')
1415
* Get a single item (note, contact, task) from Wealthbox
1516
*/
1617
export async function GET(request: NextRequest) {
17-
const requestId = crypto.randomUUID().slice(0, 8)
18+
const requestId = generateRequestId()
1819

1920
try {
2021
// Get the session

0 commit comments

Comments
 (0)