Skip to content

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

@hsuyuming

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.
import os
import json
from google.auth import default
from google.auth.transport.requests import Request
from google.cloud import secretmanager
from google.adk.tools.apihub_tool.apihub_toolset import APIHubToolset
from google.adk.agents.llm_agent import LlmAgent
from google.adk.auth.auth_credential import AuthCredential, AuthCredentialTypes, HttpAuth, HttpCredentials
from google.adk.tools.openapi_tool.auth.auth_helpers import token_to_scheme_credential
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset
import asyncio

OPENAPI_SPEC_FILENAME="hello_world_openapi.json"
TOKEN="secret-key-1"

with open(os.path.join(os.path.dirname(__file__), OPENAPI_SPEC_FILENAME), 'r') as f:
    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
)



async def main():
    tools = await smorch_toolset.get_tools()
    for tool in tools:
        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
import os
from google.adk.agents.llm_agent import LlmAgent
from google.adk.tools.openapi_tool.auth.auth_helpers import token_to_scheme_credential
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset


OPENAPI_SPEC_FILENAME="hello_world_openapi.json"
TOKEN="secret-key-1"

with open(os.path.join(os.path.dirname(__file__), OPENAPI_SPEC_FILENAME), 'r') as f:
    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:

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import APIKeyHeader

api_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"

async def get_api_keys_1(
    key1: str = Depends(api_key_header_1),
    key2: str = Depends(api_key_header_2),
):
    if not key1 or key1 != SECRET_KEY_1:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or missing API Key 1",
        )
    if not key2 or key2 != SECRET_KEY_2:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or missing API Key 2",
        )
    return key1, key2

app = FastAPI()

@app.get("/helloworld", dependencies=[Depends(get_api_keys_1)])
async def helloworld():
    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

bot triaged[Bot] This issue is triaged by ADK bottools[Component] This issue is related to tools

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions