Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 4 additions & 10 deletions core/providers/bedrock/bedrock.go
Original file line number Diff line number Diff line change
Expand Up @@ -962,10 +962,6 @@ func (provider *BedrockProvider) TextCompletionStream(ctx *schemas.BifrostContex
Message: schemas.ErrProviderNetworkError,
Error: err,
},
ExtraFields: schemas.BifrostErrorExtraFields{
RequestType: schemas.TextCompletionStreamRequest,
Provider: providerName,
},
}, responseChan, provider.logger)
} else {
providerUtils.ProcessAndSendError(ctx, postHookRunner, err, responseChan, provider.logger)
Expand Down Expand Up @@ -1139,7 +1135,6 @@ func (provider *BedrockProvider) ChatCompletionStream(ctx *schemas.BifrostContex
responseChan := make(chan *schemas.BifrostStreamChunk, schemas.DefaultStreamBufferSize)

providerUtils.SetStreamIdleTimeoutIfEmpty(ctx, provider.networkConfig.StreamIdleTimeoutInSeconds)
providerName := provider.GetProviderKey()
// Start streaming in a goroutine
go func() {
defer func() {
Expand Down Expand Up @@ -1202,7 +1197,7 @@ func (provider *BedrockProvider) ChatCompletionStream(ctx *schemas.BifrostContex
break
}
ctx.SetValue(schemas.BifrostContextKeyStreamEndIndicator, true)
provider.logger.Warn("Error decoding %s EventStream message: %v", providerName, err)
provider.logger.Warn("Error decoding EventStream message: %v", err)
Comment thread
Pratham-Mishra04 marked this conversation as resolved.
// Transport-level errors (stale/closed connection, unexpected EOF) are retryable.
// Use IsBifrostError:false so the retry gate in executeRequestWithRetries can retry.
if isStreamTransportError(err) {
Expand Down Expand Up @@ -1230,7 +1225,7 @@ func (provider *BedrockProvider) ChatCompletionStream(ctx *schemas.BifrostContex
}
}
errMsg := string(message.Payload)
err := fmt.Errorf("%s stream %s: %s", providerName, excType, errMsg)
err := fmt.Errorf("stream %s: %s", excType, errMsg)
// Retryable AWS exceptions must not set IsBifrostError:true — that would
// bypass the retry gate in executeRequestWithRetries. Instead emit
// IsBifrostError:false with the equivalent HTTP status code so the existing
Expand Down Expand Up @@ -1533,7 +1528,6 @@ func (provider *BedrockProvider) ResponsesStream(ctx *schemas.BifrostContext, po
lastChunkTime := startTime
decoder := eventstream.NewDecoder()
payloadBuf := make([]byte, 0, 1024*1024) // 1MB payload buffer
providerName := provider.GetProviderKey()
for {
// If context was cancelled/timed out, let defer handle it
if ctx.Err() != nil {
Expand Down Expand Up @@ -1575,7 +1569,7 @@ func (provider *BedrockProvider) ResponsesStream(ctx *schemas.BifrostContext, po
break
}
ctx.SetValue(schemas.BifrostContextKeyStreamEndIndicator, true)
provider.logger.Warn("Error decoding %s EventStream message: %v", providerName, err)
provider.logger.Warn("Error decoding EventStream message: %v", err)
// Transport-level errors (stale/closed connection, unexpected EOF) are retryable.
// Use IsBifrostError:false so the retry gate in executeRequestWithRetries can retry.
if isStreamTransportError(err) {
Expand Down Expand Up @@ -1603,7 +1597,7 @@ func (provider *BedrockProvider) ResponsesStream(ctx *schemas.BifrostContext, po
}
}
errMsg := string(message.Payload)
err := fmt.Errorf("%s stream %s: %s", providerName, excType, errMsg)
err := fmt.Errorf("stream %s: %s", excType, errMsg)
// Retryable AWS exceptions must not set IsBifrostError:true — that would
// bypass the retry gate in executeRequestWithRetries. Instead emit
// IsBifrostError:false with the equivalent HTTP status code so the existing
Expand Down
1 change: 1 addition & 0 deletions core/providers/openai/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -1772,6 +1772,7 @@ func HandleOpenAIResponsesStreaming(
providerUtils.ProcessAndSendBifrostError(ctx, postHookRunner, providerUtils.EnrichError(ctx, bifrostErr, jsonBody, nil, sendBackRawRequest, sendBackRawResponse), responseChan, logger)
return
}

response.ExtraFields.ChunkIndex = response.SequenceNumber
if response.Type == schemas.ResponsesStreamResponseTypeCompleted || response.Type == schemas.ResponsesStreamResponseTypeIncomplete {
// Set raw request if enabled
Expand Down
185 changes: 185 additions & 0 deletions docs/mcp/connecting-to-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,191 @@ Environment variables are:

---

## Forwarding Request Headers to MCP Servers

<Info>
Header Forwarding is available in **v1.5.0-prerelease1 and above**.
</Info>

By default, Bifrost does not forward incoming request headers to MCP servers during tool execution. The `allowed_extra_headers` field lets you define a per-client allowlist of headers that callers may inject at request time and have forwarded to that MCP server when tools are executed.

This is separate from the static `headers` field used for authentication:

| Field | Purpose | When sent |
|-------|---------|-----------|
| `headers` | Static auth credentials (API keys, tokens) | Always, on every tool call |
| `allowed_extra_headers` | Dynamic per-request headers from callers | Only when the caller provides them, and only if they match the allowlist |

**Common use cases:**
- Forwarding a user's auth token to an MCP server that enforces per-user authorization
- Passing a tenant or org ID to a multi-tenant MCP server
- Propagating trace or correlation IDs for end-to-end observability

### How It Works

1. An incoming request carries one or more headers matching a client's `allowed_extra_headers` pattern
2. Bifrost captures those headers from the request (using the union of all clients' allowlists)
3. At tool execution time, each client **re-checks** the header against its own allowlist — so the same header can be forwarded to one MCP server but not another

<Note>
Headers are matched case-insensitively. The only wildcard supported is a standalone `"*"` (allow all headers) — partial patterns like `x-tenant-*` are not supported. If `"*"` is used, it must be the only entry in the list.
</Note>

<Tabs>
<Tab title="UI">

**Configure:** Navigate to **MCP Gateway**, open the configuration sheet for an HTTP or SSE client, and set the **Allowed Extra Headers** field:

<Frame>
<img src="/media/ui-mcp-allowed-extra-headers.png" alt="Allowed Extra Headers configuration in the MCP client edit sheet" />
</Frame>

**Send headers:** Include the allowed headers in any inference request to the LLM gateway:

```bash
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-user-token: eyJhbGci..." \
-H "x-tenant-id: acme-corp" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Look up my account details"}]
}'
```

</Tab>
<Tab title="Management API">

**Configure:** Include `allowed_extra_headers` when creating or updating a client:

```bash
curl -X POST http://localhost:8080/api/mcp/client \
-H "Content-Type: application/json" \
-d '{
"name": "my_api",
"connection_type": "http",
"connection_string": "https://mcp.example.com/mcp",
"auth_type": "headers",
"headers": {
"Authorization": "Bearer service-token"
},
"allowed_extra_headers": ["x-user-token", "x-tenant-id", "x-request-id"],
"tools_to_execute": ["*"]
}'
```

**Send headers:** Include the allowed headers in any inference request:

```bash
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-user-token: eyJhbGci..." \
-H "x-tenant-id: acme-corp" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Look up my account details"}]
}'
```

</Tab>
<Tab title="Config File">

**Configure:**

```json
{
"mcp": {
"client_configs": [
{
"name": "my_api",
"connection_type": "http",
"connection_string": "https://mcp.example.com/mcp",
"auth_type": "headers",
"headers": {
"Authorization": "Bearer service-token"
},
"allowed_extra_headers": ["x-user-token", "x-tenant-id", "x-request-id"],
"tools_to_execute": ["*"]
}
]
}
}
```

**Send headers:** Include the allowed headers in any inference request:

```bash
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "x-user-token: eyJhbGci..." \
-H "x-tenant-id: acme-corp" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Look up my account details"}]
}'
```

</Tab>
<Tab title="MCP Gateway (/mcp)">

**Configure** the client as above (Web UI, Management API, or config.json).

**Send headers:** When an external MCP client (e.g., Claude Desktop, Cursor) connects to Bifrost's `/mcp` endpoint, include the allowed headers in that HTTP request. Bifrost forwards them during any tool call made within that session:

```json
{
"mcpServers": {
"bifrost": {
"url": "http://localhost:8080/mcp",
"headers": {
"x-user-token": "eyJhbGci...",
"x-tenant-id": "acme-corp"
}
}
}
}
```

<Note>
Header support in MCP client config varies by client. The above JSON format applies to clients that support custom headers (e.g., Claude Desktop, Cursor). Check your MCP client's documentation for the exact configuration syntax.
</Note>

</Tab>
<Tab title="Go SDK">

**Configure:**

```go
schemas.MCPClientConfig{
Name: "my_api",
ConnectionType: schemas.MCPConnectionTypeHTTP,
ConnectionString: bifrost.Ptr("https://mcp.example.com/mcp"),
AuthType: schemas.MCPAuthTypeHeaders,
Headers: map[string]schemas.EnvVar{
"Authorization": {Value: "Bearer service-token"},
},
AllowedExtraHeaders: schemas.WhiteList{"x-user-token", "x-tenant-id", "x-request-id"},
ToolsToExecute: []string{"*"},
}
```

**Send headers:** Set `BifrostContextKeyMCPExtraHeaders` on the context before calling `ChatCompletionRequest` or `ExecuteChatMCPTool`:

```go
bifrostCtx := schemas.NewBifrostContext(context.Background(), schemas.NoDeadline)
bifrostCtx.SetValue(schemas.BifrostContextKeyMCPExtraHeaders, map[string][]string{
"x-user-token": {"eyJhbGci..."},
"x-tenant-id": {"acme-corp"},
})

response, err := client.ChatCompletionRequest(bifrostCtx, request)
```

</Tab>
</Tabs>

---

## Client State Management

### Connection States
Expand Down
Binary file added docs/media/ui-mcp-allowed-extra-headers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading