Skip to content
Open

v1.4.0 #1153

Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9853088
feat: added agents and codemode to mcp (#941)
Pratham-Mishra04 Nov 28, 2025
0f0be03
feat: add MCP server implementation (#936)
Pratham-Mishra04 Nov 28, 2025
22cca4b
Merge branch 'main' into v1.4.0
Pratham-Mishra04 Dec 8, 2025
cd4cfac
Merge branch 'main' into v1.4.0
Pratham-Mishra04 Dec 12, 2025
bd2355b
Merge branch 'main' into v1.4.0
akshaydeo Dec 22, 2025
270cc82
feat: governance plugin refactor
Pratham-Mishra04 Dec 8, 2025
d877912
deadlock fix
akshaydeo Dec 11, 2025
0753937
chore: governance tests added
Pratham-Mishra04 Dec 9, 2025
580f591
feat: governance plugin refactor (#1154)
akshaydeo Dec 24, 2025
e42e8da
chore: governance tests added (#1041)
akshaydeo Dec 24, 2025
f26d081
feat: added binding level toggle in mcp codemode
Pratham-Mishra04 Dec 22, 2025
fab6ecc
feat: added responses mcp tool execute endpoint
Pratham-Mishra04 Dec 23, 2025
d7414a6
feat: added pings for mcp connections
Pratham-Mishra04 Dec 23, 2025
4f82c3b
feat: add tool-level binding option for MCP code mode (#1160)
akshaydeo Dec 24, 2025
187061c
feat: added Response API execution enpoint for MCP tools (#1161)
akshaydeo Dec 24, 2025
86283ac
feat: add health monitoring for MCP clients (#1168)
akshaydeo Dec 24, 2025
e65a335
Merge branch 'main' into v1.4.0
akshaydeo Dec 26, 2025
bca600b
mod update
akshaydeo Dec 26, 2025
0a6427c
Merge branch 'main' into v1.4.0
akshaydeo Dec 29, 2025
76a4e9d
Merge branch 'main' into v1.4.0
akshaydeo Dec 29, 2025
07da5f5
plugins v2 architecture
akshaydeo Nov 19, 2025
d3b125f
backmerge fixes
akshaydeo Dec 26, 2025
ed0154c
plugins v2 architecture (#888)
akshaydeo Dec 29, 2025
0851d14
bumped plugin versions
akshaydeo Dec 29, 2025
34eeb43
Merge branch 'main' into 12-29-bumped_plugin_versions
akshaydeo Dec 29, 2025
6be3aa3
bumped plugin versions (#1187)
akshaydeo Dec 29, 2025
2c4cc12
cr-fixes
akshaydeo Dec 29, 2025
525b59e
cr-fixes (#1188)
akshaydeo Dec 29, 2025
f0d5187
framework: bump core to v1.3.0 --skip-pipeline
github-actions[bot] Dec 29, 2025
869d7aa
skipping tests for plugins
akshaydeo Dec 29, 2025
c3c68a9
skipping tests for plugins (#1190)
akshaydeo Dec 29, 2025
b66e053
plugins/governance: bump core to v1.3.0 and framework to v1.2.0 --ski…
github-actions[bot] Dec 29, 2025
f879fdf
plugins/jsonparser: bump core to v1.3.0 and framework to v1.2.0 --ski…
github-actions[bot] Dec 29, 2025
4754b30
plugins/logging: bump core to v1.3.0 and framework to v1.2.0 --skip-p…
github-actions[bot] Dec 29, 2025
e6d398b
plugins/maxim: bump core to v1.3.0 and framework to v1.2.0 --skip-pip…
github-actions[bot] Dec 29, 2025
8a46230
plugins/mocker: bump core to v1.3.0 and framework to v1.2.0 --skip-pi…
github-actions[bot] Dec 29, 2025
9e55ffb
plugins/otel: bump core to v1.3.0 and framework to v1.2.0 --skip-pipe…
github-actions[bot] Dec 29, 2025
9c384a4
upgrades mocker plugin for semantic cache
akshaydeo Dec 29, 2025
b14e8c8
upgrades mocker plugin for semantic cache (#1192)
akshaydeo Dec 29, 2025
238bb2c
plugins/semanticcache: bump core to v1.3.0 and framework to v1.2.0 --…
github-actions[bot] Dec 29, 2025
31f1d83
plugins/telemetry: bump core to v1.3.0 and framework to v1.2.0 --skip…
github-actions[bot] Dec 29, 2025
1ce7ff4
build script fixes
akshaydeo Dec 29, 2025
f3dd273
Merge pull request #1194 from maximhq/12-29-build_script_fixes
akshaydeo Dec 29, 2025
654fee8
transports: update dependencies --skip-pipeline
github-actions[bot] Dec 29, 2025
27f830b
Adds changelog for v1.4.0-prerelease1 --skip-pipeline
github-actions[bot] Dec 29, 2025
4d9a667
Merge branch 'main' into v1.4.0
akshaydeo Dec 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ insert_final_newline = false
end_of_line = lf
charset = utf-8

[*.go]
indent_style = tab
indent_size = 4

[*.{js,jsx,ts,tsx,mjs,json,md,css,scss,html}]
insert_final_newline = false
189 changes: 146 additions & 43 deletions core/bifrost.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"time"

"github.com/google/uuid"
"github.com/maximhq/bifrost/core/mcp"
"github.com/maximhq/bifrost/core/providers/anthropic"
"github.com/maximhq/bifrost/core/providers/azure"
"github.com/maximhq/bifrost/core/providers/bedrock"
Expand Down Expand Up @@ -66,7 +67,8 @@ type Bifrost struct {
pluginPipelinePool sync.Pool // Pool for PluginPipeline objects
bifrostRequestPool sync.Pool // Pool for BifrostRequest objects
logger schemas.Logger // logger instance, default logger is used if not provided
mcpManager *MCPManager // MCP integration manager (nil if MCP not configured)
mcpManager *mcp.MCPManager // MCP integration manager (nil if MCP not configured)
mcpInitOnce sync.Once // Ensures MCP manager is initialized only once
dropExcessRequests atomic.Bool // If true, in cases where the queue is full, requests will not wait for the queue to be empty and will be dropped instead.
keySelector schemas.KeySelector // Custom key selector function
}
Expand Down Expand Up @@ -179,13 +181,10 @@ func Init(ctx context.Context, config schemas.BifrostConfig) (*Bifrost, error) {

// Initialize MCP manager if configured
if config.MCPConfig != nil {
mcpManager, err := newMCPManager(bifrostCtx, *config.MCPConfig, bifrost.logger)
if err != nil {
bifrost.logger.Warn(fmt.Sprintf("failed to initialize MCP manager: %v", err))
} else {
bifrost.mcpManager = mcpManager
bifrost.mcpInitOnce.Do(func() {
bifrost.mcpManager = mcp.NewMCPManager(bifrostCtx, *config.MCPConfig, bifrost.logger)
bifrost.logger.Info("MCP integration initialized successfully")
}
})
}

// Create buffered channels for each provider and start workers
Expand Down Expand Up @@ -539,8 +538,7 @@ func (bifrost *Bifrost) TextCompletionStreamRequest(ctx context.Context, req *sc
return bifrost.handleStreamRequest(ctx, bifrostReq)
}

// ChatCompletionRequest sends a chat completion request to the specified provider.
func (bifrost *Bifrost) ChatCompletionRequest(ctx context.Context, req *schemas.BifrostChatRequest) (*schemas.BifrostChatResponse, *schemas.BifrostError) {
func (bifrost *Bifrost) makeChatCompletionRequest(ctx context.Context, req *schemas.BifrostChatRequest) (*schemas.BifrostChatResponse, *schemas.BifrostError) {
if req == nil {
return nil, &schemas.BifrostError{
IsBifrostError: false,
Expand Down Expand Up @@ -574,10 +572,35 @@ func (bifrost *Bifrost) ChatCompletionRequest(ctx context.Context, req *schemas.
if err != nil {
return nil, err
}
//TODO: Release the response

return response.ChatResponse, nil
}

// ChatCompletionRequest sends a chat completion request to the specified provider.
func (bifrost *Bifrost) ChatCompletionRequest(ctx context.Context, req *schemas.BifrostChatRequest) (*schemas.BifrostChatResponse, *schemas.BifrostError) {
// If ctx is nil, use the bifrost context (defensive check for mcp agent mode)
if ctx == nil {
ctx = bifrost.ctx
}

response, err := bifrost.makeChatCompletionRequest(ctx, req)
if err != nil {
return nil, err
}

// Check if we should enter agent mode
if bifrost.mcpManager != nil {
return bifrost.mcpManager.CheckAndExecuteAgentForChatRequest(
&ctx,
req,
response,
bifrost.makeChatCompletionRequest,
)
}

return response, nil
}

// ChatCompletionStreamRequest sends a chat completion stream request to the specified provider.
func (bifrost *Bifrost) ChatCompletionStreamRequest(ctx context.Context, req *schemas.BifrostChatRequest) (chan *schemas.BifrostStream, *schemas.BifrostError) {
if req == nil {
Expand Down Expand Up @@ -612,8 +635,7 @@ func (bifrost *Bifrost) ChatCompletionStreamRequest(ctx context.Context, req *sc
return bifrost.handleStreamRequest(ctx, bifrostReq)
}

// ResponsesRequest sends a responses request to the specified provider.
func (bifrost *Bifrost) ResponsesRequest(ctx context.Context, req *schemas.BifrostResponsesRequest) (*schemas.BifrostResponsesResponse, *schemas.BifrostError) {
func (bifrost *Bifrost) makeResponsesRequest(ctx context.Context, req *schemas.BifrostResponsesRequest) (*schemas.BifrostResponsesResponse, *schemas.BifrostError) {
if req == nil {
return nil, &schemas.BifrostError{
IsBifrostError: false,
Expand Down Expand Up @@ -647,10 +669,34 @@ func (bifrost *Bifrost) ResponsesRequest(ctx context.Context, req *schemas.Bifro
if err != nil {
return nil, err
}
//TODO: Release the response
return response.ResponsesResponse, nil
}

// ResponsesRequest sends a responses request to the specified provider.
func (bifrost *Bifrost) ResponsesRequest(ctx context.Context, req *schemas.BifrostResponsesRequest) (*schemas.BifrostResponsesResponse, *schemas.BifrostError) {
// If ctx is nil, use the bifrost context (defensive check for mcp agent mode)
if ctx == nil {
ctx = bifrost.ctx
}

response, err := bifrost.makeResponsesRequest(ctx, req)
if err != nil {
return nil, err
}

// Check if we should enter agent mode
if bifrost.mcpManager != nil {
return bifrost.mcpManager.CheckAndExecuteAgentForResponsesRequest(
&ctx,
req,
response,
bifrost.makeResponsesRequest,
)
}

return response, nil
}

// ResponsesStreamRequest sends a responses stream request to the specified provider.
func (bifrost *Bifrost) ResponsesStreamRequest(ctx context.Context, req *schemas.BifrostResponsesRequest) (chan *schemas.BifrostStream, *schemas.BifrostError) {
if req == nil {
Expand Down Expand Up @@ -1683,10 +1729,10 @@ func (bifrost *Bifrost) RegisterMCPTool(name, description string, handler func(a
return fmt.Errorf("MCP is not configured in this Bifrost instance")
}

return bifrost.mcpManager.registerTool(name, description, handler, toolSchema)
return bifrost.mcpManager.RegisterTool(name, description, handler, toolSchema)
}

// ExecuteMCPTool executes an MCP tool call and returns the result as a tool message.
// ExecuteChatMCPTool executes an MCP tool call and returns the result as a chat message.
// This is the main public API for manual MCP tool execution.
//
// Parameters:
Expand All @@ -1696,7 +1742,7 @@ func (bifrost *Bifrost) RegisterMCPTool(name, description string, handler func(a
// Returns:
// - schemas.ChatMessage: Tool message with execution result
// - schemas.BifrostError: Any execution error
func (bifrost *Bifrost) ExecuteMCPTool(ctx context.Context, toolCall schemas.ChatAssistantMessageToolCall) (*schemas.ChatMessage, *schemas.BifrostError) {
func (bifrost *Bifrost) ExecuteChatMCPTool(ctx context.Context, toolCall schemas.ChatAssistantMessageToolCall) (*schemas.ChatMessage, *schemas.BifrostError) {
if bifrost.mcpManager == nil {
return nil, &schemas.BifrostError{
IsBifrostError: false,
Expand All @@ -1709,13 +1755,12 @@ func (bifrost *Bifrost) ExecuteMCPTool(ctx context.Context, toolCall schemas.Cha
}
}

result, err := bifrost.mcpManager.executeTool(ctx, toolCall)
result, err := bifrost.mcpManager.ExecuteChatTool(ctx, toolCall)
if err != nil {
return nil, &schemas.BifrostError{
IsBifrostError: false,
Error: &schemas.ErrorField{
Message: err.Error(),
Error: err,
},
ExtraFields: schemas.BifrostErrorExtraFields{
RequestType: schemas.ChatCompletionRequest, // MCP tools are used with chat completions
Expand All @@ -1726,6 +1771,44 @@ func (bifrost *Bifrost) ExecuteMCPTool(ctx context.Context, toolCall schemas.Cha
return result, nil
}

// ExecuteResponsesMCPTool executes an MCP tool call and returns the result as a responses message.

// Parameters:
// - ctx: Execution context
// - toolCall: The tool call to execute (from assistant message)
//
// Returns:
// - schemas.ResponsesMessage: Tool message with execution result
// - schemas.BifrostError: Any execution error
func (bifrost *Bifrost) ExecuteResponsesMCPTool(ctx context.Context, toolCall *schemas.ResponsesToolMessage) (*schemas.ResponsesMessage, *schemas.BifrostError) {
if bifrost.mcpManager == nil {
return nil, &schemas.BifrostError{
IsBifrostError: false,
Error: &schemas.ErrorField{
Message: "MCP is not configured in this Bifrost instance",
},
ExtraFields: schemas.BifrostErrorExtraFields{
RequestType: schemas.ResponsesRequest, // MCP tools are used with responses requests
},
}
}

result, err := bifrost.mcpManager.ExecuteResponsesTool(ctx, toolCall)
if err != nil {
return nil, &schemas.BifrostError{
IsBifrostError: false,
Error: &schemas.ErrorField{
Message: err.Error(),
},
ExtraFields: schemas.BifrostErrorExtraFields{
RequestType: schemas.ResponsesRequest, // MCP tools are used with responses requests
},
}
}

return result, nil
}

// IMPORTANT: Running the MCP client management operations (GetMCPClients, AddMCPClient, RemoveMCPClient, EditMCPClientTools)
// may temporarily increase latency for incoming requests while the operations are being processed.
// These operations involve network I/O and connection management that require mutex locks
Expand All @@ -1741,12 +1824,9 @@ func (bifrost *Bifrost) GetMCPClients() ([]schemas.MCPClient, error) {
return nil, fmt.Errorf("MCP is not configured in this Bifrost instance")
}

clients, err := bifrost.mcpManager.GetClients()
if err != nil {
return nil, err
}

clients := bifrost.mcpManager.GetClients()
clientsInConfig := make([]schemas.MCPClient, 0, len(clients))

for _, client := range clients {
tools := make([]schemas.ChatToolFunction, 0, len(client.ToolMap))
for _, tool := range client.ToolMap {
Expand All @@ -1759,21 +1839,27 @@ func (bifrost *Bifrost) GetMCPClients() ([]schemas.MCPClient, error) {
return tools[i].Name < tools[j].Name
})

state := schemas.MCPConnectionStateConnected
if client.Conn == nil {
state = schemas.MCPConnectionStateDisconnected
}

clientsInConfig = append(clientsInConfig, schemas.MCPClient{
Config: client.ExecutionConfig,
Tools: tools,
State: state,
State: client.State,
})
}

return clientsInConfig, nil
}

// GetAvailableTools returns the available tools for the given context.
//
// Returns:
// - []schemas.ChatTool: List of available tools
func (bifrost *Bifrost) GetAvailableMCPTools(ctx context.Context) []schemas.ChatTool {
if bifrost.mcpManager == nil {
return nil
}
return bifrost.mcpManager.GetAvailableTools(ctx)
}

// AddMCPClient adds a new MCP client to the Bifrost instance.
// This allows for dynamic MCP client management at runtime.
//
Expand All @@ -1792,13 +1878,17 @@ func (bifrost *Bifrost) GetMCPClients() ([]schemas.MCPClient, error) {
// })
func (bifrost *Bifrost) AddMCPClient(config schemas.MCPClientConfig) error {
if bifrost.mcpManager == nil {
manager := &MCPManager{
ctx: bifrost.ctx,
clientMap: make(map[string]*MCPClient),
logger: bifrost.logger,
}
// Use sync.Once to ensure thread-safe initialization
bifrost.mcpInitOnce.Do(func() {
bifrost.mcpManager = mcp.NewMCPManager(bifrost.ctx, schemas.MCPConfig{
ClientConfigs: []schemas.MCPClientConfig{config},
}, bifrost.logger)
})
}

bifrost.mcpManager = manager
// Handle case where initialization succeeded elsewhere but manager is still nil
if bifrost.mcpManager == nil {
return fmt.Errorf("MCP manager is not initialized")
}

return bifrost.mcpManager.AddClient(config)
Expand Down Expand Up @@ -1866,6 +1956,22 @@ func (bifrost *Bifrost) ReconnectMCPClient(id string) error {
return bifrost.mcpManager.ReconnectClient(id)
}

// UpdateToolManagerConfig updates the tool manager config for the MCP manager.
// This allows for hot-reloading of the tool manager config at runtime.
func (bifrost *Bifrost) UpdateToolManagerConfig(maxAgentDepth int, toolExecutionTimeoutInSeconds int, codeModeBindingLevel string) error {
if bifrost.mcpManager == nil {
return fmt.Errorf("MCP is not configured in this Bifrost instance")
}

bifrost.mcpManager.UpdateToolManagerConfig(&schemas.MCPToolManagerConfig{
MaxAgentDepth: maxAgentDepth,
ToolExecutionTimeout: time.Duration(toolExecutionTimeoutInSeconds) * time.Second,
CodeModeBindingLevel: schemas.CodeModeBindingLevel(codeModeBindingLevel),
})
return nil
}


// PROVIDER MANAGEMENT

// createBaseProvider creates a provider based on the base provider type
Expand Down Expand Up @@ -2378,11 +2484,8 @@ func (bifrost *Bifrost) tryRequest(ctx context.Context, req *schemas.BifrostRequ
}

// Add MCP tools to request if MCP is configured and requested
if req.RequestType != schemas.EmbeddingRequest &&
req.RequestType != schemas.SpeechRequest &&
req.RequestType != schemas.TranscriptionRequest &&
bifrost.mcpManager != nil {
req = bifrost.mcpManager.addMCPToolsToBifrostRequest(ctx, req)
if bifrost.mcpManager != nil {
req = bifrost.mcpManager.AddToolsToRequest(ctx, req)
}

pipeline := bifrost.getPluginPipeline()
Expand Down Expand Up @@ -2498,7 +2601,7 @@ func (bifrost *Bifrost) tryStreamRequest(ctx context.Context, req *schemas.Bifro

// Add MCP tools to request if MCP is configured and requested
if req.RequestType != schemas.SpeechStreamRequest && req.RequestType != schemas.TranscriptionStreamRequest && bifrost.mcpManager != nil {
req = bifrost.mcpManager.addMCPToolsToBifrostRequest(ctx, req)
req = bifrost.mcpManager.AddToolsToRequest(ctx, req)
}

pipeline := bifrost.getPluginPipeline()
Expand Down Expand Up @@ -2690,7 +2793,7 @@ func executeRequestWithRetries[T any](

// Calculate and apply backoff
backoff := calculateBackoff(attempts-1, config)
logger.Debug("sleeping for %s", backoff)
logger.Debug("sleeping for %s before retry", backoff)
time.Sleep(backoff)
}

Expand Down Expand Up @@ -3516,7 +3619,7 @@ func (bifrost *Bifrost) Shutdown() {

// Cleanup MCP manager
if bifrost.mcpManager != nil {
err := bifrost.mcpManager.cleanup()
err := bifrost.mcpManager.Cleanup()
if err != nil {
bifrost.logger.Warn(fmt.Sprintf("Error cleaning up MCP manager: %s", err.Error()))
}
Expand Down
3 changes: 3 additions & 0 deletions core/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- feat: added code mode to mcp
- feat: added health monitoring to mcp
- feat: added responses format tool execution support to mcp
2 changes: 1 addition & 1 deletion core/chatbot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ func (s *ChatSession) handleToolCalls(assistantMessage schemas.ChatMessage) (str
stopChan, wg := startLoader()

// Execute the tool using Bifrost's integrated MCP functionality
toolResult, err := s.client.ExecuteMCPTool(context.Background(), toolCall)
toolResult, err := s.client.ExecuteChatMCPTool(context.Background(), toolCall)

// Stop loading animation
stopLoader(stopChan, wg)
Expand Down
6 changes: 6 additions & 0 deletions core/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.94.0
github.com/aws/smithy-go v1.24.0
github.com/bytedance/sonic v1.14.2
github.com/clarkmcc/go-typescript v0.7.0
github.com/dop251/goja v0.0.0-20251103141225-af2ceb9156d7
github.com/google/uuid v1.6.0
github.com/hajimehoshi/go-mp3 v0.3.4
github.com/mark3labs/mcp-go v0.43.2
Expand All @@ -22,6 +24,7 @@ require (

require (
cloud.google.com/go/compute/metadata v0.9.0 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
Expand All @@ -42,6 +45,9 @@ require (
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
Expand Down
Loading
Loading