Skip to content

Commit d01506b

Browse files
committed
chore(mcp): trim verbose comments + reuse SDK Tool type in McpTool
- McpTool now extends `Pick<Tool, 'name' | 'description'>` from @modelcontextprotocol/sdk so name/description fields stay in sync with the SDK; serverId/serverName remain Sim-specific additions. - Drop file-header restatements ("MCP Types - for connecting to external MCP servers"), one-line wrapper docstrings ("Get connection status"), and narrative comment blocks that just restate visible code. - Keep only comments that document non-obvious "why" — OAuth refresh-lock tradeoff, in-flight dedup key composition, SDK Tool.inputSchema typing, preregistered-client semantics, postMessage handshake contract.
1 parent 13c2a6d commit d01506b

5 files changed

Lines changed: 16 additions & 185 deletions

File tree

apps/sim/hooks/queries/mcp.ts

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ export const mcpKeys = {
5757

5858
export type { McpServer }
5959

60-
/**
61-
* Input for creating/updating an MCP server (distinct from McpServerConfig in types.ts)
62-
*/
60+
/** Wire shape for create/update; distinct from runtime McpServerConfig. */
6361
export interface McpServerInput {
6462
name: string
6563
transport: McpTransport
@@ -265,11 +263,7 @@ export function useCreateMcpServer() {
265263
})
266264
}
267265

268-
/**
269-
* Result of `useStartMcpOauth`. When `popup` is set, the caller should wait
270-
* for it to close (or for the `mcp-oauth` postMessage) before clearing any
271-
* "connecting" UI state.
272-
*/
266+
/** On `redirect`, the caller must wait for `popup.closed` or the `mcp-oauth` postMessage. */
273267
export type StartMcpOauthMutationResult =
274268
| { status: 'redirect'; popup: Window }
275269
| { status: 'already_authorized' }
@@ -464,13 +458,7 @@ const sseConnections: Map<string, SseEntry> =
464458
((globalThis as Record<string, unknown>)[SSE_KEY] as Map<string, SseEntry>) ??
465459
((globalThis as Record<string, unknown>)[SSE_KEY] = new Map<string, SseEntry>())
466460

467-
/**
468-
* Subscribe to MCP tool-change SSE events for a workspace.
469-
* On each `tools_changed` event, invalidates the relevant React Query caches
470-
* so the UI refreshes automatically.
471-
*
472-
* Invalidates both external MCP server keys and workflow MCP server keys.
473-
*/
461+
/** Subscribes to `tools_changed` SSE events and invalidates the affected query keys. */
474462
export function useMcpToolsEvents(workspaceId: string) {
475463
const queryClient = useQueryClient()
476464

@@ -598,17 +586,11 @@ export function useMcpServerTest() {
598586
}
599587
}
600588

601-
/**
602-
* Fetch allowed MCP domains (admin-configured allowlist)
603-
*/
604589
async function fetchAllowedMcpDomains(signal?: AbortSignal): Promise<string[] | null> {
605590
const data = await requestJson(getAllowedMcpDomainsContract, { signal })
606591
return data.allowedMcpDomains ?? null
607592
}
608593

609-
/**
610-
* Hook to fetch allowed MCP domains
611-
*/
612594
export function useAllowedMcpDomains() {
613595
return useQuery<string[] | null>({
614596
queryKey: mcpKeys.allowedDomains(),

apps/sim/lib/mcp/client.ts

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
/**
2-
* MCP (Model Context Protocol) Client
3-
*
4-
* Implements the client side of MCP protocol with support for:
5-
* - Streamable HTTP transport (MCP 2025-06-18)
6-
* - Tool execution and discovery
7-
* - Session management and protocol version negotiation
8-
* - Custom security/consent layer
9-
*/
10-
111
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'
122
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
133
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
@@ -94,11 +84,6 @@ export class McpClient {
9484
)
9585
}
9686

97-
/**
98-
* Initialize connection to MCP server.
99-
* If an `onToolsChanged` callback was provided, registers a notification handler
100-
* for `notifications/tools/list_changed` after connecting.
101-
*/
10287
async connect(): Promise<void> {
10388
logger.info(`Connecting to MCP server: ${this.config.name} (${this.config.transport})`)
10489

@@ -135,9 +120,6 @@ export class McpClient {
135120
}
136121
}
137122

138-
/**
139-
* Disconnect from MCP server
140-
*/
141123
async disconnect(): Promise<void> {
142124
logger.info(`Disconnecting from MCP server: ${this.config.name}`)
143125

@@ -152,16 +134,10 @@ export class McpClient {
152134
logger.info(`Disconnected from MCP server: ${this.config.name}`)
153135
}
154136

155-
/**
156-
* Get current connection status
157-
*/
158137
getStatus(): McpConnectionStatus {
159138
return { ...this.connectionStatus }
160139
}
161140

162-
/**
163-
* List all available tools from the server
164-
*/
165141
async listTools(): Promise<McpTool[]> {
166142
if (!this.isConnected) {
167143
throw new McpConnectionError('Not connected to server', this.config.name)
@@ -190,9 +166,6 @@ export class McpClient {
190166
}
191167
}
192168

193-
/**
194-
* Execute a tool on the MCP server
195-
*/
196169
async callTool(toolCall: McpToolCall): Promise<McpToolResult> {
197170
if (!this.isConnected) {
198171
throw new McpConnectionError('Not connected to server', this.config.name)
@@ -237,10 +210,6 @@ export class McpClient {
237210
}
238211
}
239212

240-
/**
241-
* Ping the server to check if it's still alive and responsive
242-
* Per MCP spec: servers should respond to ping requests
243-
*/
244213
async ping(): Promise<{ _meta?: Record<string, any> }> {
245214
if (!this.isConnected) {
246215
throw new McpConnectionError('Not connected to server', this.config.name)
@@ -257,18 +226,11 @@ export class McpClient {
257226
}
258227
}
259228

260-
/**
261-
* Check if the server declared `capabilities.tools.listChanged: true` during initialization.
262-
*/
263229
hasListChangedCapability(): boolean {
264230
return !!this.client.getServerCapabilities()?.tools?.listChanged
265231
}
266232

267-
/**
268-
* Register a callback to be invoked when the underlying transport closes.
269-
* Used by the connection manager for reconnection logic.
270-
* Chains with the SDK's internal onclose handler so it still performs its cleanup.
271-
*/
233+
/** Chains with the SDK's internal onclose handler so its cleanup still runs. */
272234
onClose(callback: () => void): void {
273235
const existingHandler = this.transport.onclose
274236
this.transport.onclose = () => {
@@ -277,26 +239,17 @@ export class McpClient {
277239
}
278240
}
279241

280-
/**
281-
* Get server configuration
282-
*/
283242
getConfig(): McpServerConfig {
284243
return { ...this.config }
285244
}
286245

287-
/**
288-
* Get version information for this client
289-
*/
290246
static getVersionInfo(): McpVersionInfo {
291247
return {
292248
supported: [...McpClient.SUPPORTED_VERSIONS],
293249
preferred: McpClient.SUPPORTED_VERSIONS[0],
294250
}
295251
}
296252

297-
/**
298-
* Get the negotiated protocol version for this connection
299-
*/
300253
getNegotiatedVersion(): string | undefined {
301254
const serverVersion = this.client.getServerVersion()
302255
return typeof serverVersion === 'string' ? serverVersion : undefined
@@ -306,9 +259,6 @@ export class McpClient {
306259
return this.transport.sessionId
307260
}
308261

309-
/**
310-
* Request user consent for tool execution
311-
*/
312262
async requestConsent(consentRequest: McpConsentRequest): Promise<McpConsentResponse> {
313263
if (!this.securityPolicy.requireConsent) {
314264
return { granted: true, auditId: `audit-${Date.now()}` }

apps/sim/lib/mcp/oauth/provider.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,7 @@ export interface PreregisteredClient {
4141
interface SimMcpOauthProviderInit {
4242
row: McpOauthRow
4343
scope?: string
44-
/**
45-
* Optional user-supplied client credentials. When provided, the SDK skips
46-
* Dynamic Client Registration and uses these for the auth/token exchange.
47-
*/
44+
/** When set, the SDK skips Dynamic Client Registration and uses these credentials directly. */
4845
preregistered?: PreregisteredClient
4946
}
5047

apps/sim/lib/mcp/service.ts

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
/**
2-
* MCP Service - Clean stateless service for MCP operations
3-
*/
4-
51
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'
62
import { StreamableHTTPError } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
73
import { db } from '@sim/db'
@@ -100,19 +96,12 @@ class McpService {
10096
}
10197
}
10298

103-
/**
104-
* Dispose of the service and cleanup resources
105-
*/
10699
dispose(): void {
107100
this.unsubscribeConnectionManager?.()
108101
this.cacheAdapter.dispose()
109102
logger.info('MCP Service disposed')
110103
}
111104

112-
/**
113-
* Resolve environment variables in server config.
114-
* Uses shared utility with strict mode (throws on missing vars).
115-
*/
116105
private async resolveConfigEnvVars(
117106
config: McpServerConfig,
118107
userId: string,
@@ -126,9 +115,6 @@ class McpService {
126115
return { config: resolvedConfig, resolvedIP }
127116
}
128117

129-
/**
130-
* Get server configuration from database
131-
*/
132118
private async getServerConfig(
133119
serverId: string,
134120
workspaceId: string
@@ -171,9 +157,6 @@ class McpService {
171157
}
172158
}
173159

174-
/**
175-
* Get all enabled servers for a workspace
176-
*/
177160
private async getWorkspaceServers(workspaceId: string): Promise<McpServerConfig[]> {
178161
const whereConditions = [
179162
eq(mcpServers.workspaceId, workspaceId),
@@ -205,9 +188,6 @@ class McpService {
205188
.filter((config) => isMcpDomainAllowed(config.url))
206189
}
207190

208-
/**
209-
* Create and connect to an MCP client
210-
*/
211191
private async createClient(
212192
config: McpServerConfig,
213193
resolvedIP: string | null,
@@ -262,10 +242,6 @@ class McpService {
262242
})
263243
}
264244

265-
/**
266-
* Execute a tool on a specific server with retry logic for session errors.
267-
* Retries once on session-related errors (400, 404, session ID issues).
268-
*/
269245
async executeTool(
270246
userId: string,
271247
serverId: string,
@@ -320,22 +296,14 @@ class McpService {
320296
throw new Error(`Failed to execute tool ${toolCall.name} after ${maxRetries} attempts`)
321297
}
322298

323-
/**
324-
* Detects an expired or unknown `Mcp-Session-Id` so the caller can retry.
325-
* Per MCP spec, the server returns HTTP 404 for an unknown session id and
326-
* may return 400 when the session header is malformed; the SDK surfaces
327-
* both as `StreamableHTTPError` with a typed numeric `code` field.
328-
*/
299+
/** MCP spec: server returns 404 for unknown session id, 400 for malformed header. */
329300
private isSessionError(error: unknown): boolean {
330301
if (error instanceof StreamableHTTPError) {
331302
return error.code === 404 || error.code === 400
332303
}
333304
return false
334305
}
335306

336-
/**
337-
* Update server connection status after discovery attempt
338-
*/
339307
private async updateServerStatus(
340308
serverId: string,
341309
workspaceId: string,
@@ -448,9 +416,6 @@ class McpService {
448416
}
449417
}
450418

451-
/**
452-
* Discover tools from all workspace servers
453-
*/
454419
async discoverTools(
455420
userId: string,
456421
workspaceId: string,
@@ -744,9 +709,6 @@ class McpService {
744709
throw new Error(`Failed to discover tools from server ${serverId} after ${maxRetries} attempts`)
745710
}
746711

747-
/**
748-
* Get server summaries for a user
749-
*/
750712
async getServerSummaries(userId: string, workspaceId: string): Promise<McpServerSummary[]> {
751713
const requestId = generateRequestId()
752714

0 commit comments

Comments
 (0)