Skip to content

[Bug]: Bifrost MCP tools work over direct MCP, but Codex tool calls return unsupported call #3189

Description

@Defman

Prerequisites

  • I have searched existing issues and discussions to avoid duplicates
  • I am using the latest version (or have tested against main/nightly)

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

  1. 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
  1. 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"
  1. 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"]
  1. 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"
  ]
}
  1. In Codex, try to call the code-mode helper tool:
listToolFiles
  1. Observe Codex returning:
unsupported call: listToolFiles
  1. 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
  1. 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
  1. Disable code mode:
"is_code_mode_client": false
  1. 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.

  1. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions