Skip to content

Commit 57e5bac

Browse files
authored
fix(mcp): resolve userId before JWT generation for agent block auth (#3932)
* fix(mcp): resolve userId before JWT generation for agent block auth * test(mcp): add regression test for agent block JWT userId resolution
1 parent 8ce0299 commit 57e5bac

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

apps/sim/tools/index.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const {
2525
mockGetCustomToolById,
2626
mockListCustomTools,
2727
mockGetCustomToolByIdOrTitle,
28+
mockGenerateInternalToken,
2829
} = vi.hoisted(() => ({
2930
mockIsHosted: { value: false },
3031
mockEnv: { NEXT_PUBLIC_APP_URL: 'http://localhost:3000' } as Record<string, string | undefined>,
@@ -38,6 +39,7 @@ const {
3839
mockGetCustomToolById: vi.fn(),
3940
mockListCustomTools: vi.fn(),
4041
mockGetCustomToolByIdOrTitle: vi.fn(),
42+
mockGenerateInternalToken: vi.fn(),
4143
}))
4244

4345
// Mock feature flags
@@ -65,6 +67,10 @@ vi.mock('@/lib/api-key/byok', () => ({
6567
getBYOKKey: (...args: unknown[]) => mockGetBYOKKey(...args),
6668
}))
6769

70+
vi.mock('@/lib/auth/internal', () => ({
71+
generateInternalToken: (...args: unknown[]) => mockGenerateInternalToken(...args),
72+
}))
73+
6874
vi.mock('@/lib/billing/core/usage-log', () => ({}))
6975

7076
vi.mock('@/lib/core/rate-limiter/hosted-key', () => ({
@@ -1154,6 +1160,34 @@ describe('MCP Tool Execution', () => {
11541160
expect(result.timing).toBeDefined()
11551161
})
11561162

1163+
it('should embed userId in JWT when executionContext is undefined (agent block path)', async () => {
1164+
mockGenerateInternalToken.mockResolvedValue('test-token')
1165+
1166+
global.fetch = Object.assign(
1167+
vi.fn().mockImplementation(async () => ({
1168+
ok: true,
1169+
status: 200,
1170+
json: () =>
1171+
Promise.resolve({
1172+
success: true,
1173+
data: { output: { content: [{ type: 'text', text: 'OK' }] } },
1174+
}),
1175+
})),
1176+
{ preconnect: vi.fn() }
1177+
) as typeof fetch
1178+
1179+
await executeTool('mcp-123-test_tool', {
1180+
query: 'test',
1181+
_context: {
1182+
workspaceId: 'workspace-456',
1183+
workflowId: 'workflow-789',
1184+
userId: 'user-abc',
1185+
},
1186+
})
1187+
1188+
expect(mockGenerateInternalToken).toHaveBeenCalledWith('user-abc')
1189+
})
1190+
11571191
describe('Tool request retries', () => {
11581192
function makeJsonResponse(
11591193
status: number,

apps/sim/tools/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,11 +1552,13 @@ async function executeMcpTool(
15521552

15531553
const baseUrl = getInternalApiBaseUrl()
15541554

1555+
const mcpScope = resolveToolScope(params, executionContext)
1556+
15551557
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
15561558

15571559
if (typeof window === 'undefined') {
15581560
try {
1559-
const internalToken = await generateInternalToken(executionContext?.userId)
1561+
const internalToken = await generateInternalToken(mcpScope.userId)
15601562
headers.Authorization = `Bearer ${internalToken}`
15611563
} catch (error) {
15621564
logger.error(`[${actualRequestId}] Failed to generate internal token:`, error)
@@ -1587,8 +1589,6 @@ async function executeMcpTool(
15871589
)
15881590
}
15891591

1590-
const mcpScope = resolveToolScope(params, executionContext)
1591-
15921592
if (mcpScope.callChain && mcpScope.callChain.length > 0) {
15931593
headers[SIM_VIA_HEADER] = serializeCallChain(mcpScope.callChain)
15941594
}

0 commit comments

Comments
 (0)