Skip to content

Commit af2c93c

Browse files
committed
fix(mcp): don't leak non-OAuth errors; clearCache covers disabled servers
1 parent f155e3c commit af2c93c

3 files changed

Lines changed: 31 additions & 6 deletions

File tree

apps/sim/app/api/mcp/oauth/start/route.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,23 @@ describe('MCP OAuth start route', () => {
134134
expect(body.error).toBe('OAuth authorization already in progress for this server')
135135
expect(mockMcpAuth).not.toHaveBeenCalled()
136136
})
137+
138+
it('does not leak non-OAuth internal error details to the client', async () => {
139+
mcpOauthMockFns.mockGetOrCreateOauthRow.mockRejectedValueOnce(
140+
new Error('connect ECONNREFUSED 10.0.0.5:5432 (internal-db-host)')
141+
)
142+
const request = new NextRequest(
143+
'http://localhost:3000/api/mcp/oauth/start?workspaceId=workspace-1&serverId=server-1'
144+
)
145+
146+
const response = await GET(request)
147+
const body = await response.json()
148+
149+
expect(response.status).toBe(500)
150+
expect(body.error).toBe('Failed to start OAuth flow')
151+
expect(body.error).not.toContain('ECONNREFUSED')
152+
expect(body.error).not.toContain('internal-db-host')
153+
})
137154
})
138155

139156
describe('surfaceOauthError', () => {

apps/sim/app/api/mcp/oauth/start/route.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,11 @@ export const GET = withRouteHandler(
150150
}
151151
} catch (error) {
152152
logger.error('Error starting MCP OAuth flow:', error)
153-
return createMcpErrorResponse(toError(error), surfaceOauthError(error), 500)
153+
// Only surface OAuth-flow errors verbatim; everything else (DB, decryption,
154+
// network) gets a generic message to avoid leaking internal details.
155+
const userMessage =
156+
error instanceof OAuthError ? surfaceOauthError(error) : 'Failed to start OAuth flow'
157+
return createMcpErrorResponse(toError(error), userMessage, 500)
154158
}
155159
})
156160
)

apps/sim/lib/mcp/service.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -673,13 +673,17 @@ class McpService {
673673
async clearCache(workspaceId?: string): Promise<void> {
674674
try {
675675
if (workspaceId) {
676-
const servers = await this.getWorkspaceServers(workspaceId)
676+
// Enumerate without enabled/deletedAt filters so disabled or soft-deleted
677+
// servers still get their cache keys dropped (hard-deleted rows are gone
678+
// from the table and their keys expire naturally via TTL).
679+
const rows = await db
680+
.select({ id: mcpServers.id })
681+
.from(mcpServers)
682+
.where(eq(mcpServers.workspaceId, workspaceId))
677683
await Promise.allSettled(
678-
servers.map((s) => this.cacheAdapter.delete(serverCacheKey(workspaceId, s.id)))
679-
)
680-
logger.debug(
681-
`Cleared MCP tool cache for workspace ${workspaceId} (${servers.length} servers)`
684+
rows.map((r) => this.cacheAdapter.delete(serverCacheKey(workspaceId, r.id)))
682685
)
686+
logger.debug(`Cleared MCP tool cache for workspace ${workspaceId} (${rows.length} servers)`)
683687
} else {
684688
await this.cacheAdapter.clear()
685689
logger.debug('Cleared all MCP tool cache')

0 commit comments

Comments
 (0)