Bifrost HTTP Transport Release v2.3.1
✨ Features
- OpenAPI MCP Servers - New
openapiMCP connection type. Bifrost turns an OpenAPI or Swagger document (inlinespec, config.json-onlyspec_file, orspec_url, capped at 5 MiB) into an in-process MCP server where each operation is a tool that calls the upstream API, withsecurity_credentialsper security scheme, an optionalbase_url,include_deprecatedandmax_response_bytes. Configure it in config.json, the MCP management API, or the new OpenAPI option in the New MCP Server sheet, which can also replace the document later. Go SDK users opt in withMCPConfig.InProcessServerFactory(#8307, #8308, #8309, #8310, #8311, #8312, #8313, #8314, #8315, #8558) - Gateway Tool Search - Requests that defer tools with
defer_loadingon a model with no tool search of its own (Gemini, older GPT, Bedrock non-Claude) no longer get every tool loaded up front or a rejection. The deferred tools sit behind abifrost_tool_searchfunction that Bifrost resolves in the gateway (select:or BM25, at most 3 model calls per request), on both non-streaming and streaming Responses requests (#8504, #8505) - Passthrough Logs as Conversations - With content logging on, passthrough calls (
/openai_passthrough,/anthropic_passthrough,/genai_passthrough, ...) are parsed into the same fields as typed requests: chat, Responses, Anthropic messages, Gemini, embeddings, speech, transcription and images. Streamed responses are rebuilt from the recorded SSE, and multipart uploads are logged as a form summary. Rows are stored under their inferred request type with anis_passthroughflag, count in per-type filters, render as a conversation in the log sheet, and keep the raw bodies alongside (#8516, #8517, #8518, #8519, #8520, #8521, #8522, #8523, #8524, #8525) - Extra Fields as Response Headers - New
client.extra_fields_placementsetting:body(default, unchanged behaviour) orheaders. Withheaders, inference responses drop theextra_fieldsobject from the body and send it asx-bifrost-*response headers on native and integration routes, unary and streaming. Set it in Client Settings, the config API or config.json (#8476, #8477, #8478) - Virtual Key Metadata - Virtual keys carry string key/value
metadata(for examplecost_center,owner; keys use letters, digits,.,_and-, values up to 512 characters, up to 50 entries) set from config.json, Helm (bifrost.governance.virtualKeys[].metadata), the API (omit to keep,{}to clear) or the virtual key sheet. The list API filters bymetadata_<key>=<value>andsearchmatches metadata. Each log carries a snapshot of its key's metadata, so logs and spend filter by it, and the key's values overridex-bf-lh-*, logging headers andx-bf-dim-*dimensions so callers cannot spoof attribution (#7934, #7935, #7936, #7937, #8680) (thanks @jimseiwert!) - News Feeds - The notification tray has a News tab with product and provider announcements from RSS and Atom feeds (Bifrost, OpenAI, Anthropic, Gemini and Cursor built in). Admins toggle built-in feeds and add custom ones with their own logo; the gateway fetches, caches and merges them server-side through
/api/rss/feedsand/api/rss/items(#8537, #8538, #8539, #8540) - Code Mode Scripting - The Starlark sandbox adds the
jsonandmathmodules. A failed nested tool call reaches the script as an error value (hasattr(r, "error"),r.error) instead of a plain string, theexecuteToolCodereply carries only what the script prints and returns, runtime errors include a Python-style traceback, and error hints match the messages the interpreter actually emits (#8605, #8606, #8607, #8692, #8693) - Native Decisions on Azure and Databricks -
/v1/decisionsreaches Microsoft'sMicrosoft-Decision-1on Azure (Foundry/providers/microsoft/v1/systemone, with API key, Entra ID or managed identity auth) and Databricksai_decide. Typesafe accepts images by reference and answersboxquestions (#8485, #8498, #8571) - Vertex Session Routing - Vertex keys accept
use_session_routing: true, which sends the request's prompt cache key assessionIdon GeminigenerateContentcalls so requests sharing a prefix reach the same replica's implicit cache. Off by default, since Google enables it per project (#8650) - Secret References in Extra Headers -
network_config.extra_headersvalues acceptenv.andvault.references. Values are masked in API responses and restored on save, and the network form and the Databricks and Cloudflare migration planners treat masked values as unreadable (#8618, #8619, #8620) - Prompt Caching on Tool Calls -
cache_controlon an assistant message'stool_callsentry on/v1/chat/completionsnow reaches the Anthropic and Bedrocktool_useblock instead of being dropped (#8494, #8495) - Mistral Thinking Blocks - Mistral streams that carry
thinkingblocks indelta.contentarrays map them to reasoning instead of failing, and assistant reasoning is dropped before a request is sent, so replayed multi-turn conversations no longer hit Mistral's 422extra_forbidden(#8567, #8612) - Observability Attributes - Spans and Prometheus metrics carry the stored prompt a request ran (
bifrost.prompt.id,name,version), MCP spans carry the client name andbifrost.mcp.tool.is_errorwhen a tool reports failure, and the inbound W3Ctracestateis captured for connectors. A2A operations export bodies, stream updates and thea2a.client.operation.durationmetric to OTel, Maxim and telemetry, and gRPC ingress keeps caller request and trace IDs (#8575, #8681, #8682, #8683) - Calendar Date Presets - The date filter adds Today, This week and This month. The server resolves them in the caller's time zone (#8592)
- MCP OAuth Redirect URI Wildcard -
oauth2_server_config.allowed_redirect_urissyncs from config.json and accepts a bare"*"to approve any remote callback (shape checks still apply; partial patterns return 400). A startup warning and a dashboard alert flag the wildcard, and the MCP settings page has an Allowed redirect URIs input (#8480) - Antigravity Client Detection - Requests from Antigravity CLI and Antigravity Desktop are logged as their own apps instead of Other or Gemini CLI, including behind the edge proxy (#8543, #8658)
🐞 Fixed
- Go Module Path - The transports module is now published as
github.com/maximhq/bifrost/transports/v2, sogo getresolves thev2.xtags (#8450)
Go code that importsgithub.com/maximhq/bifrost/transports/bifrost-http/...must switch togithub.com/maximhq/bifrost/transports/v2/bifrost-http/....
- Explicit Public URLs for Agent Gateway and Skills - Bifrost no longer builds agent-card endpoints or skills marketplace clone URLs from the incoming
Hostheader, which a caller controls. These URLs come only from the configured public URL; an existingmcp_external_client_urlis copied to the newskills_external_client_urlon upgrade when it is a public URL. The MCP settings page also requires an Issuer URL in OAuth and Both modes (#8484, #8526)
If your Agent Gateway or skills marketplace relied on the request host, set the public URL explicitly before upgrading, or clients receive no usable agent-card endpoint.
- Agent Gateway Destination Permissions - Card discovery, OAuth and JSON-RPC, REST and gRPC runtime connections (including through a proxy) only reach approved destinations and only forward credentials to permitted origins, per agent
credential_originsandrequire_public_target. The unusedtenantandextension_urisregistration options are removed (#8552) - A2A Idle Goroutine Leak - The A2A push relay no longer leaks one goroutine every 3 seconds (about 1,200 per hour on v2.3.0, until OOM), and ClickHouse no longer issues a heavyweight
agent_logsmutation every minute when no rows match (#8596) - Calendar-Aligned Reset Windows - Calendar-aligned budgets and rate limits honour the duration multiplier, so a
7dbudget no longer resets daily and a2wlimit no longer resets weekly (#8596) - Agent OAuth Cache and ClickHouse Contention - The Agent Gateway OAuth token and fingerprint caches are bounded at 5,000 entries (about 12 MB) with expired entries evicted first, and a failed token fetch drops its stale entry. ClickHouse agent-log correlation no longer loads large payload columns or rewrites complete rows, so large A2A responses no longer back up the logging queue (#8608, #8617, #8657)
- Request Body Memory Retention - Pooled keep-alive connections no longer keep their largest-ever request-body buffer, which made per-pod memory ratchet up with large LLM payloads. Buffers above
server.body_buffer_pool_limit_bytes(default 4 MiB) are released after each request; larger bodies are still accepted up tomax_request_body_size_mb. Set a negative value to restore the previous behaviour. Async jobs (x-bf-async) that forward the raw body now copy it instead of reading a buffer the next request can overwrite (#8602) - Oversized Request Bodies - A body above
client_config.max_request_body_size_mbreturns413withrequest_too_largeand the configured limit, and logs a warning, instead of a generic400 Error when parsing request(#8700) - Rolling Upgrade Migrations - Postgres migrations wait for a live
CREATE INDEX CONCURRENTLYfrom a killed pod instead of dropping it, logstore upgrade migrations no longer stall behind long queries on old replicas, and the Helm chart adds a configurablestartupProbe(#8479, #8572) - OpenAI-Compatible xhigh Reasoning Effort -
reasoning_effort: "xhigh"is forwarded to vLLM, Ollama, SGL and custom OpenAI-compatible providers instead of being rewritten tohigh(#6244) (thanks @pranavthakur0-0!) - Tool Search Round-Trips - Deferred tools on OpenAI gpt-5.4+ (and Azure and Bedrock gpt) get the
tool_searchOpenAI requires instead of a 400. OpenAItool_search_callandtool_search_outputitems render for/anthropicclients. Claude's server tool searchserver_tool_useandtool_search_tool_resultblocks replay unchanged, andtool_referenceblocks in a clienttool_resultsurvive on the Anthropic and Vertex routes (#7813, #8502, #8503, #8555) (thanks @CMWR421!) - Client web_search and web_fetch Tools - On the Anthropic endpoint, a client
tool_usenamedweb_searchorweb_fetchstays a function call instead of becoming a server tool call, so itstool_resultis no longer orphaned and Bedrock stops rejecting the replay (#8370) (thanks @17jmumford!) - Bedrock Prompt Caching - Anthropic automatic caching (top-level
cache_control) now creates a Bedrock cache point after the last cacheable block instead of being dropped, andcache_controlon a tool message emits one standalonecachePointafter thetoolResult(#8573, #8595) - OpenRouter Claude Cache Markers -
cache_controlmarkers to Claude models on OpenRouter are clamped to four, so Claude no longer rejects the request (#8512) - Parallel Tool Calls on Claude -
parallel_tool_calls: falseandtool_choice.disable_parallel_tool_usereach Claude on every path (Anthropic Responses, Bedrock, Vertex), not only/v1/chat/completionsto Anthropic direct (#8471) - Responses Instructions with System Messages - Top-level
instructionsare no longer dropped when the input also has a system or developer message; they lead the system prompt as OpenAI does (#8515) - Chat Tool Results on Responses Models - Tool messages with array content use Responses input block types, and an empty tool result keeps
output: "", so OpenAI and Azure no longer reject the converted request (#8514, #8621) - Responses Streaming Wire Format - Lifecycle events no longer send
output: nulland no longer carry an invalid item object, which crashed or aborted strict clients (#8563) - Responses Input Items -
configuration_updateitems reach OpenAI with their reasoning effort (and are dropped on routes that cannot accept them), and replayed assistant messages with anidkeep theirstatus(#8599, #8634) - Converted Chat Stream Identity - Chat streams served through the Responses conversion carry the upstream
id,modelandcreatedon every chunk, so clients no longer see empty text (#8699) - Gemini Prompt Blocks - A prompt Gemini or Vertex refuses outright (
promptFeedback.blockReason) returns an error instead of a completed response with empty output (#8564) - Reasoning Token Usage - Gemini and Vertex batch results include thinking tokens in
completion_tokensand reportreasoning_tokens, and SGLang's top-levelusage.reasoning_tokensmaps tocompletion_tokens_details(#8600, #8687) (thanks @Shashank2985!) - Bedrock Converse Round-Trip - Converse responses keep
stopReasonandadditionalModelResponseFieldsverbatim (#8704) - Prompt Cache Mode on Bedrock Converse -
prompt_cache_optionsinadditionalModelRequestFieldson/bedrockConverse and ConverseStream now reaches OpenAI, Azure and Bedrock Mantle gpt-5.6 models. Send{"mode": "explicit"}with acachePointafter the system prompt to cache only the system prompt, or without acachePointto turn caching off (#8554) - Azure Deployments Route - Unknown chat fields are forwarded on the
/openai/deployments/...route instead of being dropped (#8703) - Decisions - Custom OpenAI-base providers are gated on
/v1/decisionsby their allowed requests alone, so models outside OpenAI's own list are no longer rejected withunsupported_operation, and answer objects a model JSON-encodes as strings are recovered (#8617, #8626, #8705) - Zero-Key Custom Providers in Routing - A custom provider with no keys no longer claims every model during provider inference, and
no_key_supports_modelno longer stops the fallback chain (#8706) - Keyless Provider Discovery - A custom provider switched from keyed to keyless runs model discovery despite leftover disabled keys, and those keys can be deleted (#8530)
- Provider Key enabled in config.json -
enabledon a provider key in config.json or Helm now persists across restarts, without re-syncing keys that UI edits had turned back on (#8610, #8647) - MCP Endpoint Slugs - A config.json MCP client without an
endpoint_slugkeeps its derived slug across re-syncs, so/mcp/<slug>no longer returns 403 after a reboot (#8535) - MCP Library Installed Badge - "Installed" is computed server-side over every MCP client, not only the first 100 matched by URL or name (#8581)
- Code Mode Tool Results - Tools restored from the config store keep their parameters in Starlark stubs, and nested results arrive as structured values: multiple JSON blocks,
structuredContentand JSON-in-a-string are decoded (#8603, #8604) - OAuth Refresh Failures - Token endpoint rejections are classified by OAuth error code, so Slack and Atlassian refresh failures prompt re-auth (#8559)
- Throughput Metric - Dashboard tokens/sec counts only inference requests, so
list_modelsand similar calls no longer skew it (#8579) - Passthrough Logs - Passthrough log keys no longer show "(deleted)", and passthrough streams with no upstream
Content-Typeare no longer labelledtext/plain(#8601, #8638, #8689) - SQLite Log Writes - SQLite
SQLITE_BUSYandSQLITE_LOCKEDare treated as transient, and a failed batch is retried whole instead of row by row (#8707) - Model Catalog Prices - Per-1M token prices show up to 4 decimals, so
$0.075no longer displays as$0.08(#8326) (thanks @Shashank2985!)
🗄️ Database Migrations
- add_mcp_client_openapi_spec_column - Adds
openapi_spectoconfig_mcp_clients. Non-reversible: dropping it would delete the only stored copy of every OpenAPI client's document. Additive; older binaries ignore it. - add_mcp_client_openapi_config_json_column - Adds
openapi_config_jsontoconfig_mcp_clients. Non-reversible: dropping it would delete every OpenAPI client's base URL and stored credentials. Additive. - add_client_config_extra_fields_placement_column - Adds
extra_fields_placement(NOT NULL DEFAULT 'body') toconfig_client. Reversible: rollback drops the column. - add_skills_external_client_url_column - Adds
skills_external_client_urltoconfig_client. Non-reversible: dropping it would discard marketplace configuration. Additive. - backfill_skills_external_client_url - Copies
mcp_external_client_urlintoskills_external_client_urlwhere it is empty and the value is a public URL. Non-reversible: migrated marketplace configuration is preserved. - add_virtual_key_metadata_column - Adds the nullable
metadatacolumn togovernance_virtual_keys. Non-reversible: dropping it would delete every key's metadata. Additive. - add_a2a_private_allowlist_column - Adds
a2a_private_allowlist_jsontoconfig_client. Reversible: rollback drops the column. - add_agent_destination_policy_columns - Adds
credential_originsandrequire_public_targettoconfig_agent_registrations. Non-reversible: removing them would discard security policy. Additive. - drop_agent_registration_options - Drops
tenantandextension_urisfromconfig_agent_registrations. Non-reversible: the removed values cannot be recovered.
This migration drops columns. An older binary running alongside it during a rolling upgrade loses access to those values, and rolling back does not restore them.
- add_vertex_use_session_routing_column - Adds
vertex_use_session_routingto the provider keys table. Reversible: rollback drops the column. - add_rss_feeds_table - Creates
rss_feedsand seeds the built-in feeds. Non-reversible: dropping it would delete custom feeds, their logos and every built-in enabled choice. Additive. - add_rss_feeds_icon_url_column - Adds
icon_urltorss_feedstables created before it existed. Non-reversible: dropping it would delete every custom feed logo. Additive. - logs_add_matview_refresh_state_table - Creates
matview_refresh_stateto record when each materialized view last refreshed across the cluster. Reversible: rollback drops the table. - logs_add_is_passthrough_column - Adds
is_passthroughtologs. Rollback is a no-op and leaves the column in place. Additive.
🐙 Closed GitHub Issues
- #3440 - [Feature]: Add nodeSelector support for all workloads managed by Helm Chart
- #6193 - [Bug]: Cannot pass
xhighreasoning effort to vLLM provider - #6499 - [Feature]: Add "today" "this week" "this month" to date filter
- #7763 - All /mcp/ endpoints return 403 access_denied on v2.1.1-enterprise (silent optional-interface assertion failure)
- #7933 - [Feature]: Custom metadata on virtual keys (API, UI, and spend filtering)
- #8078 - [Bug]: Anthropic endpoint converts a client tool_use named web_search/web_fetch into a server tool call, orphaning its tool_result (Bedrock 400)
- #8324 - [bug]: Model catalog rounds per-1M token prices to 2 decimals, so $0.075 shows as $0.08 and $0.0004 as $0.00
- #8359 - [Bug]: ClickHouse store still issues a heavyweight mutation on agent_logs every minute
- #8361 - [Bug]: /anthropic/v1/messages to OpenAI gpt-5.4+ fails with 400 when tools have defer_loading but no tool_search tool
- #8383 - [Bug]: calendar_aligned budgets and rate limits silently ignore the numeric part of reset_duration (7d resets daily)
- #8500 - [Bug]: Keyless provider with residual disabled keys never discovers models - key CRUD blocked, deadlock
- #8541 - [Bug]: Bedrock: top-level cache_control is silently dropped, automatic caching never creates a cache point
- #8548 - [Bug]: MCP Library "Installed" badge only checks the first 100 clients and only matches by URL or name
- #8554 - [Bug]: Bedrock Converse cache points no longer limit gpt-5.6 caching to the system prompt in v2.3.0, and prompt_cache_options cannot be sent on this route
- #8568 - [Bug]: Empty tool message breaks requests to Responses API models
- #8590 - [Bug]: goroutine count climbs ~1,200/hour on v2.3.0 until OOM, even at low traffic
- #8616 - [Bug]: Responses API configuration_update input items drop the reasoning field
- #8684 - [Bug]: Gemini and Vertex batch results ignore thinking tokens (thoughtsTokenCount), so batch cost, budgets and totals are understated
- #8709 - A2A push relay leaks one goroutine every 3s:
gateContextresult is never cancelled (v2.3.0)
Installation
Docker
docker run -p 8080:8080 maximhq/bifrost:v2.3.1Binary Download
npx @maximhq/bifrost --transport-version v2.3.1Docker Images
maximhq/bifrost:v2.3.1- This specific versionmaximhq/bifrost:latest- Latest version (updated with this release)
This release was automatically created with dependencies: core v1.13.0, framework v1.10.0. All plugins have been validated and updated.