Skip to content

Commit 2d8118b

Browse files
waleedlatif1j15z
authored andcommitted
chore(mcp): remove unused cache CAS methods and simplify discovery helpers
- Delete the unused setIfCurrentMutation/deleteIfCurrentMutation cache methods (interface + memory + redis adapters) and their two Redis Lua scripts; production only uses applyMutationIfCurrent. Tests rewritten onto applyMutationIfCurrent (ordering coverage preserved) or dropped where they only exercised the removed Lua. - Drop the vestigial fallback param from getDiscoveryFailureMessage (all callers coerced to 'Connection failed'). - Route isDynamicClientRegistrationUnsupported through getErrorMessage to match the module's error-inspection convention.
1 parent 0fe1865 commit 2d8118b

8 files changed

Lines changed: 21 additions & 179 deletions

File tree

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { OAuthError, ServerError } from '@modelcontextprotocol/sdk/server/auth/e
22
import { db } from '@sim/db'
33
import { mcpServers } from '@sim/db/schema'
44
import { createLogger } from '@sim/logger'
5-
import { toError } from '@sim/utils/errors'
5+
import { getErrorMessage, toError } from '@sim/utils/errors'
66
import { and, eq, isNull } from 'drizzle-orm'
77
import type { NextRequest } from 'next/server'
88
import { NextResponse } from 'next/server'
@@ -29,10 +29,9 @@ const DCR_UNSUPPORTED_MESSAGE =
2929
"This server doesn't support OAuth client registration. Configure a token instead."
3030

3131
function isDynamicClientRegistrationUnsupported(error: unknown): boolean {
32-
return (
33-
error instanceof Error &&
34-
error.message.toLowerCase().includes('does not support dynamic client registration')
35-
)
32+
return getErrorMessage(error, '')
33+
.toLowerCase()
34+
.includes('does not support dynamic client registration')
3635
}
3736

3837
export function surfaceOauthError(error: unknown): string {

apps/sim/lib/mcp/service.test.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,24 +53,6 @@ const {
5353
cacheMutations.set(scopeKey, mutationId)
5454
return mutationId
5555
}),
56-
setIfCurrentMutation: vi.fn(
57-
async (
58-
scopeKey: string,
59-
mutationId: number,
60-
key: string,
61-
tools: unknown[],
62-
ttlMs: number
63-
) => {
64-
if (cacheMutations.get(scopeKey) !== mutationId) return false
65-
cacheStore.set(key, { tools, expiry: Date.now() + ttlMs })
66-
return true
67-
}
68-
),
69-
deleteIfCurrentMutation: vi.fn(async (scopeKey: string, mutationId: number, key: string) => {
70-
if (cacheMutations.get(scopeKey) !== mutationId) return false
71-
cacheStore.delete(key)
72-
return true
73-
}),
7456
applyMutationIfCurrent: vi.fn(
7557
async (
7658
scopeKey: string,

apps/sim/lib/mcp/service.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,7 @@ function isOauthAuthorizationError(error: unknown, authType: McpServerConfig['au
169169
)
170170
}
171171

172-
function getDiscoveryFailureMessage(
173-
error: unknown,
174-
authType: McpServerConfig['authType'],
175-
fallback: string
176-
): string {
172+
function getDiscoveryFailureMessage(error: unknown, authType: McpServerConfig['authType']): string {
177173
if (authType !== 'oauth' && error instanceof UnauthorizedError) {
178174
return 'Authentication failed'
179175
}
@@ -197,7 +193,7 @@ function getDiscoveryFailureMessage(
197193
) {
198194
return 'Unable to reach the MCP server'
199195
}
200-
return fallback === 'Unknown error' ? 'Connection failed' : fallback
196+
return 'Connection failed'
201197
}
202198

203199
function isTimeoutError(error: unknown): boolean {
@@ -898,7 +894,7 @@ class McpService {
898894
}
899895
return {
900896
kind: 'error',
901-
message: getDiscoveryFailureMessage(error, config.authType, 'Unknown error'),
897+
message: getDiscoveryFailureMessage(error, config.authType),
902898
originalError: error,
903899
config,
904900
mutation,
@@ -1137,7 +1133,7 @@ class McpService {
11371133
config,
11381134
mutation,
11391135
error,
1140-
getDiscoveryFailureMessage(error, config.authType, 'Connection failed')
1136+
getDiscoveryFailureMessage(error, config.authType)
11411137
)
11421138
}
11431139
}
@@ -1200,7 +1196,7 @@ class McpService {
12001196
status: 'error',
12011197
toolCount: 0,
12021198
lastSeen: undefined,
1203-
error: getDiscoveryFailureMessage(error, config.authType, 'Connection failed'),
1199+
error: getDiscoveryFailureMessage(error, config.authType),
12041200
})
12051201
}
12061202
}

apps/sim/lib/mcp/storage/adapter.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,6 @@ export interface McpCacheStorageAdapter {
2222
* same value orders database publication for an end-to-end consistent state.
2323
*/
2424
beginMutation(scopeKey: string): Promise<number>
25-
setIfCurrentMutation(
26-
scopeKey: string,
27-
mutationId: number,
28-
key: string,
29-
tools: McpTool[],
30-
ttlMs: number
31-
): Promise<boolean>
32-
deleteIfCurrentMutation(scopeKey: string, mutationId: number, key: string): Promise<boolean>
3325
/** Atomically applies one server's complete cache state if this mutation still owns it. */
3426
applyMutationIfCurrent(
3527
scopeKey: string,

apps/sim/lib/mcp/storage/memory-cache.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,20 @@ describe('MemoryMcpCache', () => {
175175
expect(newer).toBeGreaterThan(older)
176176

177177
expect(
178-
await cache.setIfCurrentMutation(
178+
await cache.applyMutationIfCurrent(
179179
'server-1',
180180
newer,
181-
'server-1:tools',
182-
[createTool('new-tool')],
183-
60000
181+
{ key: 'server-1:tools', tools: [createTool('new-tool')], ttlMs: 60000 },
182+
[]
184183
)
185184
).toBe(true)
186185
expect(
187-
await cache.setIfCurrentMutation('server-1', older, 'server-1:failure', [], 60000)
186+
await cache.applyMutationIfCurrent(
187+
'server-1',
188+
older,
189+
{ key: 'server-1:failure', tools: [], ttlMs: 60000 },
190+
[]
191+
)
188192
).toBe(false)
189193

190194
expect((await cache.get('server-1:tools'))?.tools).toEqual([createTool('new-tool')])
@@ -218,12 +222,11 @@ describe('MemoryMcpCache', () => {
218222
await cache.clear()
219223

220224
expect(
221-
await cache.setIfCurrentMutation(
225+
await cache.applyMutationIfCurrent(
222226
'server-1',
223227
mutation,
224-
'server-1:tools',
225-
[createTool('stale-tool')],
226-
60000
228+
{ key: 'server-1:tools', tools: [createTool('stale-tool')], ttlMs: 60000 },
229+
[]
227230
)
228231
).toBe(false)
229232
expect(await cache.get('server-1:tools')).toBeNull()

apps/sim/lib/mcp/storage/memory-cache.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -97,28 +97,6 @@ export class MemoryMcpCache implements McpCacheStorageAdapter {
9797
return mutationId
9898
}
9999

100-
async setIfCurrentMutation(
101-
scopeKey: string,
102-
mutationId: number,
103-
key: string,
104-
tools: McpTool[],
105-
ttlMs: number
106-
): Promise<boolean> {
107-
if (this.mutationVersions.get(scopeKey) !== mutationId) return false
108-
await this.set(key, tools, ttlMs)
109-
return true
110-
}
111-
112-
async deleteIfCurrentMutation(
113-
scopeKey: string,
114-
mutationId: number,
115-
key: string
116-
): Promise<boolean> {
117-
if (this.mutationVersions.get(scopeKey) !== mutationId) return false
118-
await this.delete(key)
119-
return true
120-
}
121-
122100
async applyMutationIfCurrent(
123101
scopeKey: string,
124102
mutationId: number,

apps/sim/lib/mcp/storage/redis-cache.test.ts

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -55,50 +55,6 @@ describe('RedisMcpCache ordered mutations', () => {
5555
)
5656
})
5757

58-
it('reports whether a tool cache write still owns the current mutation', async () => {
59-
redis.eval.mockResolvedValueOnce(1).mockResolvedValueOnce(0)
60-
61-
await expect(
62-
cache.setIfCurrentMutation('workspace:w:server:s', 7, 'workspace:w:server:s', [tool], 60_000)
63-
).resolves.toBe(true)
64-
await expect(
65-
cache.setIfCurrentMutation(
66-
'workspace:w:server:s',
67-
6,
68-
'workspace:w:server:s:failure',
69-
[],
70-
60_000
71-
)
72-
).resolves.toBe(false)
73-
74-
expect(redis.eval).toHaveBeenNthCalledWith(
75-
1,
76-
expect.stringContaining("redis.call('SET'"),
77-
2,
78-
'mcp:tools-mutation:workspace:w:server:s',
79-
'mcp:tools:workspace:w:server:s',
80-
'7',
81-
expect.stringContaining('new-tool'),
82-
'60000'
83-
)
84-
})
85-
86-
it('guards cache deletion with the same mutation id', async () => {
87-
redis.eval.mockResolvedValueOnce(0)
88-
89-
await expect(
90-
cache.deleteIfCurrentMutation('workspace:w:server:s', 6, 'workspace:w:server:s:failure')
91-
).resolves.toBe(false)
92-
93-
expect(redis.eval).toHaveBeenCalledWith(
94-
expect.stringContaining("redis.call('DEL'"),
95-
2,
96-
'mcp:tools-mutation:workspace:w:server:s',
97-
'mcp:tools:workspace:w:server:s:failure',
98-
'6'
99-
)
100-
})
101-
10258
it('atomically replaces the complete cache state for the current mutation', async () => {
10359
redis.eval.mockResolvedValueOnce(1)
10460

apps/sim/lib/mcp/storage/redis-cache.ts

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,6 @@ redis.call('SET', KEYS[1], tostring(mutationId), 'PX', ARGV[1])
1818
return mutationId
1919
`
2020

21-
const SET_IF_CURRENT_MUTATION = `
22-
if redis.call('GET', KEYS[1]) ~= ARGV[1] then
23-
return 0
24-
end
25-
redis.call('SET', KEYS[2], ARGV[2], 'PX', ARGV[3])
26-
return 1
27-
`
28-
29-
const DELETE_IF_CURRENT_MUTATION = `
30-
if redis.call('GET', KEYS[1]) ~= ARGV[1] then
31-
return 0
32-
end
33-
redis.call('DEL', KEYS[2])
34-
return 1
35-
`
36-
3721
const APPLY_MUTATION_IF_CURRENT = `
3822
if redis.call('GET', KEYS[1]) ~= ARGV[1] then
3923
return 0
@@ -124,54 +108,6 @@ export class RedisMcpCache implements McpCacheStorageAdapter {
124108
}
125109
}
126110

127-
async setIfCurrentMutation(
128-
scopeKey: string,
129-
mutationId: number,
130-
key: string,
131-
tools: McpTool[],
132-
ttlMs: number
133-
): Promise<boolean> {
134-
try {
135-
const entry: McpCacheEntry = {
136-
tools,
137-
expiry: Date.now() + ttlMs,
138-
}
139-
const result = await this.redis.eval(
140-
SET_IF_CURRENT_MUTATION,
141-
2,
142-
this.getMutationKey(scopeKey),
143-
this.getKey(key),
144-
String(mutationId),
145-
JSON.stringify(entry),
146-
String(ttlMs)
147-
)
148-
return result === 1
149-
} catch (error) {
150-
logger.error('Redis conditional cache set error:', error)
151-
throw error
152-
}
153-
}
154-
155-
async deleteIfCurrentMutation(
156-
scopeKey: string,
157-
mutationId: number,
158-
key: string
159-
): Promise<boolean> {
160-
try {
161-
const result = await this.redis.eval(
162-
DELETE_IF_CURRENT_MUTATION,
163-
2,
164-
this.getMutationKey(scopeKey),
165-
this.getKey(key),
166-
String(mutationId)
167-
)
168-
return result === 1
169-
} catch (error) {
170-
logger.error('Redis conditional cache delete error:', error)
171-
throw error
172-
}
173-
}
174-
175111
async applyMutationIfCurrent(
176112
scopeKey: string,
177113
mutationId: number,

0 commit comments

Comments
 (0)