@@ -14,7 +14,13 @@ import {
14
14
} from './items' ;
15
15
import logger , { Logger } from './logger' ;
16
16
import { ModelResponse , ModelSettings } from './model' ;
17
- import { ComputerTool , FunctionTool , Tool , FunctionToolResult } from './tool' ;
17
+ import {
18
+ ComputerTool ,
19
+ FunctionTool ,
20
+ Tool ,
21
+ FunctionToolResult ,
22
+ HostedMCPTool ,
23
+ } from './tool' ;
18
24
import { AgentInputItem , UnknownContext } from './types' ;
19
25
import { Runner } from './run' ;
20
26
import { RunContext } from './runContext' ;
@@ -31,6 +37,7 @@ import * as protocol from './types/protocol';
31
37
import { Computer } from './computer' ;
32
38
import { RunState } from './runState' ;
33
39
import { isZodObject } from './utils' ;
40
+ import * as ProviderData from './types/providerData' ;
34
41
35
42
type ToolRunHandoff = {
36
43
toolCall : protocol . FunctionCallItem ;
@@ -47,11 +54,17 @@ type ToolRunComputer = {
47
54
computer : ComputerTool ;
48
55
} ;
49
56
57
+ type ToolRunMCPApprovalRequest = {
58
+ requestItem : RunToolApprovalItem ;
59
+ mcpTool : HostedMCPTool ;
60
+ } ;
61
+
50
62
export type ProcessedResponse < TContext = UnknownContext > = {
51
63
newItems : RunItem [ ] ;
52
64
handoffs : ToolRunHandoff [ ] ;
53
65
functions : ToolRunFunction < TContext > [ ] ;
54
66
computerActions : ToolRunComputer [ ] ;
67
+ mcpApprovalRequests : ToolRunMCPApprovalRequest [ ] ;
55
68
toolsUsed : string [ ] ;
56
69
hasToolsOrApprovalsToRun ( ) : boolean ;
57
70
} ;
@@ -69,21 +82,78 @@ export function processModelResponse<TContext>(
69
82
const runHandoffs : ToolRunHandoff [ ] = [ ] ;
70
83
const runFunctions : ToolRunFunction < TContext > [ ] = [ ] ;
71
84
const runComputerActions : ToolRunComputer [ ] = [ ] ;
85
+ const runMCPApprovalRequests : ToolRunMCPApprovalRequest [ ] = [ ] ;
72
86
const toolsUsed : string [ ] = [ ] ;
73
87
const handoffMap = new Map ( handoffs . map ( ( h ) => [ h . toolName , h ] ) ) ;
74
88
const functionMap = new Map (
75
89
tools . filter ( ( t ) => t . type === 'function' ) . map ( ( t ) => [ t . name , t ] ) ,
76
90
) ;
77
91
const computerTool = tools . find ( ( t ) => t . type === 'computer' ) ;
92
+ const mcpToolMap = new Map (
93
+ tools
94
+ . filter ( ( t ) => t . type === 'hosted_tool' && t . providerData ?. type === 'mcp' )
95
+ . map ( ( t ) => t as HostedMCPTool )
96
+ . map ( ( t ) => [ t . providerData . serverLabel , t ] ) ,
97
+ ) ;
78
98
79
99
for ( const output of modelResponse . output ) {
80
100
if ( output . type === 'message' ) {
81
101
if ( output . role === 'assistant' ) {
82
102
items . push ( new RunMessageOutputItem ( output , agent ) ) ;
83
103
}
84
104
} else if ( output . type === 'hosted_tool_call' ) {
85
- items . push ( new RunToolCallItem ( output , agent ) ) ;
86
- toolsUsed . push ( output . name ) ;
105
+ if (
106
+ output . providerData ?. type === 'mcp_approval_request' ||
107
+ output . name === 'mcp_approval_request'
108
+ ) {
109
+ // Hosted remote MCP server support
110
+ const providerData =
111
+ output . providerData as ProviderData . HostedMCPApprovalRequest ;
112
+ const mcpServerLabel = providerData . serverLabel ;
113
+ const mcpServerTool = mcpToolMap . get ( mcpServerLabel ) ;
114
+ if ( mcpServerTool !== undefined ) {
115
+ const toolName = JSON . stringify ( {
116
+ server : providerData . serverLabel ,
117
+ name : providerData . name ,
118
+ } ) ;
119
+ // Do this approval later
120
+ runMCPApprovalRequests . push ( {
121
+ requestItem : new RunToolApprovalItem (
122
+ {
123
+ type : 'function_call' ,
124
+ name : toolName ,
125
+ callId : providerData . id ,
126
+ arguments : providerData . arguments || '' ,
127
+ status : 'in_progress' ,
128
+ providerData,
129
+ } ,
130
+ agent ,
131
+ ) ,
132
+ mcpTool : mcpServerTool ,
133
+ } ) ;
134
+ items . push ( new RunToolCallItem ( output , agent ) ) ;
135
+ toolsUsed . push ( toolName ) ;
136
+ } else {
137
+ const message = `MCP server (${ mcpServerLabel } ) not found in Agent (${ agent . name } )` ;
138
+ addErrorToCurrentSpan ( {
139
+ message,
140
+ data : { mcp_server_label : mcpServerLabel } ,
141
+ } ) ;
142
+ throw new ModelBehaviorError ( message ) ;
143
+ }
144
+ } else {
145
+ // the rest of the hosted
146
+ items . push ( new RunToolCallItem ( output , agent ) ) ;
147
+ const toolName = output . providerData ?. serverLabel
148
+ ? // hosted MCP tool
149
+ JSON . stringify ( {
150
+ server : output . providerData . serverLabel ,
151
+ name : output . name ,
152
+ } )
153
+ : // other hosted tools
154
+ output . name ;
155
+ toolsUsed . push ( toolName ) ;
156
+ }
87
157
} else if ( output . type === 'reasoning' ) {
88
158
items . push ( new RunReasoningItem ( output , agent ) ) ;
89
159
} else if ( output . type === 'computer_call' ) {
@@ -147,6 +217,7 @@ export function processModelResponse<TContext>(
147
217
handoffs : runHandoffs ,
148
218
functions : runFunctions ,
149
219
computerActions : runComputerActions ,
220
+ mcpApprovalRequests : runMCPApprovalRequests ,
150
221
toolsUsed : toolsUsed ,
151
222
hasToolsOrApprovalsToRun ( ) : boolean {
152
223
return (
@@ -344,6 +415,40 @@ export async function executeToolsAndSideEffects<TContext>(
344
415
newItems = newItems . concat ( functionResults . map ( ( r ) => r . runItem ) ) ;
345
416
newItems = newItems . concat ( computerResults ) ;
346
417
418
+ // run hosted MCP approval requests
419
+ if ( processedResponse . mcpApprovalRequests . length > 0 ) {
420
+ for ( const approvalRequest of processedResponse . mcpApprovalRequests ) {
421
+ const toolData = approvalRequest . mcpTool
422
+ . providerData as ProviderData . HostedMCPTool < TContext > ;
423
+ if ( ! toolData . onApproval ) {
424
+ throw new UserError (
425
+ `Hosted remote MCP server tool (${ toolData . serverLabel } ) does not have an onApproval function` ,
426
+ ) ;
427
+ }
428
+ const approvalResult = await toolData . onApproval (
429
+ state . _context ,
430
+ approvalRequest . requestItem ,
431
+ ) ;
432
+ const requestData = approvalRequest . requestItem . rawItem
433
+ . providerData as ProviderData . HostedMCPApprovalRequest ;
434
+ const approvalResponseData : ProviderData . HostedMCPApprovalResponse = {
435
+ approve : approvalResult . approve ,
436
+ approvalRequestId : requestData . id ,
437
+ reason : approvalResult . reason ,
438
+ } ;
439
+ newItems . push (
440
+ new RunToolCallItem (
441
+ {
442
+ type : 'hosted_tool_call' ,
443
+ name : 'mcp_approval_response' ,
444
+ providerData : approvalResponseData ,
445
+ } ,
446
+ agent as Agent < unknown , 'text' > ,
447
+ ) ,
448
+ ) ;
449
+ }
450
+ }
451
+
347
452
// process handoffs
348
453
if ( processedResponse . handoffs . length > 0 ) {
349
454
return await executeHandoffCalls (
0 commit comments