Skip to content

OpenAPIToolset does not support tools (endpoints) that have multiple security definitions. #2228

Description

@hsuyuming

Describe the bug
OpenAPIToolset and APIHubToolset only allow each tool to have a single security schema.
For example:
If your endpoint(tool) required multiple security, then OpenAPIToolset and APIHubToolset will only choose the first one become its auth_scheme [1]

[1] https://github.com/google/adk-python/blob/main/src/google/adk/tools/openapi_tool/openapi_spec_parser/openapi_spec_parser.py#L129-L136

To Reproduce
Steps to reproduce the behavior:

  1. Create python environment
uv venv --python 3.12.0
source .venv/bin/activate
uv pip install google-adk==1.8.0
  1. Use this openapi.json
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"servers": [
{
"url": "http://localhost:8080"
}
],
"paths": {
"/helloworld": {
"get": {
"summary": "Helloworld",
"operationId": "helloworld_helloworld_get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
}
}
}
}
},
"security": [
{
"api1": []
},
{
"api2": []
}
]
}
}
},
"components": {
"securitySchemes": {
"api1": {
"type": "apiKey",
"in": "header",
"name": "x-api-key-1"
},
"api2": {
"type": "apiKey",
"in": "header",
"name": "x-api-key-2"
}
}
}
}
  1. use test_tool.py to testing.
importosimportjsonfromgoogle.authimportdefaultfromgoogle.auth.transport.requestsimportRequestfromgoogle.cloudimportsecretmanagerfromgoogle.adk.tools.apihub_tool.apihub_toolsetimportAPIHubToolsetfromgoogle.adk.agents.llm_agentimportLlmAgentfromgoogle.adk.auth.auth_credentialimportAuthCredential, AuthCredentialTypes, HttpAuth, HttpCredentialsfromgoogle.adk.tools.openapi_tool.auth.auth_helpersimporttoken_to_scheme_credentialfromgoogle.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolsetimportOpenAPIToolsetimportasyncioOPENAPI_SPEC_FILENAME="hello_world_openapi.json"TOKEN="secret-key-1"withopen(os.path.join(os.path.dirname(__file__), OPENAPI_SPEC_FILENAME), 'r') asf:
spec_content=f.read()
auth_scheme, auth_credential=token_to_scheme_credential(
"apikey", "header", "x-api-key-1", TOKEN
)
smorch_toolset=OpenAPIToolset(
spec_str=spec_content,
spec_str_type='json',
auth_scheme=auth_scheme,
auth_credential=auth_credential
)
asyncdefmain():
tools=awaitsmorch_toolset.get_tools()
fortoolintools:
print(tool.auth_scheme)
asyncio.run(main())
  1. tool only have one auth_scheme (x-api-key-1), x-api-key-2 is missing....
(adk_with_different_auth) user@abehsu-us-vscode:~/abehsu/fastapi_with_multiple_security$ python /home/user/abehsu/adk_with_different_auth/api_key/openapi/test_tool.py
type_=<SecuritySchemeType.apiKey: 'apiKey'> description=None in_=<APIKeyIn.header: 'header'> name='x-api-key-1'
  1. The other way you can test it is using this agent.py
importosfromgoogle.adk.agents.llm_agentimportLlmAgentfromgoogle.adk.tools.openapi_tool.auth.auth_helpersimporttoken_to_scheme_credentialfromgoogle.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolsetimportOpenAPIToolsetOPENAPI_SPEC_FILENAME="hello_world_openapi.json"TOKEN="secret-key-1"withopen(os.path.join(os.path.dirname(__file__), OPENAPI_SPEC_FILENAME), 'r') asf:
spec_content=f.read()
auth_scheme, auth_credential=token_to_scheme_credential(
"apikey", "header", "x-api-key-1", TOKEN
)
hello_world_toolset=OpenAPIToolset(
spec_str=spec_content,
spec_str_type='json',
auth_scheme=auth_scheme,
auth_credential=auth_credential
)
# --- Agent Configuration ---# Configure and create the main LLM Agent.root_agent=LlmAgent(
model='gemini-2.0-flash',
name='hello_world_agent',
instruction='when user say hi you will use hello world tool to response them',
tools=[hello_world_toolset],
)

Backend server example code:

fromfastapiimportFastAPI, Depends, HTTPException, statusfromfastapi.securityimportAPIKeyHeaderapi_key_header_1=APIKeyHeader(name="x-api-key-1", auto_error=False, scheme_name="api1")
api_key_header_2=APIKeyHeader(name="x-api-key-2", auto_error=False, scheme_name="api2")
SECRET_KEY_1="secret-key-1"SECRET_KEY_2="secret-key-2"asyncdefget_api_keys_1(
key1: str=Depends(api_key_header_1),
key2: str=Depends(api_key_header_2),
):
ifnotkey1orkey1!=SECRET_KEY_1:
raiseHTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API Key 1",
)
ifnotkey2orkey2!=SECRET_KEY_2:
raiseHTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API Key 2",
)
returnkey1, key2app=FastAPI()
@app.get("/helloworld", dependencies=[Depends(get_api_keys_1)])asyncdefhelloworld():
return {"message": "Hello World"}

Screenshots

Image

Desktop (please complete the following information):

  • OS: Linux
  • Python version(python -V): Python 3.12.10
  • ADK version(pip show google-adk): 1.18.0

Metadata

Metadata

Assignees

Labels

needs review[Status] The PR/issue is awaiting review from the maintainertools[Component] This issue is related to tools

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions