Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions provider/openaiprovider/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,11 @@ func responsesBuildCompletionParams(config AgentConfig, messages []*message.Mess
if tl.Authorization != "" {
variant.Authorization = openai.String(tl.Authorization)
}
// Honor the caller's approval mode. When unset, the Responses API
// defaults require_approval to "always".
if tl.ApprovalMode != "" {
variant.RequireApproval.OfMcpToolApprovalSetting = openai.String(string(tl.ApprovalMode))
}
params.Tools = append(params.Tools, responses.ToolUnionParam{
OfMcp: &variant,
})
Expand Down
32 changes: 32 additions & 0 deletions provider/openaiprovider/responses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3425,6 +3425,38 @@ func TestResponsesMCPServerToolAddressRouting(t *testing.T) {
}
}

// An MCPServer ApprovalMode is forwarded as the Responses require_approval
// setting; unset leaves it off (provider default).
func TestResponsesMCPServerApprovalMode(t *testing.T) {
const output = `{"id":"resp","object":"response","created_at":1,"status":"completed","model":"gpt-4o-mini","output":[]}`
tests := []struct {
name string
mode hostedtool.MCPApprovalMode
wantTool string
}{
{"never", hostedtool.MCPApprovalNever, `{"type":"mcp","server_label":"drive","server_url":"https://example.com/mcp","require_approval":"never"}`},
{"always", hostedtool.MCPApprovalAlways, `{"type":"mcp","server_label":"drive","server_url":"https://example.com/mcp","require_approval":"always"}`},
{"unset", "", `{"type":"mcp","server_label":"drive","server_url":"https://example.com/mcp"}`},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := `{"model":"gpt-4o-mini","input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"hello"}]}],"tools":[` + tt.wantTool + `]}`
server := newTestResponsesServer(t, input, output)
defer server.Close()
_, err := newTestResponsesClient(server, "gpt-4o-mini").RunText(t.Context(), "hello",
agent.WithTool(&hostedtool.MCPServer{
ServerName: "drive",
ServerAddress: "https://example.com/mcp",
ApprovalMode: tt.mode,
}),
).Collect()
if err != nil {
t.Fatalf("error = %v", err)
}
})
}
}

func TestResponsesCodeInterpreterTool_NonStreaming(t *testing.T) {
const input = `
{
Expand Down
14 changes: 14 additions & 0 deletions tool/hostedtool/hostedtool.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,22 @@ type MCPServer struct {
AllowedTools []string
// Headers contains optional HTTP headers to send when connecting to the MCP server.
Headers map[string]string
// ApprovalMode controls whether calls to this MCP server require approval.
// The zero value leaves the provider default (which, for the OpenAI Responses
// API, requires approval for every call).
ApprovalMode MCPApprovalMode
}

// MCPApprovalMode controls whether hosted MCP tool calls require human approval.
type MCPApprovalMode string

const (
// MCPApprovalAlways requires approval for every hosted MCP tool call.
MCPApprovalAlways MCPApprovalMode = "always"
// MCPApprovalNever auto-approves hosted MCP tool calls.
MCPApprovalNever MCPApprovalMode = "never"
)

func (t *MCPServer) Name() string {
return "mcp"
}
Expand Down
Loading