Skip to main content

Overview

Model Context Protocol (MCP) enables AI models to seamlessly discover and execute external tools at runtime, transforming static chat models into dynamic, action-capable agents. Instead of being limited to text generation, AI models can interact with filesystems, search the web, query databases, and execute custom business logic through external MCP servers. Bifrost’s MCP integration provides a secure, high-performance bridge between AI models and external tools, with client-side control over all tool execution and granular filtering capabilities. 🔒 Security-First Design: Bifrost never automatically executes tool calls. Instead, it provides APIs for explicit tool execution, ensuring human oversight and approval for all potentially dangerous operations. Key Benefits:
FeatureDescription
Dynamic DiscoveryTools are discovered at runtime from external MCP servers
Stateless DesignIndependent API calls with no session state management
Client-Side ControlBifrost manages all tool execution for security and observability
Multiple ProtocolsSTDIO, HTTP, and SSE connection types
Granular FilteringControl tool availability per request and client
High PerformanceAsync execution with minimal latency overhead
Copy-Pastable ResponsesTool results designed for seamless conversation assembly

How MCP Works in Bifrost

Bifrost acts as an MCP client that connects to external MCP servers hosting tools. The integration is completely stateless with independent API calls:
  1. Discovery: Bifrost connects to configured MCP servers and discovers available tools
  2. Integration: Tools are automatically added to the AI model’s function calling schema
  3. Suggestion: Chat completion requests return tool call suggestions (not executed)
  4. Execution: Separate tool execution API calls execute specific tool calls
  5. Assembly: Your application manages conversation state and assembles chat history
  6. Continuation: Follow-up chat requests use the complete conversation history
Stateless Tool Flow:
Chat Request → Tool Call Suggestions (Independent)

Tool Execution Request → Tool Results (Independent)  

Your App Assembles History → Continue Chat (Independent)
Bifrost never automatically executes tool calls. All API calls are independent and stateless:
  • Chat completions return tool call suggestions without executing them
  • Tool execution requires separate API calls with explicit tool call data
  • No state management - your application controls conversation flow
  • Copy-pastable responses designed for easy conversation assembly
This design prevents:
  • Unintended API calls to external services
  • Accidental data modification or deletion
  • Execution of potentially harmful commands
Implementation Pattern:
1. POST /v1/chat/completions → Get tool call suggestions (stateless)
2. Your App Reviews Tool Calls → Decides which to execute
3. POST /v1/mcp/tool/execute → Execute specific tool calls (stateless)  
4. Your App Assembles History → Continue with complete conversation
This stateless pattern ensures explicit control over all tool operations while providing responses optimized for conversation continuity.

Setup Guides

Go SDK Setup

Configure MCP in your Bifrost initialization:
package main

import (
    "github.com/maximhq/bifrost/core"
    "github.com/maximhq/bifrost/core/schemas"
)

func main() {
    mcpConfig := &schemas.MCPConfig{
        ClientConfigs: []schemas.MCPClientConfig{
            {
                Name:           "filesystem-tools",
                ConnectionType: schemas.MCPConnectionTypeSTDIO,
                StdioConfig: &schemas.MCPStdioConfig{
                    Command: "node",
                    Args:    []string{"filesystem-mcp-server.js"},
                },
                ToolsToExecute: []string{"read_file", "write_file"},
            },
            {
                Name:             "web-search",
                ConnectionType:   schemas.MCPConnectionTypeHTTP,
                ConnectionString: bifrost.Ptr("http://localhost:3001/mcp"),
                ToolsToExecute: []string{"*"}, // Allow all tools from this client
            },
        },
    }
    // ToolsToExecute semantics for MCPClientConfig:
	// - ["*"] => all tools are included
	// - []    => no tools are included (deny-by-default)
	// - nil/omitted => treated as [] (no tools)
	// - ["tool1", "tool2"] => include only the specified tools

    // Initialize Bifrost with MCP configuration
    client, err := bifrost.Init(context.Background(), schemas.BifrostConfig{
        Account:   account,
        MCPConfig: mcpConfig,
        Logger:    bifrost.NewDefaultLogger(schemas.LogLevelInfo),
    })
    if err != nil {
        panic(err)
    }
}
Note: Bifrost needs to be initialized with the MCP configuration(even an empty MCP config is fine) before using the MCP Methods. Read more about runtime MCP client management here.

Gateway Setup

  • Web UI
  • API
  • config.json
MCP Configuration in Web UI
  1. Navigate to MCP Clients in the Bifrost Gateway UI
  2. Click New MCP Client
  3. Configure connection details:
    • Name: Unique identifier for the MCP client
    • Connection Type: STDIO, HTTP, or SSE
    • Connection Details: Command/URL based on connection type
By default, all tools from the MCP client are included. You can update the tools to be included after the MCP client is created.MCP Tools Configuration in Web UI

Connection Types

STDIO Connection

STDIO connections launch external processes and communicate via standard input/output. Best for local tools and scripts. Configuration:
{
  "name": "local-tools",
  "connection_type": "stdio", 
  "stdio_config": {
    "command": "python",
    "args": ["-m", "my_mcp_server"],
    "envs": ["PYTHON_PATH", "API_KEY"]
  }
}
Use Cases:
  • Local filesystem operations
  • Database queries with local credentials
  • Python/Node.js MCP servers
  • Custom business logic scripts

HTTP Connection

HTTP connections communicate with MCP servers via HTTP requests. Ideal for remote services and microservices. Configuration:
{
  "name": "remote-api",
  "connection_type": "http",
  "connection_string": "https://mcp-server.example.com/api"
}
Use Cases:
  • Remote API integrations
  • Cloud-hosted MCP services
  • Microservice architectures
  • Third-party tool providers

SSE Connection

Server-Sent Events (SSE) connections provide real-time, persistent connections to MCP servers. Best for streaming data and live updates. Configuration:
{
  "name": "live-data",
  "connection_type": "sse",
  "connection_string": "https://stream.example.com/mcp/events"
}
Use Cases:
  • Real-time market data
  • Live system monitoring
  • Streaming analytics
  • Event-driven workflows

End-to-End Tool Calling

  • Go SDK
  • Gateway
Complete tool calling workflow with the Go SDK:
package main

import (
    "context"
    "fmt"
    "github.com/maximhq/bifrost/core"
    "github.com/maximhq/bifrost/core/schemas"
)

func main() {
    // Initialize Bifrost with MCP
    client, err := bifrost.Init(context.Background(), schemas.BifrostConfig{
        Account: account,
        MCPConfig: &schemas.MCPConfig{
            ClientConfigs: []schemas.MCPClientConfig{
                {
                    Name:           "filesystem",
                    ConnectionType: schemas.MCPConnectionTypeSTDIO,
                    StdioConfig: &schemas.MCPStdioConfig{
                        Command: "node",
                        Args:    []string{"fs-mcp-server.js"},
                    },
                    ToolsToExecute: []string{"*"},
                },
            },
        },
    })

    firstMessage := schemas.ChatMessage{
        Role: schemas.ChatMessageRoleUser,
        Content: schemas.ChatMessageContent{
            ContentStr: bifrost.Ptr("Read the contents of config.json file"),
        },
    }

    // Create request with tools automatically included
    request := &schemas.BifrostChatRequest{
        Provider: schemas.OpenAI,
        Model:    "gpt-4o-mini",
        Input: []schemas.ChatMessage{
            firstMessage,
        },
        Params: &schemas.ChatParameters{
            Temperature: bifrost.Ptr(0.7),
        },
    }

    // Send chat completion request - MCP tools are automatically available  
    response, err := client.ChatCompletionRequest(context.Background(), request)
    if err != nil {
        panic(err)
    }

    // Build conversation history for final response
    conversationHistory := []schemas.ChatMessage{
        firstMessage,
    }

    // Handle tool calls in response (suggestions only - not executed)
    if response.Choices[0].Message.ToolCalls != nil {
        secondMessage := response.Choices[0].Message
        
        // Add assistant message with tool calls to history
        conversationHistory = append(conversationHistory, secondMessage)

        for _, toolCall := range *secondMessage.ToolCalls {
            fmt.Printf("Tool suggested: %s\n", *toolCall.Function.Name)
            
            // YOUR APPLICATION DECISION: Review the tool call
            // - Validate tool name and arguments
            // - Apply security and business rules  
            // - Check permissions and rate limits
            // - Decide whether to execute
            
            shouldExecute := validateToolCall(toolCall) // Your validation logic
            if !shouldExecute {
                fmt.Printf("Tool call rejected by application\n")
                continue
            }
            
            // EXPLICIT EXECUTION: Separate API call
            thirdMessage, err := client.ExecuteMCPTool(context.Background(), toolCall)
            if err != nil {
                fmt.Printf("Tool execution failed: %v\n", err)
                continue
            }
            
            fmt.Printf("Tool result: %s\n", *thirdMessage.Content.ContentStr)
            
            // Add tool result to conversation history
            conversationHistory = append(conversationHistory, thirdMessage)
        }

        // Send complete conversation history for final response
        finalRequest := &schemas.BifrostChatRequest{
            Provider: schemas.OpenAI,
            Model:    "gpt-4o-mini",
            Input: conversationHistory,
            Params: &schemas.ChatParameters{
                Temperature: bifrost.Ptr(0.7),
            },
        }

        finalResponse, err := client.ChatCompletionRequest(context.Background(), finalRequest)
        if err != nil {
            panic(err)
        }

        fmt.Printf("Final response: %s\n", *finalResponse.Choices[0].Message.Content.ContentStr)
    }
}

Tool Registry (Go SDK Only)

The Go SDK provides a powerful tool registry for hosting custom tools directly within your application using typed handlers.
package main

import (
    "fmt"
    "strings"
    "github.com/maximhq/bifrost/core"
    "github.com/maximhq/bifrost/core/schemas"
)

// Define typed arguments for your tool
type CalculatorArgs struct {
    Operation string  `json:"operation"` // add, subtract, multiply, divide
    A         float64 `json:"a"`
    B         float64 `json:"b"`
}

// Define typed tool handler
func calculatorHandler(args CalculatorArgs) (string, error) {
    switch strings.ToLower(args.Operation) {
    case "add":
        return fmt.Sprintf("%.2f", args.A + args.B), nil
    case "subtract": 
        return fmt.Sprintf("%.2f", args.A - args.B), nil
    case "multiply":
        return fmt.Sprintf("%.2f", args.A * args.B), nil
    case "divide":
        if args.B == 0 {
            return "", fmt.Errorf("cannot divide by zero")
        }
        return fmt.Sprintf("%.2f", args.A / args.B), nil
    default:
        return "", fmt.Errorf("unsupported operation: %s", args.Operation)
    }
}

func main() {
    // Initialize Bifrost (tool registry creates in-process MCP automatically)
    client, err := bifrost.Init(context.Background(), schemas.BifrostConfig{
        Account: account,
        Logger:  bifrost.NewDefaultLogger(schemas.LogLevelInfo),
    })

    // Define tool schema
    calculatorSchema := schemas.ChatTool{
        Type: "function",
        Function: schemas.ChatToolFunction{
            Name:        "calculator",
            Description: "Perform basic arithmetic operations",
            Parameters: schemas.ToolFunctionParameters{
                Type: "object",
                Properties: map[string]interface{}{
                    "operation": map[string]interface{}{
                        "type":        "string",
                        "description": "The operation to perform",
                        "enum":        []string{"add", "subtract", "multiply", "divide"},
                    },
                    "a": map[string]interface{}{
                        "type":        "number", 
                        "description": "First number",
                    },
                    "b": map[string]interface{}{
                        "type":        "number",
                        "description": "Second number",
                    },
                },
                Required: []string{"operation", "a", "b"},
            },
        },
    }

    // Register the typed tool
    err = client.RegisterMCPTool("calculator", "Perform arithmetic calculations", 
        func(args any) (string, error) {
            // Convert args to typed struct
            calculatorArgs := CalculatorArgs{}
            if jsonBytes, err := json.Marshal(args); err == nil {
                json.Unmarshal(jsonBytes, &calculatorArgs)
            }
            return calculatorHandler(calculatorArgs)
        }, calculatorSchema)

    if err != nil {
        panic(fmt.Sprintf("Failed to register tool: %v", err))
    }

    // Now use the tool in requests
    request := &schemas.BifrostChatRequest{
        Provider: schemas.OpenAI,
        Model:    "gpt-4o-mini",
        Input: []schemas.ChatMessage{
            {
                Role: schemas.ChatMessageRoleUser,
                Content: schemas.ChatMessageContent{
                    ContentStr: bifrost.Ptr("Calculate 15.5 + 24.3"),
                },
            },
        },
        Params: &schemas.ChatParameters{
            Temperature: bifrost.Ptr(0.7),
        },
    }

    response, err := client.ChatCompletionRequest(context.Background(), request)
    // The model can now use the calculator tool automatically
}
Tool Registry Benefits:
  • Type Safety: Compile-time checking of tool arguments and return types
  • Performance: In-process execution with zero network overhead
  • Simplicity: No external MCP server setup required
  • Integration: Tools are automatically available to all AI requests
  • Error Handling: Structured error responses with detailed context

Advanced Configuration

Tool and Client Filtering

Control which tools and clients are available per request or globally: Request-Level Filtering:
  • Go SDK
  • Gateway
Use context values to filter clients and tools per request:
// Include only specific clients
ctx := context.WithValue(context.Background(), "mcp-include-clients", []string{"filesystem", "web-search"})

// Include only specific tools (use clientName/toolName format)
ctx = context.WithValue(ctx, "mcp-include-tools", []string{"web-search/search", "filesystem/read_file"})

// Use wildcard to include all tools from a specific client
ctx = context.WithValue(ctx, "mcp-include-tools", []string{"web-search/*", "filesystem/read_file"})

// Use wildcard to include all clients
ctx = context.WithValue(ctx, "mcp-include-clients", []string{"*"})

response, err := client.ChatCompletionRequest(ctx, request)
Filtering Logic: The client’s configuration (ToolsToExecute) defines the set of enabled tools for that client. The request-level mcp-include-tools list can then be used to select a subset of those tools for a specific request. If mcp-include-tools is not provided, all tools enabled by the client’s configuration are available.
  • Include lists are strict whitelists: If include-clients/include-tools is specified, ONLY those clients/tools are allowed.
  • Wildcard support: Use * to include all clients. For tools, use * in the client configuration to include all its tools. At the request level, use <clientName>/* to include all tools from a specific client.
  • Empty array behavior: An empty array [] means no clients/tools are included.

Environment Variables

Use environment variables for sensitive configuration: Gateway:
{
  "name": "secure-api",
  "connection_type": "http",
  "connection_string": "env.SECURE_MCP_URL",  // References $SECURE_MCP_URL
  "stdio_config": {
    "command": "python",
    "args": ["-m", "secure_server"],
    "envs": ["API_SECRET", "DATABASE_URL"]     // Required environment variables
  }
}
Environment variables are:
  • Automatically resolved during client connection
  • Redacted in API responses and UI for security
  • Validated at startup to ensure all required variables are set

Client State Management

Monitor and manage MCP client connections:
  • Go SDK
  • Gateway API
// Get all connected clients and their status
clients, err := client.GetMCPClients()
for _, mcpClient := range clients {
    fmt.Printf("Client: %s, State: %s, Tools: %v\n", 
        mcpClient.Name, mcpClient.State, mcpClient.Tools)
}

// Reconnect a disconnected client
err = client.ReconnectMCPClient("filesystem-tools")

// Add new client at runtime  
err = client.AddMCPClient(newClientConfig)

// Remove client
err = client.RemoveMCPClient("old-client")

// Edit client tools
err = client.EditMCPClientTools("filesystem-tools", 
    []string{"read_file", "write_file"}) // tools to be included
Connection States:
  • Connected: Client is active and tools are available
  • Connecting: Client is establishing connection
  • Disconnected: Client lost connection but can be reconnected
  • Error: Client configuration or connection failed

Architecture Details

For detailed information about MCP’s internal architecture, concurrency model, tool discovery process, and performance characteristics, see the MCP Architecture Guide.