Skip to content

Commit 1a73022

Browse files
committed
inject requestMcpToolData
1 parent c2978f5 commit 1a73022

File tree

10 files changed

+101
-89
lines changed

10 files changed

+101
-89
lines changed

backend/src/client-wrapper.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { generateCompactId } from '@codebuff/common/util/string'
33
import { subscribeToAction } from './websockets/websocket-action'
44

55
import type { ServerAction } from '@codebuff/common/actions'
6+
import type { RequestMcpToolDataFn } from '@codebuff/common/types/contracts/client'
7+
import type { ParamsOf } from '@codebuff/common/types/function-params'
68
import type { MCPConfig } from '@codebuff/common/types/mcp'
79
import type { ToolResultOutput } from '@codebuff/common/types/messages/content-part'
810
import type { ServerMessage } from '@codebuff/common/websockets/websocket-schema'
@@ -94,3 +96,48 @@ export async function requestToolCallWs(params: {
9496
})
9597
})
9698
}
99+
100+
/**
101+
* Requests a tool call execution from the client with timeout support
102+
* @param ws - The WebSocket connection
103+
* @param mcpConfig - The configuration for the MCP server
104+
* @param input - Arguments for the tool (can include timeout)
105+
* @returns Promise resolving to the tool execution result
106+
*/
107+
export async function requestMcpToolDataWs(
108+
params: ParamsOf<RequestMcpToolDataFn> & {
109+
ws: WebSocket
110+
},
111+
): ReturnType<RequestMcpToolDataFn> {
112+
const { ws, mcpConfig, toolNames } = params
113+
114+
return new Promise((resolve) => {
115+
const requestId = generateCompactId()
116+
117+
// Set up timeout
118+
const timeoutHandle = setTimeout(
119+
() => {
120+
unsubscribe()
121+
resolve([])
122+
},
123+
45_000 + 5000, // Convert to ms and add a small buffer
124+
)
125+
126+
// Subscribe to response
127+
const unsubscribe = subscribeToAction('mcp-tool-data', (action) => {
128+
if (action.requestId === requestId) {
129+
clearTimeout(timeoutHandle)
130+
unsubscribe()
131+
resolve(action.tools)
132+
}
133+
})
134+
135+
// Send the request
136+
sendAction(ws, {
137+
type: 'request-mcp-tool-data',
138+
mcpConfig,
139+
requestId,
140+
...(toolNames && { toolNames }),
141+
})
142+
})
143+
}

backend/src/mcp/util.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
import { requestMcpToolData } from '../websockets/websocket-action'
2-
31
import type { AgentTemplate } from '../templates/types'
2+
import type { RequestMcpToolDataFn } from '@codebuff/common/types/contracts/client'
3+
import type { OptionalFields } from '@codebuff/common/types/function-params'
44
import type { ProjectFileContext } from '@codebuff/common/util/file'
5-
import type { WebSocket } from 'ws'
65

7-
export async function getMCPToolData({
8-
ws,
9-
toolNames,
10-
mcpServers,
11-
writeTo,
12-
}: {
13-
ws: WebSocket
14-
toolNames: AgentTemplate['toolNames']
15-
mcpServers: AgentTemplate['mcpServers']
16-
writeTo: ProjectFileContext['customToolDefinitions']
17-
}): Promise<ProjectFileContext['customToolDefinitions']> {
6+
export async function getMCPToolData(
7+
params: OptionalFields<
8+
{
9+
toolNames: AgentTemplate['toolNames']
10+
mcpServers: AgentTemplate['mcpServers']
11+
writeTo: ProjectFileContext['customToolDefinitions']
12+
requestMcpToolData: RequestMcpToolDataFn
13+
},
14+
'writeTo'
15+
>,
16+
): Promise<ProjectFileContext['customToolDefinitions']> {
17+
const withDefaults = { writeTo: {}, ...params }
18+
const { toolNames, mcpServers, writeTo, requestMcpToolData } = withDefaults
19+
1820
const requestedToolsByMcp: Record<string, string[] | undefined> = {}
1921
for (const t of toolNames) {
2022
if (!t.includes('/')) {
@@ -28,13 +30,11 @@ export async function getMCPToolData({
2830
requestedToolsByMcp[mcpName].push(toolName)
2931
}
3032

31-
writeTo ??= {}
3233
const promises: Promise<any>[] = []
3334
for (const [mcpName, mcpConfig] of Object.entries(mcpServers)) {
3435
promises.push(
3536
(async () => {
3637
const mcpData = await requestMcpToolData({
37-
ws,
3838
mcpConfig,
3939
toolNames: requestedToolsByMcp[mcpName] ?? null,
4040
})

backend/src/run-agent-step.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,7 @@ import type {
3535
} from '@codebuff/common/types/contracts/database'
3636
import type { Logger } from '@codebuff/common/types/contracts/logger'
3737
import type { ParamsExcluding } from '@codebuff/common/types/function-params'
38-
import type {
39-
AssistantMessage,
40-
Message,
41-
} from '@codebuff/common/types/messages/codebuff-message'
38+
import type { Message } from '@codebuff/common/types/messages/codebuff-message'
4239
import type {
4340
ToolResultPart,
4441
TextPart,
@@ -53,8 +50,6 @@ import type {
5350
import type { ProjectFileContext } from '@codebuff/common/util/file'
5451
import type { WebSocket } from 'ws'
5552

56-
57-
5853
export const runAgentStep = async (
5954
params: {
6055
ws: WebSocket
@@ -95,6 +90,10 @@ export const runAgentStep = async (
9590
| 'agentState'
9691
| 'agentTemplates'
9792
| 'additionalToolDefinitions'
93+
> &
94+
ParamsExcluding<
95+
typeof getMCPToolData,
96+
'toolNames' | 'mcpServers' | 'writeTo'
9897
>,
9998
): Promise<{
10099
agentState: AgentState
@@ -204,7 +203,7 @@ export const runAgentStep = async (
204203
),
205204
)
206205
return getMCPToolData({
207-
ws,
206+
...params,
208207
toolNames: agentTemplate.toolNames,
209208
mcpServers: agentTemplate.mcpServers,
210209
writeTo: additionalToolDefinitions,
@@ -435,6 +434,10 @@ export const loopAgentSteps = async (
435434
| 'promptType'
436435
| 'agentTemplates'
437436
| 'additionalToolDefinitions'
437+
> &
438+
ParamsExcluding<
439+
typeof getMCPToolData,
440+
'toolNames' | 'mcpServers' | 'writeTo'
438441
>,
439442
): Promise<{
440443
agentState: AgentState
@@ -495,7 +498,7 @@ export const loopAgentSteps = async (
495498
),
496499
)
497500
return getMCPToolData({
498-
ws,
501+
...params,
499502
toolNames: agentTemplate.toolNames,
500503
mcpServers: agentTemplate.mcpServers,
501504
writeTo: additionalToolDefinitions,
@@ -522,7 +525,7 @@ export const loopAgentSteps = async (
522525
),
523526
)
524527
return getMCPToolData({
525-
ws,
528+
...params,
526529
toolNames: agentTemplate.toolNames,
527530
mcpServers: agentTemplate.mcpServers,
528531
writeTo: additionalToolDefinitions,
@@ -767,5 +770,3 @@ export const loopAgentSteps = async (
767770
}
768771
}
769772
}
770-
771-

backend/src/tools/tool-executor.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import type {
3333
customToolDefinitionsSchema,
3434
ProjectFileContext,
3535
} from '@codebuff/common/util/file'
36-
import type { WebSocket } from 'ws'
3736

3837
export type CustomToolCall = {
3938
toolName: string
@@ -121,7 +120,6 @@ export type ExecuteToolCallParams<T extends string = ToolName> = {
121120
toolResults: ToolResultPart[]
122121
toolResultsToAddAfterStream: ToolResultPart[]
123122
previousToolCallFinished: Promise<void>
124-
ws: WebSocket
125123
agentTemplate: AgentTemplate
126124
fileContext: ProjectFileContext
127125
agentStepId: string
@@ -146,7 +144,6 @@ export function executeToolCall<T extends ToolName>(
146144
toolResults,
147145
toolResultsToAddAfterStream,
148146
previousToolCallFinished,
149-
ws,
150147
agentTemplate,
151148
fileContext,
152149
agentStepId,
@@ -159,6 +156,7 @@ export function executeToolCall<T extends ToolName>(
159156
autoInsertEndStepParam = false,
160157
excludeToolFromMessageHistory = false,
161158
requestToolCall,
159+
requestMcpToolData,
162160
logger,
163161
} = params
164162
const toolCall: CodebuffToolCall<T> | ToolCallError = parseRawToolCall<T>({
@@ -378,7 +376,6 @@ export async function executeCustomToolCall(
378376
toolResults,
379377
toolResultsToAddAfterStream,
380378
previousToolCallFinished,
381-
ws,
382379
agentTemplate,
383380
fileContext,
384381
userInputId,
@@ -391,7 +388,7 @@ export async function executeCustomToolCall(
391388
} = params
392389
const toolCall: CustomToolCall | ToolCallError = parseRawCustomToolCall({
393390
customToolDefs: await getMCPToolData({
394-
ws,
391+
...params,
395392
toolNames: agentTemplate.toolNames,
396393
mcpServers: agentTemplate.mcpServers,
397394
writeTo: cloneDeep(fileContext.customToolDefinitions),

backend/src/websockets/middleware.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { eq } from 'drizzle-orm'
1515
import { getUserInfoFromApiKey } from './auth'
1616
import { updateRequestContext } from './request-context'
1717
import { sendAction } from './websocket-action'
18-
import { requestToolCallWs } from '../client-wrapper'
18+
import { requestMcpToolDataWs, requestToolCallWs } from '../client-wrapper'
1919
import { withAppContext } from '../context/app-context'
2020
import { BACKEND_AGENT_RUNTIME_IMPL } from '../impl/agent-runtime'
2121
import { checkAuth } from '../util/check-auth'
@@ -149,6 +149,7 @@ export class WebSocketMiddleware {
149149

150150
const scopedDeps: AgentRuntimeScopedDeps = {
151151
requestToolCall: (params) => requestToolCallWs({ ...params, ws }),
152+
requestMcpToolData: (params) => requestMcpToolDataWs({ ...params, ws }),
152153
}
153154

154155
// Use the new combined context - much cleaner!

backend/src/websockets/websocket-action.ts

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import type {
2828
import type { GetUserInfoFromApiKeyFn } from '@codebuff/common/types/contracts/database'
2929
import type { Logger } from '@codebuff/common/types/contracts/logger'
3030
import type { ParamsExcluding } from '@codebuff/common/types/function-params'
31-
import type { MCPConfig } from '@codebuff/common/types/mcp'
3231
import type { ClientMessage } from '@codebuff/common/websockets/websocket-schema'
3332
import type { WebSocket } from 'ws'
3433

@@ -456,56 +455,3 @@ export async function requestOptionalFile(params: {
456455
const file = await requestFile({ ws, filePath })
457456
return toOptionalFile(file)
458457
}
459-
460-
/**
461-
* Requests a tool call execution from the client with timeout support
462-
* @param ws - The WebSocket connection
463-
* @param mcpConfig - The configuration for the MCP server
464-
* @param input - Arguments for the tool (can include timeout)
465-
* @returns Promise resolving to the tool execution result
466-
*/
467-
export async function requestMcpToolData({
468-
ws,
469-
mcpConfig,
470-
toolNames,
471-
}: {
472-
ws: WebSocket
473-
mcpConfig: MCPConfig
474-
toolNames: string[] | null
475-
}): Promise<
476-
{
477-
name: string
478-
description?: string
479-
inputSchema: unknown
480-
}[]
481-
> {
482-
return new Promise((resolve) => {
483-
const requestId = generateCompactId()
484-
485-
// Set up timeout
486-
const timeoutHandle = setTimeout(
487-
() => {
488-
unsubscribe()
489-
resolve([])
490-
},
491-
45_000 + 5000, // Convert to ms and add a small buffer
492-
)
493-
494-
// Subscribe to response
495-
const unsubscribe = subscribeToAction('mcp-tool-data', (action) => {
496-
if (action.requestId === requestId) {
497-
clearTimeout(timeoutHandle)
498-
unsubscribe()
499-
resolve(action.tools)
500-
}
501-
})
502-
503-
// Send the request
504-
sendAction(ws, {
505-
type: 'request-mcp-tool-data',
506-
mcpConfig,
507-
requestId,
508-
...(toolNames && { toolNames }),
509-
})
510-
})
511-
}

common/src/testing/impl/agent-runtime.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,7 @@ export const TEST_AGENT_RUNTIME_SCOPED_IMPL: AgentRuntimeScopedDeps =
4444
requestToolCall: () => {
4545
throw new Error('requestToolCall not implemented in test runtime')
4646
},
47+
requestMcpToolData: () => {
48+
throw new Error('requestMcpToolData not implemented in test runtime')
49+
},
4750
})

common/src/types/contracts/agent-runtime.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { RequestToolCallFn } from './client'
1+
import type { RequestMcpToolDataFn, RequestToolCallFn } from './client'
22
import type {
33
AddAgentStepFn,
44
FetchAgentFromDatabaseFn,
@@ -33,4 +33,5 @@ export type AgentRuntimeDeps = {
3333
export type AgentRuntimeScopedDeps = {
3434
// Client (WebSocket)
3535
requestToolCall: RequestToolCallFn
36+
requestMcpToolData: RequestMcpToolDataFn
3637
}

common/src/types/contracts/client.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,14 @@ export type RequestToolCallFn = (params: {
99
}) => Promise<{
1010
output: ToolResultOutput[]
1111
}>
12+
13+
export type RequestMcpToolDataFn = (params: {
14+
mcpConfig: MCPConfig
15+
toolNames: string[] | null
16+
}) => Promise<
17+
{
18+
name: string
19+
description?: string
20+
inputSchema: unknown
21+
}[]
22+
>

evals/scaffolding.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { EventEmitter } from 'events'
33
import fs from 'fs'
44
import path from 'path'
55

6-
import { requestToolCallWs } from '@codebuff/backend/client-wrapper'
6+
import {
7+
requestMcpToolDataWs,
8+
requestToolCallWs,
9+
} from '@codebuff/backend/client-wrapper'
710
import { runAgentStep } from '@codebuff/backend/run-agent-step'
811
import { assembleLocalAgentTemplates } from '@codebuff/backend/templates/agent-registry'
912
import { getFileTokenScores } from '@codebuff/code-map/parse'
@@ -183,6 +186,8 @@ export async function runAgentStepScaffolding(
183186

184187
const agentRuntimeScopedImpl: AgentRuntimeScopedDeps = {
185188
requestToolCall: (params) => requestToolCallWs({ ...params, ws: mockWs }),
189+
requestMcpToolData: (params) =>
190+
requestMcpToolDataWs({ ...params, ws: mockWs }),
186191
}
187192
const result = await runAgentStep({
188193
...EVALS_AGENT_RUNTIME_IMPL,

0 commit comments

Comments
 (0)