Prerequisites
Description
Bifrost MCP tools work when called directly through Bifrost's MCP endpoint, but the same tools fail from Codex with unsupported call.
I am using Bifrost with Codex through the OpenAI-compatible /openai/responses endpoint, not the Anthropic proxy path. Bifrost's own MCP endpoint works when called directly: tools/list works, tools/call works, and the GitHub MCP tools execute successfully.
Inside Codex, tools exposed by Bifrost become visible but are not callable. This happens in both code mode and non-code mode:
- With code mode enabled, Codex sees helper tools such as
listToolFiles, readToolFile, getToolDocs, and executeToolCode, but calls fail with unsupported call.
- With code mode disabled, regular prefixed GitHub tools such as
github_read-search_repositories and github_read-get_me work through direct MCP calls, but fail from Codex with unsupported call.
Codex is also configured to use Bifrost directly as an MCP server via [mcp_servers.bifrost], so this may be either a Bifrost MCP compatibility issue with Codex's native MCP client, or something in the Bifrost -> OpenAI Responses -> Codex tool exposure/call path. The underlying GitHub MCP server appears to be fine because Bifrost can call it directly.
Disclosure
This issue report was prepared in collaboration with GPT-5.5 using Codex 0.128.0.
Steps to reproduce
- Run Bifrost locally through Docker Compose:
bifrost:
image: maximhq/bifrost:v1.5.0-prerelease8
ports:
- "8080:8080"
volumes:
- bifrost_data:/app/data
- ./config.json:/app/data/config.json:ro
- Configure Codex to use Bifrost's OpenAI-compatible Responses endpoint and Bifrost's native MCP endpoint:
model = "gpt-5.5"
model_provider = "azure"
[model_providers.azure]
name = "Azure OpenAI"
base_url = "http://localhost:8080/openai"
wire_api = "responses"
[mcp_servers.bifrost]
url = "http://localhost:8080/mcp"
- Configure the GitHub MCP servers using the official GitHub MCP server Docker images:
github-mcp-read:
image: ghcr.io/github/github-mcp-server:latest
command: ["http"]
github-mcp-write:
image: ghcr.io/github/github-mcp-server:latest
command: ["http"]
- Configure a Bifrost MCP client with code mode enabled:
{
"name": "github_read",
"connection_type": "http",
"connection_string": "http://github-mcp-read:8082/mcp",
"auth_type": "headers",
"is_ping_available": true,
"is_code_mode_client": true,
"tools_to_execute": [
"get_me",
"search_repositories",
"search_users",
"search_code",
"list_branches",
"list_tags",
"get_tag",
"list_commits",
"get_commit",
"get_file_contents",
"list_pull_requests",
"pull_request_read",
"search_pull_requests",
"list_issues",
"issue_read",
"search_issues",
"get_label",
"list_issue_types",
"list_releases",
"get_latest_release",
"get_release_by_tag",
"get_teams",
"get_team_members"
]
}
- In Codex, try to call the code-mode helper tool:
- Observe Codex returning:
unsupported call: listToolFiles
- Verify that direct MCP against Bifrost succeeds:
curl -s -X POST 'http://localhost:8080/mcp' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
This returns the expected code-mode tools:
executeToolCode
getToolDocs
listToolFiles
readToolFile
- Call
listToolFiles directly through MCP:
curl -s -X POST 'http://localhost:8080/mcp' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"listToolFiles","arguments":{}}}'
This succeeds and returns:
# Workflow: listToolFiles -> readToolFile -> (optional) getToolDocs -> executeToolCode
# Read the server .pyi file before executeToolCode to confirm exact tool names and parameters.
servers/
github_read.pyi
github_write.pyi
- Disable code mode:
"is_code_mode_client": false
- Verify direct MCP still works for regular prefixed tools:
curl -s -X POST 'http://localhost:8080/mcp' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"github_read-search_repositories","arguments":{"query":"repo:maximhq/bifrost","perPage":1}}}'
This returns the maximhq/bifrost repository successfully.
- Try the same regular prefixed tools from Codex:
github_read-search_repositories
github_read-get_me
github_read_search_repositories
github_read_get_me
All tested naming variants return unsupported call from the Codex tool bridge.
Expected behavior
Codex should be able to call the same Bifrost-exposed MCP tools that succeed via direct MCP tools/call. With code mode enabled, listToolFiles should return: text servers/ github_read.pyi github_write.pyi With code mode disabled, regular prefixed tools such as github_read-search_repositories and github_read-get_me should execute successfully from Codex.
Actual behavior
Direct MCP calls to POST http://localhost:8080/mcp succeed, but calls through Codex fail with: text unsupported call: listToolFiles unsupported call: github_read-search_repositories unsupported call: github_read-get_me Bifrost's /api/mcp-logs endpoint shows no failed MCP executions, which suggests the failing calls may not be reaching Bifrost's normal MCP execution path.
Affected area(s)
Transports (HTTP)
Version
v1.5.0-prerelease8
Environment
- Bifrost: maximhq/bifrost:v1.5.0-prerelease8
- Bifrost endpoint used by Codex: http://localhost:8080/openai
- Bifrost wire API: responses
- Bifrost native MCP endpoint configured in Codex: http://localhost:8080/mcp
- Codex: 0.128.0
- Model: gpt-5.5 through Azure OpenAI-compatible provider config
- MCP servers: official GitHub MCP server Docker image, ghcr.io/github/github-mcp-server:latest
- GitHub MCP server command: ["http"]
- Runtime: Docker Compose
Relevant logs/output
Codex model requests reach Bifrost successfully:
POST /openai/responses 200
Bifrost admin API reports both MCP clients connected with code mode enabled:
[
{
"name": "github_read",
"code_mode": true,
"state": "connected",
"configured_count": 23,
"exposed_tool_count": 41
},
{
"name": "github_write",
"code_mode": true,
"state": "connected",
"configured_count": 18,
"exposed_tool_count": 41
}
]
With code mode disabled, the same API reports:
[
{
"name": "github_read",
"code_mode": false,
"state": "connected",
"configured_count": 23,
"exposed_tool_count": 41
},
{
"name": "github_write",
"code_mode": false,
"state": "connected",
"configured_count": 18,
"exposed_tool_count": 41
}
]
The MCP logs endpoint did not show failed executions:
{
"logs": [],
"stats": {
"total_executions": 0,
"success_rate": 0,
"average_latency": 0,
"total_cost": 0
},
"has_logs": false
}
One initially suspicious schema detail is that no-argument tools appear to be exposed without an explicit empty `properties` object:
{
"name": "listToolFiles",
"inputSchema": {
"type": "object"
}
}
The `transports/v1.5.0-prerelease8` source already initializes empty `properties` for no-argument MCP tools in `core/mcp/utils.go`, so this is probably not the primary issue. It also does not explain why tools with normal properties, such as `github_read-search_repositories`, fail through Codex.
After inspecting `transports/v1.5.0-prerelease8`, one plausible root cause appears to be in the OpenAI-compatible Responses streaming path rather than in the GitHub MCP server or direct Bifrost MCP endpoint:
- `core/bifrost.go`: `tryRequest` calls `MCPManager.AddToolsToRequest(...)` before sending non-streaming requests upstream.
- `core/bifrost.go`: `tryStreamRequest` also calls `MCPManager.AddToolsToRequest(...)` before sending streaming requests upstream.
- `core/mcp/toolmanager.go`: for Responses requests, available MCP `ChatTool`s are converted to Responses `function` tools via `ToResponsesTool()`.
- `core/bifrost.go`: `ResponsesRequest` calls `MCPManager.CheckAndExecuteAgentForResponsesRequest(...)` after receiving the provider response.
- `core/mcp/mcp.go`: `CheckAndExecuteAgentForResponsesRequest` detects Responses `function_call` outputs, executes MCP tools, appends tool results, and continues the agent loop.
The suspicious gap is that the streaming Responses path injects MCP tools but does not appear to have an equivalent post-response agent/tool execution loop before returning streamed events to the client.
In `transports/v1.5.0-prerelease8`, `ResponsesRequest` calls `makeResponsesRequest(...)` and then calls `MCPManager.CheckAndExecuteAgentForResponsesRequest(...)`. By contrast, `ResponsesStreamRequest` builds a `BifrostRequest` with `RequestType: ResponsesStreamRequest` and returns `handleStreamRequest(...)` directly. The shared `tryStreamRequest` path does call `MCPManager.AddToolsToRequest(...)`, so MCP tools are still injected for streaming requests, but the stream path only returns the provider stream received from `msg.ResponseStream`; there is no matching streaming agent loop that consumes streamed `function_call` events, executes the MCP tool, submits the tool output back to the model, and continues streaming the final answer.
This depends on whether Codex is sending `stream: true` to `/openai/responses`. I know from Bifrost logs that Codex is using `POST /openai/responses`, but I still need to confirm the exact raw request shape. If Codex is not streaming, then this streaming-path hypothesis is less likely, because the non-streaming Responses path in `v1.5.0-prerelease8` does have an MCP agent execution loop.
Possible fix directions:
- Add tool-call execution support for the OpenAI-compatible Responses streaming path, so Bifrost consumes streamed `function_call` events, executes the MCP tool, feeds tool output back to the model, and streams the final answer to Codex.
- Or, if Bifrost should not execute tools in this path, avoid injecting Bifrost MCP tools into Responses streaming requests for clients that cannot call those tools directly.
Open question for maintainers: when Bifrost injects MCP tools into `/openai/responses`, is Bifrost expected to execute returned MCP `function_call`s server-side, or are OpenAI-compatible clients expected to execute those returned tool calls themselves? If clients are expected to execute them, Codex may need a different tool naming/dispatch compatibility layer for Bifrost-injected MCP tools.
Regression?
Unknown. I have only confirmed this behavior on maximhq/bifrost:v1.5.0-prerelease8.
Severity
High (major functionality broken)
Prerequisites
Description
Bifrost MCP tools work when called directly through Bifrost's MCP endpoint, but the same tools fail from Codex with
unsupported call.I am using Bifrost with Codex through the OpenAI-compatible
/openai/responsesendpoint, not the Anthropic proxy path. Bifrost's own MCP endpoint works when called directly:tools/listworks,tools/callworks, and the GitHub MCP tools execute successfully.Inside Codex, tools exposed by Bifrost become visible but are not callable. This happens in both code mode and non-code mode:
listToolFiles,readToolFile,getToolDocs, andexecuteToolCode, but calls fail withunsupported call.github_read-search_repositoriesandgithub_read-get_mework through direct MCP calls, but fail from Codex withunsupported call.Codex is also configured to use Bifrost directly as an MCP server via
[mcp_servers.bifrost], so this may be either a Bifrost MCP compatibility issue with Codex's native MCP client, or something in the Bifrost -> OpenAI Responses -> Codex tool exposure/call path. The underlying GitHub MCP server appears to be fine because Bifrost can call it directly.Disclosure
This issue report was prepared in collaboration with GPT-5.5 using Codex 0.128.0.
Steps to reproduce
{ "name": "github_read", "connection_type": "http", "connection_string": "http://github-mcp-read:8082/mcp", "auth_type": "headers", "is_ping_available": true, "is_code_mode_client": true, "tools_to_execute": [ "get_me", "search_repositories", "search_users", "search_code", "list_branches", "list_tags", "get_tag", "list_commits", "get_commit", "get_file_contents", "list_pull_requests", "pull_request_read", "search_pull_requests", "list_issues", "issue_read", "search_issues", "get_label", "list_issue_types", "list_releases", "get_latest_release", "get_release_by_tag", "get_teams", "get_team_members" ] }This returns the expected code-mode tools:
listToolFilesdirectly through MCP:This succeeds and returns:
This returns the
maximhq/bifrostrepository successfully.All tested naming variants return
unsupported callfrom the Codex tool bridge.Expected behavior
Codex should be able to call the same Bifrost-exposed MCP tools that succeed via direct MCP
tools/call. With code mode enabled,listToolFilesshould return:text servers/ github_read.pyi github_write.pyiWith code mode disabled, regular prefixed tools such asgithub_read-search_repositoriesandgithub_read-get_meshould execute successfully from Codex.Actual behavior
Direct MCP calls to
POST http://localhost:8080/mcpsucceed, but calls through Codex fail with:text unsupported call: listToolFiles unsupported call: github_read-search_repositories unsupported call: github_read-get_meBifrost's/api/mcp-logsendpoint shows no failed MCP executions, which suggests the failing calls may not be reaching Bifrost's normal MCP execution path.Affected area(s)
Transports (HTTP)
Version
v1.5.0-prerelease8
Environment
Relevant logs/output
Codex model requests reach Bifrost successfully: POST /openai/responses 200 Bifrost admin API reports both MCP clients connected with code mode enabled: [ { "name": "github_read", "code_mode": true, "state": "connected", "configured_count": 23, "exposed_tool_count": 41 }, { "name": "github_write", "code_mode": true, "state": "connected", "configured_count": 18, "exposed_tool_count": 41 } ] With code mode disabled, the same API reports: [ { "name": "github_read", "code_mode": false, "state": "connected", "configured_count": 23, "exposed_tool_count": 41 }, { "name": "github_write", "code_mode": false, "state": "connected", "configured_count": 18, "exposed_tool_count": 41 } ] The MCP logs endpoint did not show failed executions: { "logs": [], "stats": { "total_executions": 0, "success_rate": 0, "average_latency": 0, "total_cost": 0 }, "has_logs": false } One initially suspicious schema detail is that no-argument tools appear to be exposed without an explicit empty `properties` object: { "name": "listToolFiles", "inputSchema": { "type": "object" } } The `transports/v1.5.0-prerelease8` source already initializes empty `properties` for no-argument MCP tools in `core/mcp/utils.go`, so this is probably not the primary issue. It also does not explain why tools with normal properties, such as `github_read-search_repositories`, fail through Codex. After inspecting `transports/v1.5.0-prerelease8`, one plausible root cause appears to be in the OpenAI-compatible Responses streaming path rather than in the GitHub MCP server or direct Bifrost MCP endpoint: - `core/bifrost.go`: `tryRequest` calls `MCPManager.AddToolsToRequest(...)` before sending non-streaming requests upstream. - `core/bifrost.go`: `tryStreamRequest` also calls `MCPManager.AddToolsToRequest(...)` before sending streaming requests upstream. - `core/mcp/toolmanager.go`: for Responses requests, available MCP `ChatTool`s are converted to Responses `function` tools via `ToResponsesTool()`. - `core/bifrost.go`: `ResponsesRequest` calls `MCPManager.CheckAndExecuteAgentForResponsesRequest(...)` after receiving the provider response. - `core/mcp/mcp.go`: `CheckAndExecuteAgentForResponsesRequest` detects Responses `function_call` outputs, executes MCP tools, appends tool results, and continues the agent loop. The suspicious gap is that the streaming Responses path injects MCP tools but does not appear to have an equivalent post-response agent/tool execution loop before returning streamed events to the client. In `transports/v1.5.0-prerelease8`, `ResponsesRequest` calls `makeResponsesRequest(...)` and then calls `MCPManager.CheckAndExecuteAgentForResponsesRequest(...)`. By contrast, `ResponsesStreamRequest` builds a `BifrostRequest` with `RequestType: ResponsesStreamRequest` and returns `handleStreamRequest(...)` directly. The shared `tryStreamRequest` path does call `MCPManager.AddToolsToRequest(...)`, so MCP tools are still injected for streaming requests, but the stream path only returns the provider stream received from `msg.ResponseStream`; there is no matching streaming agent loop that consumes streamed `function_call` events, executes the MCP tool, submits the tool output back to the model, and continues streaming the final answer. This depends on whether Codex is sending `stream: true` to `/openai/responses`. I know from Bifrost logs that Codex is using `POST /openai/responses`, but I still need to confirm the exact raw request shape. If Codex is not streaming, then this streaming-path hypothesis is less likely, because the non-streaming Responses path in `v1.5.0-prerelease8` does have an MCP agent execution loop. Possible fix directions: - Add tool-call execution support for the OpenAI-compatible Responses streaming path, so Bifrost consumes streamed `function_call` events, executes the MCP tool, feeds tool output back to the model, and streams the final answer to Codex. - Or, if Bifrost should not execute tools in this path, avoid injecting Bifrost MCP tools into Responses streaming requests for clients that cannot call those tools directly. Open question for maintainers: when Bifrost injects MCP tools into `/openai/responses`, is Bifrost expected to execute returned MCP `function_call`s server-side, or are OpenAI-compatible clients expected to execute those returned tool calls themselves? If clients are expected to execute them, Codex may need a different tool naming/dispatch compatibility layer for Bifrost-injected MCP tools.Regression?
Unknown. I have only confirmed this behavior on
maximhq/bifrost:v1.5.0-prerelease8.Severity
High (major functionality broken)