Use connectors

Connectors let an AgentApp expose runtime-provided tools to a model without embedding their implementation or provider credentials in the app.

The examples below use web_search, which is available without connecting an external account.

Give tools to the model

Start by asking the runtime for the tool definitions you want to expose:

tools = agent.connectors.tools(["web_search"])

Then include them in a model request:

response = agent.responses.create(
    {
        "model": "openai/gpt-5.5",
        "input": "Find the latest Flower release and summarize what changed.",
        "tools": tools,
    }
)

tools returns the registered schemas rather than executing anything. A connector can expose several related tools. The model can respond with normal output, one function call, or multiple function calls.

Execute function calls

The AgentApp owns the tool loop. When the model asks to use a connector, your app executes the call and gives the result back to the model. For each output item whose type is function_call, call the connector and send the resulting function_call_output items back to the model:

This loop expects agent.input in the run configuration. Flower records that prompt in context.state before the AgentApp starts:

import json

from flwr.app import Context


def load_context_items(context: Context) -> list[dict[str, object]]:
    """Load the Open Responses items stored by the Flower runtime."""
    record = context.state.get("items")
    if record is None:
        return []
    stored_items = [json.loads(item) for item in record["json"]]
    return [
        item
        for item in stored_items
        if not str(item.get("type", "")).startswith("response.tool_call.")
    ]


tools = agent.connectors.tools(["web_search"])
response = agent.responses.create(
    {
        "model": "openai/gpt-5.5",
        "input": load_context_items(context),
        "tools": tools,
    }
)

tool_turns = 0
while True:
    tool_calls = [
        item
        for item in response.get("output", [])
        if isinstance(item, dict) and item.get("type") == "function_call"
    ]
    if not tool_calls:
        break
    if tool_turns == 5:
        raise RuntimeError("Agent exceeded the connector turn limit")

    for tool_call in tool_calls:
        agent.connectors.call(tool_call)
    response = agent.responses.create(
        {
            "model": "openai/gpt-5.5",
            "input": load_context_items(context),
            "tools": tools,
        }
    )
    tool_turns += 1

agent.connectors.call accepts the function-call item returned by the model. It parses the call arguments, starts the connector task, and stores the output in the Flower Context. The next call to load_context_items includes that output with the same call_id. The helper filters out the connector activity events that Flower also stores for run inspection because those events aren’t valid model input items.

The loop allows at most five connector turns. A limit prevents a model from repeatedly requesting tools without reaching a final response.

Once the model returns no more function calls, the loop ends and response contains the final model response.

Choose the narrowest set of connectors

Only expose the connectors the task needs. This gives the model a smaller, clearer set of tools to choose from.

Handle errors

Connector calls can fail when a provider or target is unavailable. The call raises a RuntimeError; if the app does not catch it, the AgentApp task fails and the error is available in the run details and logs.

Catch an exception only when the app has a useful fallback, for example trying a different source:

try:
    output = agent.connectors.call(tool_call)
except RuntimeError as exc:
    print(f"Connector failed: {exc}")

Do not put secrets in model prompts or connector arguments. The model or connected service receives those values when the tool runs.