OpenAI Python SDK
The OpenAI Python SDK takes a base_url on the client constructor, so pointing it at an LLM Registry entry needs no wrapper. It can also send custom headers, which is how the Agent Access Key travels when an entry requires one.
base_url must end in /v1, on both routes below. The SDK appends only the operation path, so the version segment has to come from the base URL. This applies to every OpenAI-compatible provider (OpenAI, OpenRouter, Kimi, Groq, Mistral). Gemini is the exception and takes no /v1; for Gemini agents see the Google ADK guide.
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, in the same request | You want the model to call 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.
Connect to an LLM Registry entry
Pick the credential mode on the entry's Provider Credential panel. All three are verified working against an OpenAI entry.
- API Key
- Passthrough (Agent Access Key Required)
- Passthrough (No Agent Access Key)
The gateway stores the OpenAI credential, so no OpenAI key leaves your machine. api_key carries your Agent Access Key instead.
from openai import OpenAI
client = OpenAI(
base_url="https://<gateway-host>/llm/<url-prefix>/v1",
api_key="<your-agent-access-key>"
)
response = client.chat.completions.create(
model="<model-id>",
messages=[{"role": "user", "content": "Hello"}]
)
print(response)
The SDK sends api_key as Authorization: Bearer, which the gateway accepts as your credential on this mode. It then attaches the stored OpenAI credential on the outbound side.
Two credentials travel on every call, in two separate places.
from openai import OpenAI
client = OpenAI(
base_url="https://<gateway-host>/llm/<url-prefix>/v1",
api_key="<your-openai-api-key>",
default_headers={"X-Agent-Key": "<your-agent-access-key>"}
)
response = client.chat.completions.create(
model="<model-id>",
messages=[{"role": "user", "content": "Hello"}]
)
print(response)
What each argument does:
api_keycarries your own OpenAI key, which the gateway forwards to OpenAI unchanged. The SDK sends it asAuthorization: Bearer.default_headerscarries the Agent Access Key onX-Agent-Key, which identifies you to the gateway. It applies to every request the client makes.
Without a custom header
If setting a header is awkward in your setup, both credentials can travel in api_key instead, combined provider key first and separated by two colons:
client = OpenAI(
base_url="https://<gateway-host>/llm/<url-prefix>/v1",
api_key="<your-openai-api-key>::<your-agent-access-key>"
)
The gateway splits the value apart before authenticating, exactly as it does for clients with only one credential field.
With Require an agent access key off, only your OpenAI key travels and no headers are needed.
from openai import OpenAI
client = OpenAI(
base_url="https://<gateway-host>/llm/<url-prefix>/v1",
api_key="<your-openai-api-key>"
)
response = client.chat.completions.create(
model="<model-id>",
messages=[{"role": "user", "content": "Hello"}]
)
print(response)
Connect through an Agent Persona
The SDK reaches a persona differently from a terminal client. Rather than registering an MCP server locally, it declares the persona's tools socket as a tool in the request, and OpenAI connects to that socket so the model can call the persona's tools within the same request.
Three things change from the setups above:
base_urlbecomes the persona-scoped route, ending in/v1as before.- The call moves from
client.chat.completions.createtoclient.responses.create. Remote MCP tools are a Responses API feature, sochat.completionswill not accept them. Note the Responses API takesinputrather thanmessages. - A tool entry of
type: "mcp"points athttps://<gateway-host>/p/<persona-id>.
One Agent Access Key covers both. It authenticates the model route and, in the tool's headers, the tools socket.
Because OpenAI's servers open the connection to the tools socket, the persona endpoint has to be reachable from the public internet. This differs from Codex CLI or Gemini CLI, where your own machine connects to it.
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 openai import OpenAI
client = OpenAI(
base_url="https://<gateway-host>/p/<persona-id>/llm/<registry-entry-id>/v1",
api_key="<your-agent-access-key>"
)
response = client.responses.create(
model="<model-id>",
input="Hello",
tools=[{
"type": "mcp",
"server_label": "<persona-name>",
"server_url": "https://<gateway-host>/p/<persona-id>",
"headers": {"X-Agent-Key": "<your-agent-access-key>"},
"require_approval": "never",
}],
)
print(response)
The Agent Access Key appears twice on purpose: in default_headers for the model route, and in the tool's headers for the tools socket.
from openai import OpenAI
client = OpenAI(
base_url="https://<gateway-host>/p/<persona-id>/llm/<registry-entry-id>/v1",
api_key="<your-openai-api-key>",
default_headers={"X-Agent-Key": "<your-agent-access-key>"}
)
response = client.responses.create(
model="<model-id>",
input="Hello",
tools=[{
"type": "mcp",
"server_label": "<persona-name>",
"server_url": "https://<gateway-host>/p/<persona-id>",
"headers": {"X-Agent-Key": "<your-agent-access-key>"},
"require_approval": "never",
}],
)
print(response)
What the tool fields do:
server_urlis the persona root. It takes no/llm/...path and no/v1suffix, unlikebase_url. The model route and the tools socket are different endpoints on the same persona.server_labelis any short label you want to give the tools socket.headerscarries the Agent Access Key onX-Agent-Key, which OpenAI presents when it connects to the socket.require_approval: "never"lets the model call the persona's tools without pausing for per-call confirmation. Leave it out if you'd rather approve each call.
Troubleshooting
The model answers but never calls a tool
Check the server_url. It is the persona root, /p/<persona-id>, with neither the /llm/... path nor the /v1 suffix that base_url needs.
tools is rejected as an unknown parameter
You're on client.chat.completions.create. Remote MCP tools require client.responses.create.
A not-found error rather than an authentication one
Check that base_url ends in /v1. A missing version segment looks like a routing failure, not a credential problem.
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 |
<persona-name> | Any short label you want to give the tools socket in your code |
<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, for example gpt-5-nano |
<your-agent-access-key> | Generate from the entry's Agent Access Keys step, or the persona's Connect flow. Plaintext is shown once. |
<your-openai-api-key> | Your own OpenAI 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.
base_urlis persona-scoped and ends in/v1;server_urlis the bare persona root. Copying one into the other is the most common setup mistake here. - Anthropic entries need the Anthropic SDK. This guide is verified against an OpenAI entry. Pointing the OpenAI SDK at an Anthropic-provider entry is not a supported path today, so use the Anthropic Python SDK for those.
- Working in a different SDK? There are sibling guides for the Anthropic Python SDK and Google ADK.
Cequence AI Gateway