Repository Public
1# Tool search23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45Tool search allows the model to dynamically search for and load tools into the model's context as needed. This allows you to avoid loading all tool definitions into the model's context up front and **may help reduce overall token usage and cost**. For optimal cost and latency, tool search is designed to **preserve the model’s cache**. When new tools are discovered by the model, they are injected at the end of the context window.67In the Responses API, only `gpt-5.4` and later models support `tool_search`.89The following configuration and examples use the Responses API. For session-based function loading and automatic MCP discovery, see [Agents API](#agents-api).1011To activate tool search in the Responses API, you must do two things:12131. Add `tool_search` as a tool in your `tools` array.142. If you are using [functions](https://developers.openai.com/api/docs/guides/function-calling#defining-functions), mark the ones you want to defer with `defer_loading: true`. If you are using [MCP servers](https://developers.openai.com/api/docs/guides/tools-connectors-mcp), set `defer_loading: true` on the MCP server tool definition.1516### Use namespaces where possible1718You can use tool search with deferred [functions](https://developers.openai.com/api/docs/guides/function-calling#defining-functions), [namespaces](https://developers.openai.com/api/docs/guides/function-calling#defining-namespaces), or [MCP servers](https://developers.openai.com/api/docs/guides/tools-connectors-mcp), but we recommend using namespaces or MCP servers when possible. Our models have primarily been trained to search those surfaces, and token savings are usually more material there.1920For namespaces, `defer_loading` applies to the functions inside the namespace, not to the namespace object itself.2122At the start of a request, the model still sees the name and description of whatever is searchable. For a namespace or MCP server, that means the model sees only the namespace or server name and description at the beginning, without showing details of the individual functions contained within it until the tool search tool loads them. For an individual deferred function, the model still sees the function name and description, so in practice tool search is mostly deferring the parameter schema.2324For maximum token savings, we recommend grouping deferred functions into namespaces or MCP servers with clear, high-level descriptions that give the model a strong overview of what is contained within them, so it can effectively search and load only the relevant functions. As a best practice, aim to keep each namespace to fewer than 10 functions for better token efficiency and model performance.2526```json27{28 "tools": [29 {30// highlight-start:subtle31 "type": "namespace",32// highlight-end33 "name": "crm",34 "description": "CRM tools for customer lookup and order management.",35 "tools": [36 {37 "type": "function",38 "name": "list_open_orders",39 "description": "List open orders for a customer ID.",40// highlight-start:subtle41 "defer_loading": true,42// highlight-end43 "parameters": {44 "type": "object",45 "properties": {46 "customer_id": { "type": "string" }47 },48 "required": ["customer_id"],49 "additionalProperties": false50 }51 }52 ]53 },54 {55 "type": "tool_search"56 }57 ]58 }59```606162Namespaces can have a mix of tools that are deferred and not deferred. Tools without `defer_loading: true` are callable immediately, while deferred tools in the same namespace are loaded through tool search.6364### Tool search types6566Choose between two types of tool search:6768- **Hosted tool search:** OpenAI searches across the deferred tools you declared in the request and returns the loaded subset in the same response.69- **Client-executed tool search:** The model emits a `tool_search_call`, your application performs the lookup, and you return a matching `tool_search_output`.7071Start with hosted tool search if the candidate tools are already known when72 you create the request. Use client-executed tool search when tool discovery73 depends on project state, tenant state, or another system your application74 controls.7576## Hosted tool search7778Hosted tool search is the simplest path when you already know the full inventory of [functions](https://developers.openai.com/api/docs/guides/function-calling#defining-functions), [namespaces](https://developers.openai.com/api/docs/guides/function-calling#defining-namespaces), or [MCP servers](https://developers.openai.com/api/docs/guides/tools-connectors-mcp) you want the model to search. You declare them up front, add `{"type": "tool_search"}`, and let the API decide what to load.7980Configure hosted tool search8182```javascript83import OpenAI from "openai";8485const client = new OpenAI();8687const crmNamespace = {88 type: "namespace",89 name: "crm",90 description: "CRM tools for customer lookup and order management.",91 tools: [92 {93 type: "function",94 name: "get_customer_profile",95 description: "Fetch a customer profile by customer ID.",96 parameters: {97 type: "object",98 properties: {99 customer_id: { type: "string" },100 },101 required: ["customer_id"],102 additionalProperties: false,103 },104 },105 {106 type: "function",107 name: "list_open_orders",108 description: "List open orders for a customer ID.",109 // highlight-start:subtle110 defer_loading: true,111 // highlight-end112 parameters: {113 type: "object",114 properties: {115 customer_id: { type: "string" },116 },117 required: ["customer_id"],118 additionalProperties: false,119 },120 },121 ],122};123124const response = await client.responses.create({125 model: "gpt-6-astra",126 input: "List open orders for customer CUST-12345.",127 // highlight-start:subtle128 tools: [crmNamespace, { type: "tool_search" }],129 // highlight-end130 parallel_tool_calls: false,131});132133console.log(response.output);134```135136```python137from openai import OpenAI138139client = OpenAI()140141crm_namespace = {142 "type": "namespace",143 "name": "crm",144 "description": "CRM tools for customer lookup and order management.",145 "tools": [146 {147 "type": "function",148 "name": "get_customer_profile",149 "description": "Fetch a customer profile by customer ID.",150 "parameters": {151 "type": "object",152 "properties": {153 "customer_id": {"type": "string"},154 },155 "required": ["customer_id"],156 "additionalProperties": False,157 },158 },159 {160 "type": "function",161 "name": "list_open_orders",162 "description": "List open orders for a customer ID.",163 # highlight-start:subtle164 "defer_loading": True,165 # highlight-end166 "parameters": {167 "type": "object",168 "properties": {169 "customer_id": {"type": "string"},170 },171 "required": ["customer_id"],172 "additionalProperties": False,173 },174 },175 ],176}177178response = client.responses.create(179 model="gpt-6-astra",180 input="List open orders for customer CUST-12345.",181 tools=[182 crm_namespace,183 # highlight-start:subtle184 {"type": "tool_search"},185 # highlight-end186 ],187 parallel_tool_calls=False,188)189190print(response.output)191```192193```go194package main195196import (197 "context"198 "fmt"199200 "github.com/openai/openai-go/v3"201 "github.com/openai/openai-go/v3/responses"202)203204func main() {205 client := openai.NewClient()206 parameters := map[string]any{207 "type": "object",208 "properties": map[string]any{"customer_id": map[string]any{"type": "string"}},209 "required": []string{"customer_id"},210 "additionalProperties": false,211 }212 namespace := responses.ToolParamOfNamespace(213 "CRM tools for customer lookup and order management.",214 "crm",215 []responses.NamespaceToolToolUnionParam{216 {OfFunction: &responses.NamespaceToolToolFunctionParam{217 Name: "get_customer_profile", Description: openai.String("Fetch a customer profile by customer ID."), Parameters: parameters,218 }},219 {OfFunction: &responses.NamespaceToolToolFunctionParam{220 Name: "list_open_orders", Description: openai.String("List open orders for a customer ID."), DeferLoading: openai.Bool(true), Parameters: parameters,221 }},222 },223 )224 response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{225 Model: "gpt-6-astra",226 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("List open orders for customer CUST-12345.")},227 Tools: []responses.ToolUnionParam{namespace, {OfToolSearch: &responses.ToolSearchToolParam{}}},228 ParallelToolCalls: openai.Bool(false),229 })230 if err != nil {231 panic(err)232 }233 fmt.Println(response.Output)234}235```236237```java238import com.openai.client.OpenAIClient;239import com.openai.client.okhttp.OpenAIOkHttpClient;240import com.openai.core.JsonValue;241import com.openai.models.responses.NamespaceTool;242import com.openai.models.responses.ResponseCreateParams;243import com.openai.models.responses.ToolSearchTool;244import java.util.List;245import java.util.Map;246247ResponseCreateParams params =248 ResponseCreateParams.builder()249 .model("gpt-6-astra")250 .input("List open orders for customer CUST-12345.")251 .parallelToolCalls(false)252 .addTool(253 NamespaceTool.builder()254 .name("crm")255 .description("CRM tools for customer lookup and order management.")256 .addTool(257 NamespaceTool.Tool.Function.builder()258 .name("get_customer_profile")259 .description("Fetch a customer profile by customer ID.")260 .strict(true)261 .parameters(262 JsonValue.from(263 Map.of(264 "type",265 "object",266 "properties",267 Map.of("customer_id", Map.of("type", "string")),268 "required",269 List.of("customer_id"),270 "additionalProperties",271 false)))272 .build())273 .addTool(274 NamespaceTool.Tool.Function.builder()275 .name("list_open_orders")276 .description("List open orders for a customer ID.")277 .deferLoading(true)278 .strict(true)279 .parameters(280 JsonValue.from(281 Map.of(282 "type",283 "object",284 "properties",285 Map.of("customer_id", Map.of("type", "string")),286 "required",287 List.of("customer_id"),288 "additionalProperties",289 false)))290 .build())291 .build())292 .addTool(ToolSearchTool.builder().execution(ToolSearchTool.Execution.SERVER).build())293 .build();294295client.responses().create(params).output().forEach(System.out::println);296```297298```ruby299require "openai"300301client = OpenAI::Client.new302parameters = {303 type: :object,304 properties: { customer_id: { type: :string } },305 required: ["customer_id"],306 additionalProperties: false307}308response = client.responses.create(309 model: "gpt-6-astra",310 input: "List open orders for customer CUST-12345.",311 parallel_tool_calls: false,312 tools: [313 {314 type: :namespace,315 name: "crm",316 description: "CRM tools for customer lookup and order management.",317 tools: [318 {319 type: :function,320 name: "get_customer_profile",321 description: "Fetch a customer profile by customer ID.",322 parameters: parameters323 },324 {325 type: :function,326 name: "list_open_orders",327 description: "List open orders for a customer ID.",328 defer_loading: true,329 parameters: parameters330 }331 ]332 },333 { type: :tool_search }334 ]335)336337puts(response.output)338```339340341If the model decides it needs a deferred tool, the response includes two additional output items before the eventual function call:342343- `tool_search_call`, which records the hosted search step.344- `tool_search_output`, which contains the loaded subset that becomes callable.345346Hosted tool search response347348```json349[350 {351 // highlight-start:subtle352 "type": "tool_search_call",353 // highlight-end354 "execution": "server",355 "call_id": null,356 "status": "completed",357 "arguments": {358 "paths": ["crm"]359 }360 },361 {362 // highlight-start:subtle363 "type": "tool_search_output",364 // highlight-end365 "execution": "server",366 "call_id": null,367 "status": "completed",368 "tools": [369 {370 "type": "namespace",371 "name": "crm",372 "description": "CRM tools for customer lookup and order management.",373 "tools": [374 {375 "type": "function",376 "name": "list_open_orders",377 "description": "List open orders for a customer ID.",378 "defer_loading": true,379 "parameters": {380 "type": "object",381 "properties": {382 "customer_id": { "type": "string" }383 },384 "required": ["customer_id"],385 "additionalProperties": false386 }387 }388 ]389 }390 ]391 },392 {393 "type": "function_call",394 "name": "list_open_orders",395 "namespace": "crm",396 "call_id": "call_abc123",397 "arguments": "{\"customer_id\":\"CUST-12345\"}"398 }399]400```401402403In hosted mode, `execution` is set to `server` and `call_id` is set to `null`.404405For more complex tasks, the model can also load multiple namespaces or MCP servers in the same `tool_search_call`. For example, if it needs functions from different namespaces to complete one task, it may choose to search and load those surfaces together before making the subsequent function calls.406407## Client-executed tool search408409Client-executed tool search gives your application full control over how tool discovery works. This is useful when the available tools depend on information that is not practical to declare in the initial `tools` list.410411Configure the `tool_search` tool with `execution: "client"` and a schema for the search arguments your application expects:412413Configure client-executed tool search414415```javascript416import OpenAI from "openai";417import { toResponseInputItems } from "openai/lib/responses/ResponseInputItems";418419const client = new OpenAI();420421const firstResponse = await client.responses.create({422 model: "gpt-6-astra",423 input: "Find the shipping ETA tool first, then use it for order_42.",424 tools: [425 {426 type: "tool_search",427 // highlight-start:subtle428 execution: "client",429 // highlight-end430 description:431 "Find the project-specific tools needed to continue the task.",432 parameters: {433 type: "object",434 properties: {435 goal: { type: "string" },436 },437 required: ["goal"],438 additionalProperties: false,439 },440 },441 ],442 parallel_tool_calls: false,443});444445const searchCall = firstResponse.output.find(446 (item) => item.type === "tool_search_call"447);448449if (!searchCall) {450 throw new Error("The response did not include a tool search call.");451}452453const loadedTools = [454 {455 type: "function",456 name: "get_shipping_eta",457 description: "Look up shipping ETA details for an order.",458 defer_loading: true,459 parameters: {460 type: "object",461 properties: {462 order_id: { type: "string" },463 },464 required: ["order_id"],465 additionalProperties: false,466 },467 strict: true,468 },469];470471const searchOutput = {472 type: "tool_search_output",473 execution: "client",474 call_id: searchCall.call_id,475 status: "completed",476 tools: loadedTools,477};478479const secondResponse = await client.responses.create({480 model: "gpt-6-astra",481 input: [482 ...toResponseInputItems(firstResponse.output),483 // highlight-start:subtle484 searchOutput,485 // highlight-end486 ],487});488489console.log(secondResponse.output);490```491492```python493from openai import OpenAI494495client = OpenAI()496497first_response = client.responses.create(498 model="gpt-6-astra",499 input="Find the shipping ETA tool first, then use it for order_42.",500 tools=[501 {502 "type": "tool_search",503 # highlight-start:subtle504 "execution": "client",505 # highlight-end506 "description": "Find the project-specific tools needed to continue the task.",507 "parameters": {508 "type": "object",509 "properties": {510 "goal": {"type": "string"},511 },512 "required": ["goal"],513 "additionalProperties": False,514 },515 }516 ],517 parallel_tool_calls=False,518)519520search_call = next(521 item for item in first_response.output if item.type == "tool_search_call"522)523524loaded_tools = [525 {526 "type": "function",527 "name": "get_shipping_eta",528 "description": "Look up shipping ETA details for an order.",529 "defer_loading": True,530 "parameters": {531 "type": "object",532 "properties": {533 "order_id": {"type": "string"},534 },535 "required": ["order_id"],536 "additionalProperties": False,537 },538 }539]540541second_response = client.responses.create(542 model="gpt-6-astra",543 input=[544 *first_response.output,545 {546 # highlight-start:subtle547 "type": "tool_search_output",548 # highlight-end549 "execution": "client",550 "call_id": search_call.call_id,551 "status": "completed",552 # highlight-start:subtle553 "tools": loaded_tools,554 # highlight-end555 },556 ],557)558559print(second_response.output)560```561562```go563package main564565import (566 "context"567 "fmt"568569 "github.com/openai/openai-go/v3"570 "github.com/openai/openai-go/v3/responses"571)572573func main() {574 client := openai.NewClient()575 searchTool := responses.ToolUnionParam{OfToolSearch: &responses.ToolSearchToolParam{576 Execution: responses.ToolSearchToolExecutionClient,577 Description: openai.String("Find the project-specific tools needed to continue the task."),578 Parameters: map[string]any{579 "type": "object",580 "properties": map[string]any{"goal": map[string]any{"type": "string"}},581 "required": []string{"goal"},582 "additionalProperties": false,583 },584 }}585 first, err := client.Responses.New(context.Background(), responses.ResponseNewParams{586 Model: "gpt-6-astra",587 Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Find the shipping ETA tool first, then use it for order_42.")},588 Tools: []responses.ToolUnionParam{searchTool},589 ParallelToolCalls: openai.Bool(false),590 })591 if err != nil {592 panic(err)593 }594595 callID := ""596 for _, item := range first.Output {597 if item.Type == "tool_search_call" {598 callID = item.CallID599 break600 }601 }602 if callID == "" {603 panic("the response did not include a tool search call")604 }605 loadedTool := responses.ToolParamOfFunction("get_shipping_eta", map[string]any{606 "type": "object",607 "properties": map[string]any{"order_id": map[string]any{"type": "string"}},608 "required": []string{"order_id"},609 "additionalProperties": false,610 }, true)611 loadedTool.OfFunction.Description = openai.String("Look up shipping ETA details for an order.")612 loadedTool.OfFunction.DeferLoading = openai.Bool(true)613 searchOutput := responses.ResponseInputItemParamOfToolSearchOutput([]responses.ToolUnionParam{loadedTool})614 searchOutput.OfToolSearchOutput.CallID = openai.String(callID)615 searchOutput.OfToolSearchOutput.Execution = responses.ResponseToolSearchOutputItemParamExecutionClient616 searchOutput.OfToolSearchOutput.Status = responses.ResponseToolSearchOutputItemParamStatusCompleted617618 second, err := client.Responses.New(context.Background(), responses.ResponseNewParams{619 Model: "gpt-6-astra",620 PreviousResponseID: openai.String(first.ID),621 Input: responses.ResponseNewParamsInputUnion{OfInputItemList: responses.ResponseInputParam{searchOutput}},622 })623 if err != nil {624 panic(err)625 }626 fmt.Println(second.Output)627}628```629630```java631import com.openai.client.OpenAIClient;632import com.openai.client.okhttp.OpenAIOkHttpClient;633import com.openai.core.JsonValue;634import com.openai.models.responses.FunctionTool;635import com.openai.models.responses.ResponseCreateParams;636import com.openai.models.responses.ResponseInputItem;637import com.openai.models.responses.ResponseToolSearchOutputItemParam;638import com.openai.models.responses.ToolSearchTool;639import java.util.List;640import java.util.Map;641642ResponseCreateParams searchRequest =643 ResponseCreateParams.builder()644 .model("gpt-6-astra")645 .input("Find the shipping ETA tool, then use it for order_42.")646 .parallelToolCalls(false)647 .addTool(648 ToolSearchTool.builder()649 .execution(ToolSearchTool.Execution.CLIENT)650 .description("Find the project tools needed to continue the task.")651 .parameters(652 JsonValue.from(653 Map.of(654 "type",655 "object",656 "properties",657 Map.of("goal", Map.of("type", "string")),658 "required",659 List.of("goal"),660 "additionalProperties",661 false)))662 .build())663 .build();664665var search = client.responses().create(searchRequest);666var searchCall =667 search.output().stream()668 .flatMap(item -> item.toolSearchCall().stream())669 .findFirst()670 .orElseThrow(() -> new IllegalStateException("No tool search call returned"));671672FunctionTool shippingTool =673 FunctionTool.builder()674 .name("get_shipping_eta")675 .description("Look up shipping details for an order.")676 .deferLoading(true)677 .strict(true)678 .parameters(679 FunctionTool.Parameters.builder()680 .putAdditionalProperty("type", JsonValue.from("object"))681 .putAdditionalProperty(682 "properties", JsonValue.from(Map.of("order_id", Map.of("type", "string"))))683 .putAdditionalProperty("required", JsonValue.from(List.of("order_id")))684 .putAdditionalProperty("additionalProperties", JsonValue.from(false))685 .build())686 .build();687688var searchOutput =689 ResponseToolSearchOutputItemParam.builder()690 .callId(searchCall.callId().orElseThrow())691 .execution(ResponseToolSearchOutputItemParam.Execution.CLIENT)692 .status(ResponseToolSearchOutputItemParam.Status.COMPLETED)693 .addTool(shippingTool)694 .build();695696var response =697 client698 .responses()699 .create(700 ResponseCreateParams.builder()701 .model("gpt-6-astra")702 .previousResponseId(search.id())703 .inputOfResponse(List.of(ResponseInputItem.ofToolSearchOutput(searchOutput)))704 .build());705706var loadedCalls =707 response.output().stream().flatMap(item -> item.functionCall().stream()).toList();708if (loadedCalls.isEmpty()) {709 throw new IllegalStateException("No loaded function call returned");710}711loadedCalls.forEach(call -> System.out.println(call.name() + "(" + call.arguments() + ")"));712```713714```ruby715require "openai"716717client = OpenAI::Client.new718search = client.responses.create(719 model: "gpt-6-astra",720 input: "Find the shipping ETA tool, then use it for order_42.",721 parallel_tool_calls: false,722 tools: [723 {724 type: :tool_search,725 execution: :client,726 description: "Find the project tools needed to continue the task.",727 parameters: {728 type: :object,729 properties: { goal: { type: :string } },730 required: ["goal"],731 additionalProperties: false732 }733 }734 ]735)736call = search.output.find do |item|737 item.is_a?(OpenAI::Models::Responses::ResponseToolSearchCall)738end739unless call.is_a?(OpenAI::Models::Responses::ResponseToolSearchCall)740 raise "No tool search call returned"741end742743response = client.responses.create(744 model: "gpt-6-astra",745 previous_response_id: search.id,746 input: [747 {748 type: :tool_search_output,749 call_id: call.call_id,750 execution: :client,751 status: :completed,752 tools: [753 {754 type: :function,755 name: "get_shipping_eta",756 description: "Look up shipping details for an order.",757 defer_loading: true,758 strict: true,759 parameters: {760 type: :object,761 properties: { order_id: { type: :string } },762 required: ["order_id"],763 additionalProperties: false764 }765 }766 ]767 }768 ]769)770771function_calls = response.output.grep(772 OpenAI::Models::Responses::ResponseFunctionToolCall773)774raise "No loaded function call returned" if function_calls.empty?775776function_calls.each do |function_call|777 puts("#{function_call.name}(#{function_call.arguments})")778end779```780781782On the first turn, the model emits a `tool_search_call` and stops there:783784Client tool search call785786```json787[788 {789 "type": "tool_search_call",790 "execution": "client",791 "call_id": "call_abc123",792 "status": "completed",793 "arguments": {794 "goal": "Find the shipping ETA tool for order_42."795 }796 }797]798```799800801Your application then performs the search and returns a `tool_search_output` with the tools it wants to load:802803Return tool_search_output804805```json806[807 {808 "type": "tool_search_output",809 "execution": "client",810 "call_id": "call_abc123",811 "status": "completed",812 "tools": [813 {814 "type": "function",815 "name": "get_shipping_eta",816 "description": "Look up shipping ETA details for an order.",817 "defer_loading": true,818 "parameters": {819 "type": "object",820 "properties": {821 "order_id": { "type": "string" }822 },823 "required": ["order_id"],824 "additionalProperties": false825 }826 }827 ]828 }829]830```831832833On the next turn, the loaded tool is callable like a normal function:834835Loaded function call836837```json838[839 {840 "type": "function_call",841 "name": "get_shipping_eta",842 "namespace": "get_shipping_eta",843 "call_id": "call_xyz456",844 "arguments": "{\"order_id\":\"order_42\"}"845 }846]847```848849850In client mode, `execution` is set to `client` and `call_id` is defined. Echo the same `call_id` from the `tool_search_call` in your `tool_search_output`.851852## Advanced usage853854### Keep namespace descriptions clear855856Make namespace descriptions clear and descriptive of the use case, because the model relies on this description to decide when to load a subset of functions in that namespace. Avoid overly long descriptions. Instead, put richer detail in the deferred function descriptions that are loaded only when needed.857858### Understand what gets loaded859860`tool_search_output.tools` contains the list of tools that were dynamically loaded by the model. The model will be able to call any of these tools in future turns, so in client mode you do not need to load the same tool again across turns. Tools that were not listed as part of this array will not be available to the model. If you want to disable a loaded tool, you can remove it from the `tool_search_output` item where you define the loaded tool set, but note that changing the loaded tool set will break the model's cache from that point forward.861862### Advanced injection patterns863864Most integrations declare tools in the request's `tools` parameter. Client-executed tool search also supports more advanced patterns where your application returns tools that were not present in the original request. Treat this as an advanced workflow: validate the returned schemas carefully and only expose trusted tool definitions.865866### Tool search and caching867868All tools are loaded at the end of the model's context window. This holds true for both hosted tool search and client-executed tool search. This allows the model's cache to be preserved from one request to another, lowering overall costs and boosting speed.869870### Add tools at a specific point in the input871872For advanced workflows, you can use an `additional_tools` input item to make tools available at a specific point in the conversation. This is useful when your application loads tools outside the normal tool search flow or needs to preserve the ordering of tools added during a previous response.873874Set `role` to `developer` and include the tools to add in the item's `tools` array:875876```json877{878 "type": "additional_tools",879 "role": "developer",880 "tools": [881 {882 "type": "function",883 "name": "get_customer",884 "description": "Look up a customer by ID.",885 "parameters": {886 "type": "object",887 "properties": {888 "customer_id": { "type": "string" }889 },890 "required": ["customer_id"],891 "additionalProperties": false892 }893 }894 ]895 }896```897898899Tools in an `additional_tools` item become available only after that item appears in the input. When you manually round-trip conversation items, preserve the item's position so the model sees the same tools at the same point in the conversation.900901## Agents API902903The [Agents API](https://developers.openai.com/api/docs/guides/agents-api/overview) loads function definitions eagerly by default. To defer selected functions, include `{ "type": "tool_search" }` in `agent.tools` and set `defer_loading: true` on each function you want the agent to discover on demand. Adding `tool_search` does not defer every function.904905Your session request still supplies the full function definition, including its name, description, and argument schema. Tool search changes when that definition reaches the model. After discovery, your application handles the function call and returns its result as usual. See [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions) for result handling.906907Set `OPENAI_API_KEY` before running this example:908909Load function tools only when needed910911```javascript912import OpenAI from "openai";913const client = new OpenAI();914915const result = await client.beta.agents.sessions.create({916 agent: {917 model: "gpt-6-astra",918 tools: [919 {920 type: "tool_search",921 },922 {923 type: "function",924 name: "lookup_account",925 description: "Find an account by its account number.",926 parameters: {927 type: "object",928 properties: {929 account_id: {930 type: "string",931 },932 },933 required: ["account_id"],934 additionalProperties: false,935 },936 defer_loading: true,937 },938 ],939 },940 environment: {941 type: "none",942 },943 input: [944 {945 role: "user",946 content: [947 {948 type: "input_text",949 text: "Look up account 42.",950 },951 ],952 },953 ],954});955console.log(result.id);956```957958```python959from openai import OpenAI960961client = OpenAI()962963result = client.beta.agents.sessions.create(964 agent={965 "model": "gpt-6-astra",966 "tools": [967 {"type": "tool_search"},968 {969 "type": "function",970 "name": "lookup_account",971 "description": "Find an account by its account number.",972 "parameters": {973 "type": "object",974 "properties": {"account_id": {"type": "string"}},975 "required": ["account_id"],976 "additionalProperties": False,977 },978 "defer_loading": True,979 },980 ],981 },982 environment={"type": "none"},983 input=[984 {985 "role": "user",986 "content": [{"type": "input_text", "text": "Look up account 42."}],987 }988 ],989)990print(result.id)991```992993```go994import (995 "context"996 "fmt"997998 "github.com/openai/openai-go/v3"999)10001001ctx := context.Background()1002client := openai.NewClient()1003result, err := client.Beta.Agents.Sessions.New(ctx,1004 openai.BetaAgentSessionNewParams{1005 Agent: openai.BetaAgentSessionNewParamsAgent{1006 Model: openai.String("gpt-6-astra"),1007 Tools: []openai.AgentToolParamUnion{1008 {OfParamToolSearch: &openai.AgentToolParamToolSearch{}},1009 {1010 OfParamFunction: &openai.AgentToolParamFunction{1011 Name: "lookup_account",1012 Description: "Find an account by its account number.",1013 Parameters: map[string]any{1014 "type": "object",1015 "properties": map[string]any{"account_id": map[string]any{"type": "string"}},1016 "required": []any{"account_id"},1017 "additionalProperties": false,1018 },1019 DeferLoading: openai.Bool(true),1020 },1021 },1022 },1023 },1024 Environment: openai.EnvironmentParamUnion{OfParamNone: &openai.EnvironmentParamNone{}},1025 Input: openai.BetaAgentSessionNewParamsInputUnion{1026 OfArrayOfInputMessages: []openai.AgentSessionInputMessageParam{1027 {1028 Content: []openai.InputContentParamUnion{1029 {OfParamInputText: &openai.InputContentParamInputText{Text: "Look up account 42."}},1030 },1031 },1032 },1033 },1034 })1035if err != nil {1036 panic(err)1037}1038fmt.Println(result.ID)1039```10401041```java1042import com.openai.client.OpenAIClient;1043import com.openai.client.okhttp.OpenAIOkHttpClient;1044import com.openai.core.JsonValue;1045import com.openai.models.beta.agents.AgentToolParam;1046import com.openai.models.beta.agents.sessions.SessionCreateParams;1047import java.util.List;1048import java.util.Map;10491050OpenAIClient client = OpenAIOkHttpClient.fromEnv();1051var result =1052 client1053 .beta()1054 .agents()1055 .sessions()1056 .create(1057 SessionCreateParams.builder()1058 .agent(1059 SessionCreateParams.Agent.builder()1060 .model("gpt-6-astra")1061 .addToolToolSearch()1062 .addTool(1063 AgentToolParam.Function.builder()1064 .name("lookup_account")1065 .description("Find an account by its account number.")1066 .parameters(1067 AgentToolParam.Function.Parameters.builder()1068 .putAdditionalProperty("type", JsonValue.from("object"))1069 .putAdditionalProperty(1070 "properties",1071 JsonValue.from(1072 Map.of("account_id", Map.of("type", "string"))))1073 .putAdditionalProperty(1074 "required", JsonValue.from(List.of("account_id")))1075 .putAdditionalProperty(1076 "additionalProperties", JsonValue.from(false))1077 .build())1078 .deferLoading(true)1079 .build())1080 .build())1081 .environmentNone()1082 .input("Look up account 42.")1083 .build());1084System.out.println(result.id());1085```10861087```ruby1088require "openai"10891090client = OpenAI::Client.new1091result = client.beta.agents.sessions.create(1092 agent: {1093 model: "gpt-6-astra",1094 tools: [1095 { type: "tool_search" },1096 {1097 type: "function",1098 name: "lookup_account",1099 description: "Find an account by its account number.",1100 parameters: {1101 type: "object",1102 properties: { account_id: { type: "string" } },1103 required: ["account_id"],1104 additionalProperties: false1105 },1106 defer_loading: true1107 }1108 ]1109 },1110 environment: { type: "none" },1111 input: [1112 {1113 role: "user",1114 content: [1115 {1116 type: "input_text",1117 text: "Look up account 42."1118 }1119 ]1120 }1121 ]1122)1123puts result.id1124```112511261127### Choose a function loading strategy11281129| Strategy | Configuration | Useful for | Tradeoff |1130| ---------------- | ---------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |1131| Eager loading | Omit `defer_loading` or set it to `false`. | A small set of functions, or functions needed for most tasks. | Unused definitions occupy context. Changing a definition can invalidate a cached prefix. |1132| Deferred loading | Set `defer_loading: true` and include `tool_search`. | A large catalog where each task needs only a few functions. | Discovery adds a step and depends on finding the relevant tool. |11331134Mixing eager and deferred functions in an Agents API session is supported but generally not recommended. Give deferred functions clear names and descriptions. Compare task completion, input-token usage, and latency with representative requests before choosing a default.11351136### MCP and plugin tools11371138MCP tools use automatic discovery in the Agents API when the model and provider support tool search. The runtime defers MCP tools and adds tool search when searchable deferred tools are available. This applies to remote MCPs, executor MCPs, and MCP tools supplied by plugins.11391140You do not need to add `{ "type": "tool_search" }` just for MCP tools or set a function-level `defer_loading` flag on an MCP server. Configure the server using [MCP connections](https://developers.openai.com/api/docs/guides/agents-api/tools/mcp). The Responses API configuration earlier in this guide does not apply to Agents API MCP servers.11411142## Related guides11431144- Use [function calling](https://developers.openai.com/api/docs/guides/function-calling) to define callable functions and custom tools.1145- Use [Using tools](https://developers.openai.com/api/docs/guides/tools) for the broader tool landscape across Responses.