Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions apps/sim/tools/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const {
mockGetCustomToolById,
mockListCustomTools,
mockGetCustomToolByIdOrTitle,
mockGenerateInternalToken,
} = vi.hoisted(() => ({
mockIsHosted: { value: false },
mockEnv: { NEXT_PUBLIC_APP_URL: 'http://localhost:3000' } as Record<string, string | undefined>,
Expand All @@ -38,6 +39,7 @@ const {
mockGetCustomToolById: vi.fn(),
mockListCustomTools: vi.fn(),
mockGetCustomToolByIdOrTitle: vi.fn(),
mockGenerateInternalToken: vi.fn(),
}))

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

vi.mock('@/lib/auth/internal', () => ({
generateInternalToken: (...args: unknown[]) => mockGenerateInternalToken(...args),
}))

vi.mock('@/lib/billing/core/usage-log', () => ({}))

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

it('should embed userId in JWT when executionContext is undefined (agent block path)', async () => {
mockGenerateInternalToken.mockResolvedValue('test-token')

global.fetch = Object.assign(
vi.fn().mockImplementation(async () => ({
ok: true,
status: 200,
json: () =>
Promise.resolve({
success: true,
data: { output: { content: [{ type: 'text', text: 'OK' }] } },
}),
})),
{ preconnect: vi.fn() }
) as typeof fetch

await executeTool('mcp-123-test_tool', {
query: 'test',
_context: {
workspaceId: 'workspace-456',
workflowId: 'workflow-789',
userId: 'user-abc',
},
})

expect(mockGenerateInternalToken).toHaveBeenCalledWith('user-abc')
})

describe('Tool request retries', () => {
function makeJsonResponse(
status: number,
Expand Down
6 changes: 3 additions & 3 deletions apps/sim/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1552,11 +1552,13 @@ async function executeMcpTool(

const baseUrl = getInternalApiBaseUrl()

const mcpScope = resolveToolScope(params, executionContext)

const headers: Record<string, string> = { 'Content-Type': 'application/json' }

if (typeof window === 'undefined') {
try {
const internalToken = await generateInternalToken(executionContext?.userId)
const internalToken = await generateInternalToken(mcpScope.userId)
headers.Authorization = `Bearer ${internalToken}`
} catch (error) {
logger.error(`[${actualRequestId}] Failed to generate internal token:`, error)
Expand Down Expand Up @@ -1587,8 +1589,6 @@ async function executeMcpTool(
)
}

const mcpScope = resolveToolScope(params, executionContext)

if (mcpScope.callChain && mcpScope.callChain.length > 0) {
headers[SIM_VIA_HEADER] = serializeCallChain(mcpScope.callChain)
}
Expand Down
Loading