The "Tool-Discovery" Protocol: How Gemini 3 Agents Find and Map New Capabilities
Build truly autonomous, self-expanding AI applications using the Tool-Discovery Protocol. Learn how Gemini 3 agents dynamically browse, map, and bind new API capabilities on the fly.
Published on • 2026-06-01
AI Assistant

Most modern AI agents are built with static tools. A developer registers a set of functions (e.g., send_email, read_database) at startup, and the agent uses them. However, in complex enterprise environments, requirements change rapidly. If an agent needs to perform a new action, developers must write new code, register the tool, and redeploy the app.
With Gemini 3, we can move past static setups using the Tool-Discovery Protocol. Instead of hardcoding capabilities, we equip the agent with “meta-tools” that allow it to scan directories, parse OpenAPI specifications, or look up MCP (Model Context Protocol) endpoints. The agent dynamically discovers, verifies, maps, and executes new tools on the fly—becoming a self-expanding autonomous engine.
In this tutorial, we will build a Python agent that dynamically reads OpenAPI definitions from a local folder and registers them as usable tools at runtime.
Prerequisites
- Python 3.10+
- Google Gemini API Key configured
urllib3orrequestsfor dynamic API execution
Install the required modules:
pip install google-genai requests pyyaml dotenv
Make sure to create a .env file:
GEMINI_API_KEY=your_gemini_3_api_key
Architecture of Tool-Discovery
1. [Agent receives task]
2. [No suitable tool loaded] -> Trigger [Tool Discovery Meta-Tool]
3. [Scan OpenAPI specs / MCP servers]
4. [Compile new spec into schema]
5. [Hot-bind tool to current session] -> Execute!
Step 1: Writing the Dynamic Tool Discovery Agent
Create a file named dynamic_agent.py. This script sets up a directory containing tool specifications and creates a controller that loads them into the Gemini agent context dynamically.
import os
import json
import yaml
import requests
from dotenv import load_dotenv
from google import genai
from google.genai import types
load_dotenv()
# Initialize Gemini Client
client = genai.Client()
# Create a local registry directory for new tool specs
REGISTRY_DIR = "./tool_registry"
os.makedirs(REGISTRY_DIR, exist_ok=True)
# 1. Define a dummy schema/API spec to discover (e.g. currency converter API)
SAMPLE_SPEC = {
"openapi": "3.0.0",
"info": {"title": "Currency Converter Service", "version": "1.0.0"},
"paths": {
"/convert": {
"get": {
"summary": "Convert values between currencies",
"parameters": [
{"name": "amount", "in": "query", "required": True, "schema": {"type": "number"}},
{"name": "from_currency", "in": "query", "required": True, "schema": {"type": "string"}},
{"name": "to_currency", "in": "query", "required": True, "schema": {"type": "string"}}
]
}
}
}
}
with open(os.path.join(REGISTRY_DIR, "currency_service.json"), "w") as f:
json.dump(SAMPLE_SPEC, f, indent=2)
# 2. Meta-Tool: Let the agent list and discover available specifications
def discover_tools() -> list[str]:
"""Scans the registry folder and lists discovered API tool definitions."""
print("[Discovery Protocol] Scanning registry for new capabilities...")
files = [f for f in os.listdir(REGISTRY_DIR) if f.endswith('.json') or f.endswith('.yaml')]
return [os.path.join(REGISTRY_DIR, f) for f in files]
def read_tool_specification(file_path: str) -> str:
"""Reads a specific API definition file to extract parameters and endpoints."""
print(f"[Discovery Protocol] Reading spec file: {file_path}")
with open(file_path, 'r') as f:
return f.read()
Step 2: Setting up Dynamic Execution
Once the agent selects an API spec, it needs to execute it. We write a generic runtime function that takes the endpoint details and parameters defined in the spec and makes the network request.
Append the following agent controller to dynamic_agent.py:
# 3. Dynamic Execution Runtime
def execute_dynamic_tool(base_url: str, endpoint: str, method: str, params: dict) -> dict:
"""Generic runtime executor for dynamically discovered API tools."""
print(f"[Execution Engine] Invoking {method.upper()} {base_url}{endpoint} with params {params}")
# For testing purposes, we mock the real API response
if "convert" in endpoint:
amount = params.get("amount", 1)
return {"status": "success", "converted_value": float(amount) * 35.2, "currency": "THB"}
return {"status": "error", "message": "Endpoint not implemented"}
def run_agent_loop(user_prompt: str):
# Register the Meta-Tools (discovery tools)
meta_tools = [discover_tools, read_tool_specification, execute_dynamic_tool]
print(f"User Request: '{user_prompt}'")
# Create the agent session
response = client.models.generate_content(
model="gemini-3-flash",
contents=user_prompt,
config=types.GenerateContentConfig(
tools=meta_tools,
system_instruction=(
"You are an adaptive AI agent with self-improving capabilities. "
"If you need to perform an action but do not have the direct tools, "
"call `discover_tools` to find new API specifications, read them with `read_tool_specification`, "
"and then run the target tool using `execute_dynamic_tool`."
)
)
)
# Process the function calls (tool loop)
for function_call in response.function_calls:
print(f"\n[Agent Decision] Executing Tool: {function_call.name}")
# Map and run the matched python function
if function_call.name == "discover_tools":
res = discover_tools()
print(f"[Result] {res}")
elif function_call.name == "read_tool_specification":
res = read_tool_specification(**function_call.args)
print(f"[Result] Retrieved API Spec")
elif function_call.name == "execute_dynamic_tool":
res = execute_dynamic_tool(
base_url="https://api.example.com",
endpoint="/convert",
method="get",
params=function_call.args.get("params", {})
)
print(f"[Result] Response Payload: {res}")
if __name__ == "__main__":
run_agent_loop("Convert 100 USD to THB. You might need to discover what APIs are available in the registry folder first.")
Step 3: Running the Dynamic Agent
Run the script to watch the Tool-Discovery workflow unfold:
python dynamic_agent.py
Protocol Workflow Details
Watch your logs closely:
- Scanning: The agent realizes it has no currency tool, so it calls
discover_toolsto list json files. - Parsing: It parses
currency_service.jsonviaread_tool_specification, discovering the/convertendpoint and its parameters. - Execution: It maps the parameters and runs the dynamic network caller
execute_dynamic_toolwith the correct argument models.
Summary
The Tool-Discovery Protocol allows your AI applications to scale and adapt on their own. Instead of shipping code updates, you simply drop new OpenAPI or Model Context Protocol configurations into a directory, and the agent automatically learns how to use them to solve its goals.