Skip to content

Commit 1ab4ff3

Browse files
waleedlatif1claude
andcommitted
fix(mcp): preserve custom headers for OAuth servers; atomic PATCH + token clear
- client.ts: pass requestInit.headers for OAuth servers too. Previously OAuth authType set requestInit to undefined, dropping all custom headers including SIM_VIA_HEADER for cross-call loop prevention. The SDK's authProvider adds Authorization on top, so user/system headers must still flow through. - servers/[id]/route.ts: wrap server UPDATE and stale OAuth-token DELETE in a single transaction. Previously the update committed before the token clear, so a token-clear failure would leave new credentials with stale tokens. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 64cc3b8 commit 1ab4ff3

2 files changed

Lines changed: 38 additions & 30 deletions

File tree

apps/sim/app/api/mcp/servers/[id]/route.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -115,29 +115,6 @@ export const PATCH = withRouteHandler(
115115
finalUpdateData.authType = 'oauth'
116116
}
117117

118-
const [updatedServer] = await db
119-
.update(mcpServers)
120-
.set({
121-
...finalUpdateData,
122-
updatedAt: new Date(),
123-
})
124-
.where(
125-
and(
126-
eq(mcpServers.id, serverId),
127-
eq(mcpServers.workspaceId, workspaceId),
128-
isNull(mcpServers.deletedAt)
129-
)
130-
)
131-
.returning()
132-
133-
if (!updatedServer) {
134-
return createMcpErrorResponse(
135-
new Error('Server not found or access denied'),
136-
'Server not found',
137-
404
138-
)
139-
}
140-
141118
const urlChanged = body.url !== undefined && currentServer?.url !== body.url
142119
const clientIdChanged =
143120
body.oauthClientId !== undefined &&
@@ -155,9 +132,42 @@ export const PATCH = withRouteHandler(
155132
}
156133
}
157134
const oauthCredsChanged = clientIdChanged || clientSecretChanged
135+
const shouldClearOauth = urlChanged || oauthCredsChanged
136+
137+
const updatedServer = await db.transaction(async (tx) => {
138+
const [updated] = await tx
139+
.update(mcpServers)
140+
.set({
141+
...finalUpdateData,
142+
updatedAt: new Date(),
143+
})
144+
.where(
145+
and(
146+
eq(mcpServers.id, serverId),
147+
eq(mcpServers.workspaceId, workspaceId),
148+
isNull(mcpServers.deletedAt)
149+
)
150+
)
151+
.returning()
152+
153+
if (!updated) return null
154+
155+
if (shouldClearOauth) {
156+
await tx.delete(mcpServerOauth).where(eq(mcpServerOauth.mcpServerId, serverId))
157+
}
158+
159+
return updated
160+
})
161+
162+
if (!updatedServer) {
163+
return createMcpErrorResponse(
164+
new Error('Server not found or access denied'),
165+
'Server not found',
166+
404
167+
)
168+
}
158169

159-
if (urlChanged || oauthCredsChanged) {
160-
await db.delete(mcpServerOauth).where(eq(mcpServerOauth.mcpServerId, serverId))
170+
if (shouldClearOauth) {
161171
logger.info(
162172
`[${requestId}] Cleared OAuth credentials for server ${serverId} due to ${urlChanged ? 'URL' : 'OAuth credential'} change`
163173
)

apps/sim/lib/mcp/client.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,9 @@ export class McpClient {
9393
const useOauth = this.config.authType === 'oauth' && this.authProvider != null
9494
this.transport = new StreamableHTTPClientTransport(new URL(this.config.url), {
9595
authProvider: useOauth ? this.authProvider : undefined,
96-
requestInit: useOauth
97-
? undefined
98-
: {
99-
headers: this.config.headers,
100-
},
96+
requestInit: {
97+
headers: this.config.headers,
98+
},
10199
})
102100

103101
this.client = new Client(

0 commit comments

Comments
 (0)