Repository navigation
Provider-specific Responses capability filtering for gateways without custom tools / hosted web_search #5002
Description
Client or integration
Codex CLI and Codex App
Provider or upstream service
Agent.Space
OpenCodex version
2.58.0
Endpoint or capability
OpenAI Responses API at https://api.agent.space/v1/responses, specifically Responses custom tools and hosted web_search.
Current behaviour
Agent.Space works for plain Responses requests and standard function tools, but rejects Responses custom tools and hosted web_search with HTTP 400 unsupported_request.
When the provider is routed through OpenCodex, Codex advertises/forwards those capabilities even for a simple text-only prompt. As a result, a request such as Reply exactly with OK fails before the model can answer.
Direct provider tests:
- Plain Responses request -> HTTP 200
- Standard
functiontool -> HTTP 200 customtool such asapply_patch-> HTTP 400unsupported_request- hosted
web_search-> HTTP 400unsupported_request
Setting supportsResponsesCustomTools: false fixes the custom-tool mismatch, but the request still fails because hosted web_search is also sent.
Temporarily running Codex with web_search="disabled" succeeds.
Expected behaviour
OpenCodex should allow provider-specific Responses capability filtering so a routed provider only receives capabilities it supports.
For this provider, standard Responses and function tools should continue to work while unsupported native custom tools and hosted web-search declarations are omitted or downgraded. This should not require disabling web search globally for unrelated providers that support it.
Minimal redacted request or reproduction
# 1. Normal routed Codex request fails:
codex exec \
--skip-git-repo-check \
-m agent-space/gpt-5.6-luna \
"Reply exactly with OK"
# 2. Direct Agent.Space Responses request with a standard function tool succeeds:
curl -sS -w '\nHTTP %{http_code}\n' \
https://api.agent.space/v1/responses \
-H "Authorization: Bearer <redacted>" \
-H "Content-Type: application/json" \
-d '{
"model":"gpt-5.6-luna",
"input":"Reply exactly with OK.",
"tools":[{
"type":"function",
"name":"noop",
"description":"Do nothing",
"parameters":{
"type":"object",
"properties":{},
"additionalProperties":false
}
}]
}'
# => HTTP 200
# 3. Direct custom tool request is rejected:
curl -sS -w '\nHTTP %{http_code}\n' \
https://api.agent.space/v1/responses \
-H "Authorization: Bearer <redacted>" \
-H "Content-Type: application/json" \
-d '{
"model":"gpt-5.6-luna",
"input":"Reply exactly with OK.",
"tools":[{
"type":"custom",
"name":"apply_patch",
"description":"Apply a patch",
"format":{"type":"text"}
}]
}'
# => HTTP 400
# 4. Direct hosted web_search request is rejected:
curl -sS -w '\nHTTP %{http_code}\n' \
https://api.agent.space/v1/responses \
-H "Authorization: Bearer <redacted>" \
-H "Content-Type: application/json" \
-d '{
"model":"gpt-5.6-luna",
"input":"Reply exactly with OK.",
"tools":[{"type":"web_search"}]
}'
# => HTTP 400
# 5. Disabling hosted web search for this Codex invocation succeeds:
codex -c 'web_search="disabled"' exec \
--skip-git-repo-check \
-m agent-space/gpt-5.6-luna \
"Reply exactly with OK"
# => OKActual response or error
ERROR: {
"error": {
"message": "The request uses an invalid or unsupported capability.",
"type": "invalid_request_error",
"param": null,
"code": "unsupported_request"
}
}OpenCodex request history also records the routed request as provider: agent-space, model: gpt-5.6-luna, status 400, with the same upstream error.
Upstream documentation
https://agent.space/developer-api
Agent.Space documents an OpenAI-compatible Responses API and Codex integration, but the tested endpoint currently does not accept native Responses custom tools or hosted web_search for this model.
Suggested mapping or implementation notes
A provider-level capability setting would avoid hard-coded destination rules. For example:
{
"providers": {
"agent-space": {
"adapter": "openai-responses",
"supportsResponsesCustomTools": false,
"unsupportedHostedTools": [
"web_search",
"web_search_preview"
]
}
}
}Then the existing hosted-tool filtering path could consult provider configuration.
As a local workaround, I added a provider-specific rule in src/responses/hosted-tool-policy.ts:
{
match: (_model, baseUrl) =>
baseUrl?.replace(/\/+$/, "") === "https://api.agent.space/v1",
tools: new Set(["web_search", "web_search_preview"]),
},Together with supportsResponsesCustomTools: false, the same Codex CLI request succeeds and Codex App also works with the routed model.
Additional context and attachments
Environment:
- macOS
- Codex CLI 0.153.4
- OpenCodex 2.58.0
- Adapter:
openai-responses - Tested model:
gpt-5.6-luna
The issue is broader than Agent.Space: any OpenAI-compatible Responses gateway with a narrower capability set can hit the same mismatch if unsupported capabilities are advertised/forwarded unconditionally.
Checks
- I searched existing provider and compatibility issues.
- The request and response were redacted.
- The expected behaviour is based on an upstream specification or a concrete client requirement.
Activity
github-actions commentedon Sep 18, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
fix(responses): let a provider declare the hosted tools it rejects (#…
lidge-jun commentedon Sep 18, 2026
Fixed on
devby #5021, squashed as48a23dd9a00e947ce0e714ea46f74e1ea05d56a2.The answer is config-declarative rather than a per-vendor rule, and that was forced by your case: the only existing mechanism was a table of (model, baseUrl) predicates in
src/responses/hosted-tool-policy.ts, so a narrower gateway needed a proxy release naming its endpoint. Agent.Space is not in this repository at all — it is a user-defined provider — so a built-in entry could never have covered it.Providers can now declare
unsupportedHostedTools, andstripUnsupportedHostedToolsconsults that alongside the built-in table acrosstools,additional_toolsandtool_choice. The configuration you suggested in the report works verbatim. The declaration is additive, so it can only deny more and can never re-enable a pairing already known to be broken.Two design calls worth knowing about.
web_searchandweb_search_previeware aliased, because the rest of the proxy already folds that pair into one tool — honouring only the spelling an operator happened to write would have reproduced your 400 while the config claimed otherwise. And the value is validated against a closed vocabulary rather than accepted freeform, because the provider schema is.passthrough()and #2106 is the recorded lesson about a typo in a config key silently doing nothing.The custom-tool half needed no code:
supportsResponsesCustomToolsalready existed and you confirmed it works.No registry entry sets the new field, so nothing ships dead and no existing provider changes behaviour. Shipping in the next release off
dev.