Google ADK
Google Agent Development Kit builds Gemini agents in Python. Point its model at a Gemini LLM Registry entry and it runs under the gateway's policies, and an Agent Persona's MCP tools can be attached to the same agent.
Install
pip install google-adk
Which setup do you need?
| What you get | Use it when | |
|---|---|---|
| LLM Registry entry | The model, governed | You only need model access |
| Agent Persona | The model and the persona's MCP tools | You want the agent to use tools as well |
Which credentials you send depends on the entry's credential mode. If you haven't read which credentials your entry needs, start there.
Configure the endpoint through client_kwargs, not http_options. Gemini(...) accepts an http_options argument without complaining and then ignores it, so your headers are dropped and requests go straight to Google. There is no error and no warning, and the gateway is bypassed entirely. Every snippet below sets http_options inside client_kwargs, which is the supported hook.
Connect to an LLM Registry entry
Pick the credential mode on the entry's Provider Credential panel.
- API Key
- Passthrough (Agent Access Key Required)
- Passthrough (No Agent Access Key)
The gateway stores the Gemini credential, so no Gemini key leaves your machine. api_key carries your Agent Access Key instead.
from google.adk.agents import LlmAgent
from google.adk.models.google_llm import Gemini
from google.genai import types
BASE_URL = "https://<gateway-host>/llm/<url-prefix>"
AGENT_KEY = "<your-agent-access-key>"
agent = LlmAgent(
name="gateway_agent",
model=Gemini(
model="<model-id>",
base_url=BASE_URL,
client_kwargs={
"api_key": AGENT_KEY,
"http_options": types.HttpOptions(base_url=BASE_URL),
},
),
instruction="Answer concisely.",
)
Two credentials travel on every call: your own Gemini key and the Agent Access Key.
from google.adk.agents import LlmAgent
from google.adk.models.google_llm import Gemini
from google.genai import types
BASE_URL = "https://<gateway-host>/llm/<url-prefix>"
AGENT_KEY = "<your-agent-access-key>"
GEMINI_KEY = "<your-gemini-api-key>"
agent = LlmAgent(
name="gateway_agent",
model=Gemini(
model="<model-id>",
base_url=BASE_URL,
client_kwargs={
"api_key": GEMINI_KEY,
"http_options": types.HttpOptions(
base_url=BASE_URL,
headers={
"X-Agent-Key": AGENT_KEY,
"Authorization": f"Bearer {GEMINI_KEY}",
},
),
},
),
instruction="Answer concisely.",
)
What each part does:
X-Agent-Keycarries the Agent Access Key, which identifies you to the gateway. On a passthrough entry this header is where the gateway looks for it.Authorization: Bearercarries your Gemini key. This is the header the gateway's Gemini route honours; sending the key asx-goog-api-keyinstead returns a missing-Authorization error.api_keyis set as well because the underlying client refuses to construct without a credential. Supplying both is harmless.
With Require an agent access key off, only your Gemini key travels.
from google.adk.agents import LlmAgent
from google.adk.models.google_llm import Gemini
from google.genai import types
BASE_URL = "https://<gateway-host>/llm/<url-prefix>"
GEMINI_KEY = "<your-gemini-api-key>"
agent = LlmAgent(
name="gateway_agent",
model=Gemini(
model="<model-id>",
base_url=BASE_URL,
client_kwargs={
"api_key": GEMINI_KEY,
"http_options": types.HttpOptions(
base_url=BASE_URL,
headers={"Authorization": f"Bearer {GEMINI_KEY}"},
),
},
),
instruction="Answer concisely.",
)
Connect through an Agent Persona
ADK opens both connections from your own process, so the model and the tools are two independent legs. Each is configured separately on the same agent:
- The model route, at
https://<gateway-host>/p/<persona-id>/llm/<registry-entry-id>, on theGeminimodel. - The tools socket, at
https://<gateway-host>/p/<persona-id>/mcp, on anMcpToolset.
One Agent Access Key covers both. The tools leg takes only X-Agent-Key and no provider credential, because the gateway attaches outbound authentication to the upstream API itself.
Two separate settings are in play here, and only one of them changes the code below.
- The entry's credential mode — API Key or Passthrough — decides which credentials you send. This is what the tabs below switch between, exactly as on the route above.
- The persona's own Inbound Authentication method — Interactive or Passthrough — decides how the persona identifies you. Either way an Agent Access Key is issued, and neither changes the configuration below.
Because a persona always identifies you by an Agent Access Key, the entry's third mode — Passthrough (No Agent Access Key) — has no tab here. It applies to the LLM Registry entry route only.
- API Key
- Passthrough (Agent Access Key Required)
from google.adk.agents import LlmAgent
from google.adk.models.google_llm import Gemini
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
from google.genai import types
BASE_URL = "https://<gateway-host>/p/<persona-id>/llm/<registry-entry-id>"
MCP_URL = "https://<gateway-host>/p/<persona-id>/mcp"
AGENT_KEY = "<your-agent-access-key>"
agent = LlmAgent(
name="persona_agent",
model=Gemini(
model="<model-id>",
base_url=BASE_URL,
client_kwargs={
"api_key": AGENT_KEY,
"http_options": types.HttpOptions(base_url=BASE_URL),
},
),
instruction="Answer questions using the available tools.",
tools=[
McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=MCP_URL,
headers={"X-Agent-Key": AGENT_KEY},
)
)
],
)
from google.adk.agents import LlmAgent
from google.adk.models.google_llm import Gemini
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
from google.genai import types
BASE_URL = "https://<gateway-host>/p/<persona-id>/llm/<registry-entry-id>"
MCP_URL = "https://<gateway-host>/p/<persona-id>/mcp"
AGENT_KEY = "<your-agent-access-key>"
GEMINI_KEY = "<your-gemini-api-key>"
agent = LlmAgent(
name="persona_agent",
model=Gemini(
model="<model-id>",
base_url=BASE_URL,
client_kwargs={
"api_key": GEMINI_KEY,
"http_options": types.HttpOptions(
base_url=BASE_URL,
headers={
"X-Agent-Key": AGENT_KEY,
"Authorization": f"Bearer {GEMINI_KEY}",
},
),
},
),
instruction="Answer questions using the available tools.",
tools=[
McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=MCP_URL,
headers={"X-Agent-Key": AGENT_KEY},
)
)
],
)
Note that the tools leg carries only X-Agent-Key, even though the model leg needs both credentials.
Troubleshooting
Calls succeed but never appear in gateway activity
Your headers were dropped. Check that http_options sits inside client_kwargs. Passing http_options directly to Gemini(...) is accepted silently and ignored, and the requests go to Google rather than through the gateway.
400 Missing or invalid Authorization header
The Gemini key was sent as x-goog-api-key. It needs to be an Authorization: Bearer header, as in the snippets above.
404 ... no longer available to new users
The model identifier isn't available on your account. gemini-flash-latest is a good one to test with. Confirm your choice with a real call rather than assuming from the Allowed Models list.
Requests fail with 400 "model is not specified"
The Gemini model exposes a use_interactions_api flag. Leave it off; that path does not currently work through the gateway.
Where to find each value
| Placeholder | Where to find it |
|---|---|
<gateway-host> | Host from the entry's Overview tab, for example gw.aigateway.example.com |
<url-prefix> | The URL prefix field on the entry's Overview tab |
<persona-id> | The persona's ID on the Agent Persona page |
<registry-entry-id> | The LLM Registry entry's ID in the entry's URL |
<model-id> | A model identifier from the entry's Allowed Models list |
<your-agent-access-key> | Generate from the entry's Agent Access Keys step, or the persona's Connect flow. Plaintext is shown once. |
<your-gemini-api-key> | Your own Google Gemini API key, needed only in the passthrough modes |
Tips
- Keep credentials out of your source. The snippets inline the values so the shape is clear, but read them from environment variables or a secret store in anything you commit.
- Two URLs, two shapes. The model leg ends in
/llm/<registry-entry-id>; the tools leg is the persona root plus/mcp. Copying one into the other is the most common setup mistake here. - Working in a different SDK? There are sibling guides for the Anthropic Python SDK and the OpenAI Python SDK.
Cequence AI Gateway