Skip to content

openai harness fixes and mcp test fixes - #6089

Closed
akshaydeo wants to merge 1 commit into
graphite-base/6089from
08-11-openai_harness_fixes_and_mcp_test_fixes
Closed

openai harness fixes and mcp test fixes#6089
akshaydeo wants to merge 1 commit into
graphite-base/6089from
08-11-openai_harness_fixes_and_mcp_test_fixes

Conversation

@akshaydeo

Copy link
Copy Markdown
Contributor

Summary

Briefly explain the purpose of this PR and the problem it solves.

Changes

  • What was changed and why
  • Any notable design decisions or trade-offs

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

Describe the steps to validate this change. Include commands and expected outcomes.

# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build

If adding new configs or environment variables, document them here.

Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

Breaking changes

  • Yes
  • No

If yes, describe impact and migration instructions.

Related issues

Link related issues and discussions. Example: Closes #123

Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d1465482-e9c9-45a4-9d84-159967a3efb4

📥 Commits

Reviewing files that changed from the base of the PR and between 079c9f9 and 90b244c.

📒 Files selected for processing (17)
  • core/providers/anthropic/reasoningdialect_test.go
  • core/providers/bedrock/adaptivethinkingdisplay_test.go
  • core/providers/bedrock/responses.go
  • core/providers/bedrock/utils.go
  • core/providers/gemini/functioncallingconfigalias_test.go
  • core/providers/gemini/generationconfigalias_test.go
  • core/providers/gemini/responses.go
  • core/providers/gemini/toolconfigforcedchoice_test.go
  • core/providers/gemini/types.go
  • core/providers/openai/chat.go
  • core/providers/openai/chatfileurl.go
  • core/providers/openai/chatfileurl_test.go
  • core/schemas/responses.go
  • core/schemas/toolchoicemarshal_test.go
  • plugins/modelcatalogresolver/main.go
  • tests/e2e/api/collections/provider-harness.json
  • tests/e2e/api/runners/lib/newman-merge.jq

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Gemini request payloads accept both camelCase and snake_case field names.
    • Responses API instructions are preserved as leading system messages in chat requests.
    • OpenAI chat requests can process document file URLs automatically.
    • Rate-limited test requests support configurable retries and backoff.
  • Bug Fixes

    • Improved MIME type detection for Gemini file URLs, including query strings, fragments, and caller-specified types.
    • Improved tool-choice compatibility across Gemini, Bedrock, and Responses APIs.
    • Adaptive reasoning display settings are now handled more consistently.
    • Retry results are merged accurately.

Walkthrough

The PR adds remote MCP test infrastructure, shared gateway artifact usage, provider request compatibility, Responses instruction conversion, OpenAI file URL handling, rate-limit retries, shard monitoring, and release workflow configuration.

Changes

Integration and release test infrastructure

Layer / File(s) Summary
Remote MCP and gateway test setup
.github/workflows/*, .github/workflows/scripts/test-integrations.sh, examples/mcps/remote-test-server/*, core/internal/mcptests/*, tests/integrations/python/config.json
Release jobs reuse gateway artifacts, build MCP fixtures, configure provider credentials, and run local HTTP and SSE MCP tests.

Provider and request compatibility

Layer / File(s) Summary
Gemini request and tool conversion
core/providers/gemini/*
Gemini decoding accepts snake_case aliases. Tool choices preserve allowed-function behavior. File conversions infer MIME types from recognized URI extensions.
Cross-provider request normalization
core/providers/openai/*, core/providers/bedrock/*, core/schemas/*
OpenAI chat file URLs are inlined. Responses instructions become leading system messages. Tool-choice and Bedrock reasoning fields use provider-compatible forms.

Provider harness execution

Layer / File(s) Summary
Rate-limit retry orchestration
Makefile, tests/e2e/api/runners/filter-collection.mjs, tests/e2e/api/runners/lib/*, tests/e2e/api/runners/rate-limit-backoff.mjs
The harness detects 429 results, applies bounded backoff, retries selected requests, and merges recovered reports.
Shard monitoring and validation
tests/e2e/api/runners/harness-monitor.mjs, tests/e2e/api/runners/lib/monitor-shards.test.mjs
Shard progress is aggregated and rendered in CI and interactive modes. Retry logs remain visible but do not duplicate request counts.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant GatewayArtifact
  participant IntegrationScript
  participant MCPServer
  participant IntegrationTests
  CI->>GatewayArtifact: download bifrost-http
  CI->>IntegrationScript: run with SKIP_GATEWAY_BUILD=1
  IntegrationScript->>MCPServer: start local SSE fixture
  IntegrationScript->>IntegrationTests: launch integration suite
  IntegrationTests->>MCPServer: execute MCP tools
  IntegrationScript->>MCPServer: stop fixture during cleanup
Loading

Possibly related PRs

Suggested reviewers: pratham-mishra04, tejasghatte

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (4 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is an unfilled template and lacks the PR purpose, change details, testing steps, affected areas, and checklist selections. Complete the required sections with specific changes, affected areas, test commands and results, breaking-change status, security notes, and checklist selections.
Linked Issues check ⚠️ Warning The changes do not implement the linked issue's Files API support, including a POST /v1/files endpoint or provider file upload workflows. Implement and test the requested Files API support, or link this PR to issues covering the harness, MCP, Gemini, and schema changes.
Out of Scope Changes check ⚠️ Warning Most changes address harness, MCP testing, CI workflows, Gemini decoding, and schema conversion rather than the linked Files API objective. Limit this PR to Files API work or separate the unrelated harness, MCP, CI, Gemini, and schema changes into focused PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 52.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title describes the harness and MCP test changes, but it omits the Gemini, schema, CI, and file-related changes.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 08-11-openai_harness_fixes_and_mcp_test_fixes

Comment @coderabbitai help to get the list of available commands.

@akshaydeo akshaydeo mentioned this pull request Aug 12, 2026
17 tasks

Copy link
Copy Markdown
Contributor Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@akshaydeo
akshaydeo marked this pull request as ready for review August 12, 2026 02:45
@akshaydeo
akshaydeo requested a review from a team as a code owner August 12, 2026 02:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release-pipeline.yml:
- Around line 544-549: Allow the local MCP fixture connections used by
test-core-integrations: update .github/workflows/release-pipeline.yml lines
544-549 to permit the fixed HTTP and SSE fixture ports, and update
core/internal/mcptests/remoteserver_test.go lines 69-76 to use fixed CI-allowed
ports or configure an allowed local port range for the unit fixtures.

In `@core/internal/mcptests/remoteserver_test.go`:
- Around line 57-64: Update the remote fixture setup function after the
MCP_USE_REMOTE=1 opt-out check to immediately clear EnvMCPHTTPServerURL,
EnvMCPSSEServerURL, and both corresponding header variables, before
bifrostRootDir, os.Stat, or server startup can return an error.

In `@core/providers/gemini/types.go`:
- Around line 163-185: Update the alias-selection logic around
SystemInstruction, GenerationConfig, SafetySettings, ToolConfig, CachedContent,
and ServiceTier to track whether each canonical JSON key was present during
unmarshalling, rather than inferring absence from zero values or nil pointers.
Apply each snake_case fallback only when its camelCase key was absent,
preserving explicit empty and null canonical values.
- Around line 2556-2563: Update the envelope in
GeminiCountTokensRequest.UnmarshalJSON to accept both generateContentRequest and
generate_content_request JSON keys, preserving the existing request assignment
behavior. Add a regression test covering a payload using
generate_content_request and verify it populates GenerateContentRequest rather
than producing a zero-value request.

In `@examples/mcps/remote-test-server/go.mod`:
- Around line 5-16: Update the indirect github.com/buger/jsonparser dependency
in go.mod from v1.1.1 to v1.1.2, then run go mod tidy from the
remote-test-server module so go.sum reflects the patched version.

In `@Makefile`:
- Around line 2340-2355: Validate HARNESS_JOBS before entering either wait loop,
requiring it to be a positive integer; reject zero, negative, empty, and
non-numeric values with a clear error and nonzero exit. Update the existing
JOBS_CAP setup near shard_jobs and ensure the validation occurs before the loop
containing wait -n, while preserving the current default when HARNESS_JOBS is
unset.

In `@tests/e2e/api/runners/harness-monitor.mjs`:
- Around line 533-542: Merge retry report verdicts by item.id into the live
shard/provider totals before recordShardCompletion: update the request
accounting at harness-monitor.mjs lines 533-542 and the completion handling at
lines 740-742 so the final retry attempt supersedes the original verdict. In
monitor-shards.test.mjs lines 324-331, assert the recovered request produces
pass === 1 and fail === 0.

In `@tests/e2e/api/runners/lib/rate-limit-retry.mjs`:
- Around line 75-80: Update rateLimitedNames in
tests/e2e/api/runners/lib/rate-limit-retry.mjs:75-80 to collect rate-limited
item.id values, update tests/e2e/api/runners/filter-collection.mjs:291-296 to
match against item.id, and keep Makefile:2468-2471 using the ID-based retry
selector while adding a duplicate-name regression case.
- Around line 36-40: Update retryAfterSeconds to trim the Retry-After header,
reject an empty trimmed value before calling Number, and accept only finite
non-negative integers; return null for blank, fractional, or other malformed
values so callers use the exponential fallback.

In `@tests/integrations/python/config.json`:
- Around line 7-15: The shared test lifecycle must either start
remote-test-server on port 3012 before consumers load
tests/integrations/python/config.json, including harness-gateway.sh and
test-e2e-api.sh, or remove the sse_mcp entry from configurations that do not
exercise MCP; keep the fixture configuration only where its server is guaranteed
to run.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 777bb4a4-48c1-46c0-ac05-07dcb4d5ffe6

📥 Commits

Reviewing files that changed from the base of the PR and between 9a75bc7 and 079c9f9.

⛔ Files ignored due to path filters (2)
  • examples/mcps/remote-test-server/go.sum is excluded by !**/*.sum
  • tests/e2e/api/fixtures/sample.pdf is excluded by !**/*.pdf
📒 Files selected for processing (24)
  • .github/workflows/release-pipeline.yml
  • .github/workflows/scripts/test-integrations.sh
  • Makefile
  • core/internal/mcptests/codemode_tools_test.go
  • core/internal/mcptests/remoteserver_test.go
  • core/providers/gemini/filedatamimefromuri_test.go
  • core/providers/gemini/responses.go
  • core/providers/gemini/systeminstructionalias_test.go
  • core/providers/gemini/types.go
  • core/providers/gemini/utils.go
  • core/schemas/mux.go
  • core/schemas/responsesinstructions_test.go
  • examples/mcps/remote-test-server/go.mod
  • examples/mcps/remote-test-server/main.go
  • tests/e2e/api/collections/provider-harness.json
  • tests/e2e/api/runners/filter-collection.mjs
  • tests/e2e/api/runners/harness-monitor.mjs
  • tests/e2e/api/runners/lib/monitor-shards.test.mjs
  • tests/e2e/api/runners/lib/newman-merge.jq
  • tests/e2e/api/runners/lib/rate-limit-retry.mjs
  • tests/e2e/api/runners/lib/rate-limit-retry.test.mjs
  • tests/e2e/api/runners/rate-limit-backoff.mjs
  • tests/e2e/clis/runner_test.go
  • tests/integrations/python/config.json

Comment on lines +544 to +549
- name: Build MCP test servers
run: make setup-mcp-tests

- name: Run core integration tests
env:
SKIP_GATEWAY_BUILD: "1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allow the local MCP fixture connections.

test-core-egress permits 127.0.0.1:8080, but the integration fixture uses port 3012 and the unit fixture uses arbitrary ports. The readiness probes and MCP client connections are outbound connections. Harden Runner blocks them, so the integration fixture is unreachable and unit remote-transport tests skip.

  • .github/workflows/release-pipeline.yml#L544-L549: allow the fixed local HTTP and SSE fixture ports for test-core-integrations.
  • core/internal/mcptests/remoteserver_test.go#L69-L76: use fixed CI fixture ports that the workflow can allow, or configure an allowed local port range.
📍 Affects 2 files
  • .github/workflows/release-pipeline.yml#L544-L549 (this comment)
  • core/internal/mcptests/remoteserver_test.go#L69-L76
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-pipeline.yml around lines 544 - 549, Allow the
local MCP fixture connections used by test-core-integrations: update
.github/workflows/release-pipeline.yml lines 544-549 to permit the fixed HTTP
and SSE fixture ports, and update core/internal/mcptests/remoteserver_test.go
lines 69-76 to use fixed CI-allowed ports or configure an allowed local port
range for the unit fixtures.

Comment on lines +57 to +64
root, err := bifrostRootDir()
if err != nil {
return nil, err
}
bin := filepath.Join(root, "..", "examples", "mcps", "remote-test-server", "bin", "remote-test-server")
if _, err := os.Stat(bin); err != nil {
return nil, fmt.Errorf("binary not built (run `make setup-mcp-tests`): %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear inherited MCP URLs before fixture setup.

If bifrostRootDir, os.Stat, or server startup fails, this function returns before Lines 102-114 clear the inherited values. GetTestConfig can then target a stale external MCP URL instead of skipping the remote tests.

Clear EnvMCPHTTPServerURL, EnvMCPSSEServerURL, and their header variables immediately after the MCP_USE_REMOTE=1 opt-out check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/internal/mcptests/remoteserver_test.go` around lines 57 - 64, Update the
remote fixture setup function after the MCP_USE_REMOTE=1 opt-out check to
immediately clear EnvMCPHTTPServerURL, EnvMCPSSEServerURL, and both
corresponding header variables, before bifrostRootDir, os.Stat, or server
startup can return an error.

Comment on lines +163 to +185
// camelCase is canonical, so it wins when both are present: letting a stale snake_case field
// overwrite it would change the request out from under a caller who spelled it the modern way.
if g.SystemInstruction == nil && aux.SystemInstructionSnake != nil {
g.SystemInstruction = aux.SystemInstructionSnake
}
// GenerationConfig is a value, not a pointer, so "was it supplied?" is a comparison against the
// zero value rather than a nil check. reflect.DeepEqual rather than == because the struct holds
// slices and maps and is therefore not comparable.
if reflect.DeepEqual(g.GenerationConfig, GenerationConfig{}) && aux.GenerationConfigSnake != nil {
g.GenerationConfig = *aux.GenerationConfigSnake
}
if len(g.SafetySettings) == 0 && len(aux.SafetySettingsSnake) > 0 {
g.SafetySettings = aux.SafetySettingsSnake
}
if g.ToolConfig == nil && aux.ToolConfigSnake != nil {
g.ToolConfig = aux.ToolConfigSnake
}
if g.CachedContent == "" && aux.CachedContentSnake != "" {
g.CachedContent = aux.CachedContentSnake
}
if g.ServiceTier == "" && aux.ServiceTierSnake != "" {
g.ServiceTier = aux.ServiceTierSnake
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve canonical-field presence during alias selection.

The zero-value checks do not prove that a canonical field was absent. For example, "cachedContent": "" with "cached_content": "legacy" makes the snake_case value override the canonical value. The same problem affects empty safetySettings, empty generationConfig, and explicit null pointer fields.

Track JSON key presence separately. Apply snake_case values only when the corresponding camelCase key is absent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/providers/gemini/types.go` around lines 163 - 185, Update the
alias-selection logic around SystemInstruction, GenerationConfig,
SafetySettings, ToolConfig, CachedContent, and ServiceTier to track whether each
canonical JSON key was present during unmarshalling, rather than inferring
absence from zero values or nil pointers. Apply each snake_case fallback only
when its camelCase key was absent, preserving explicit empty and null canonical
values.

Comment on lines +2556 to +2563
// Envelope first, on its own, so it cannot be shadowed by the embedded decoder.
var envelope struct {
GenerateContentRequest *GeminiGenerationRequest `json:"generateContentRequest,omitempty"`
}
if err := sonic.Unmarshal(data, &envelope); err != nil {
return err
}
g.GenerateContentRequest = envelope.GenerateContentRequest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- relevant declarations and methods ---'
rg -n -C 24 'type Gemini(Generation|CountTokens)Request|func \(.*Gemini(Generation|CountTokens)Request\) UnmarshalJSON|generate_content_request|generateContentRequest' core/providers/gemini/types.go

printf '%s\n' '--- usages and tests ---'
rg -n -C 8 'GeminiCountTokensRequest|CountTokensRequest|generate_content_request|generateContentRequest' core/providers/gemini --glob '*.go'

Repository: maximhq/bifrost

Length of output: 36227


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- count-token conversion ---'
cat -n core/providers/gemini/count_tokens.go | sed -n '1,125p'

printf '%s\n' '--- count-token request parsing and validation call sites ---'
rg -n -C 12 'ToGeminiGenerationRequest|GeminiCountTokensRequest|CountTokens' core/providers/gemini core --glob '*.go' \
  | rg -n -C 6 'ToGeminiGenerationRequest|GeminiCountTokensRequest|countTokens|CountTokens|Validate|validation|contents|GenerateContentRequest'

printf '%s\n' '--- dependency/version context ---'
rg -n 'sonic|google.*genai|protobuf' go.mod go.sum README.md core/providers/gemini --glob '*.go' --glob '*.md' 2>/dev/null | head -120

Repository: maximhq/bifrost

Length of output: 50372


🌐 Web query:

Google Gemini countTokens REST API protobuf JSON generate_content_request generateContentRequest``

💡 Result:

The Gemini API provides a countTokens method to calculate the number of tokens in a prompt [1][2]. This method is available as a REST API endpoint that accepts JSON payloads [1][3]. REST API Endpoint The endpoint format for Google AI Studio (Gemini API) is: POST https://generativelanguage.googleapis.com/v1beta/models/{model}:countTokens [1] For Google Cloud Vertex AI, the endpoint is: POST https://{LOCATION}-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{LOCATION}/publishers/google/models/{MODEL_ID}:countTokens [3][4] Request Body Structure The request body supports two mutually exclusive ways to provide input [1][5][6]: 1. Using contents: A list of content objects (often including role and parts) [1][3]. { "contents": [{ "role": "user", "parts": [{"text": "Hello, how are you?"}] }] } 2. Using generateContentRequest: An object that encapsulates the same structure as a full GenerateContentRequest (including system instructions or tools) [1][5][6]. If this field is provided, the contents field is ignored [1][5]. { "generateContentRequest": { "model": "models/gemini-2.0-flash", "contents": [{"parts": [{"text": "Hello"}]}] } } Protobuf Definition In the underlying Protocol Buffers (protobuf) definition for the Generative Language service, the CountTokensRequest message includes these fields [5][7][6]: - model (string): The resource name of the model [5][7]. - contents (repeated Content): The prompt input [5][7]. - generate_content_request (GenerateContentRequest): A container for comprehensive request data, including steering instructions or function declarations [5][7][6]. Because these fields are defined in the service proto, when using JSON over REST, they map to camelCase keys (e.g., generateContentRequest) [1][5]. Response payloads typically include totalTokens and, optionally, details on token distribution across modalities [1][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- exact count-token conversion ---'
cat -n core/providers/gemini/count_tokens.go | sed -n '56,105p'

printf '%s\n' '--- Gemini ingress references only ---'
rg -n -C 10 'ToGeminiGenerationRequest|GeminiCountTokensRequest' core/providers/gemini --glob '*.go'

printf '%s\n' '--- validation references in Gemini count-token path ---'
rg -n -C 8 'Validate|validation|Contents|GenerateContentRequest|CountTokensRequest' core/providers/gemini/count_tokens.go core/providers/gemini/gemini.go | head -240

printf '%s\n' '--- standalone equivalent JSON-tag probe ---'
cat >/tmp/gemini-envelope-probe.go <<'GO'
package main

import (
	"encoding/json"
	"fmt"
)

type Generation struct {
	Contents []string `json:"contents,omitempty"`
}

type CountTokens struct {
	Generation
	GenerateContentRequest *Generation `json:"generateContentRequest,omitempty"`
}

func main() {
	for _, input := range []string{
		`{"generateContentRequest":{"contents":["camel"]}}`,
		`{"generate_content_request":{"contents":["snake"]}}`,
	} {
		var envelope struct {
			GenerateContentRequest *Generation `json:"generateContentRequest,omitempty"`
		}
		var req CountTokens
		if err := json.Unmarshal([]byte(input), &envelope); err != nil {
			panic(err)
		}
		if err := json.Unmarshal([]byte(input), &req.Generation); err != nil {
			panic(err)
		}
		req.GenerateContentRequest = envelope.GenerateContentRequest
		fmt.Printf("%s => envelope_nil=%t envelope_contents=%v flat_contents=%v\n",
			input,
			req.GenerateContentRequest == nil,
			func() []string {
				if req.GenerateContentRequest == nil {
					return nil
				}
				return req.GenerateContentRequest.Contents
			}(),
			req.Contents,
		)
	}
}
GO
go run /tmp/gemini-envelope-probe.go
rm -f /tmp/gemini-envelope-probe.go

Repository: maximhq/bifrost

Length of output: 29649


Support generate_content_request in GeminiCountTokensRequest.UnmarshalJSON.

The decoder only recognizes generateContentRequest. A protobuf-style generate_content_request payload leaves the envelope nil and converts to a zero-value flat request. Add the alias and a regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/providers/gemini/types.go` around lines 2556 - 2563, Update the envelope
in GeminiCountTokensRequest.UnmarshalJSON to accept both generateContentRequest
and generate_content_request JSON keys, preserving the existing request
assignment behavior. Add a regression test covering a payload using
generate_content_request and verify it populates GenerateContentRequest rather
than producing a zero-value request.

Comment on lines +5 to +16
require github.com/mark3labs/mcp-go v0.43.2

require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Which compatible github.com/mark3labs/mcp-go version or github.com/buger/jsonparser version resolves GHSA-6g7g-w4f8-9c9x?

💡 Result:

The vulnerability GHSA-6g7g-w4f8-9c9x, which affects the github.com/buger/jsonparser library, is resolved in version 1.1.2 [1][2][3]. Versions of github.com/buger/jsonparser prior to 1.1.2 are vulnerable to a denial-of-service attack due to improper offset validation in the Delete function [1][2][4]. For the github.com/mark3labs/mcp-go library, there is no direct dependency version that fixes this by simply updating the dependency. The project has addressed this vulnerability by migrating away from the affected dependency chain. Specifically, github.com/mark3labs/mcp-go replaced its use of github.com/invopop/jsonschema (which transitively depended on the vulnerable github.com/buger/jsonparser) with github.com/google/jsonschema-go in pull request #760 [5][6]. Users of github.com/mark3labs/mcp-go should update to a version that includes this migration to eliminate the vulnerable transitive dependency [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- repository state ---'
git status --short
printf '%s\n' '--- recent commits ---'
git log --oneline -8
printf '%s\n' '--- stack metadata candidates ---'
find . -maxdepth 3 -type f \( -iname '*stack*' -o -iname '*series*' -o -name '.gitstack' \) -print
printf '%s\n' '--- module files ---'
git ls-files '*go.mod' '*go.sum' | sed -n '1,120p'
printf '%s\n' '--- target module ---'
cat -n examples/mcps/remote-test-server/go.mod
if test -f examples/mcps/remote-test-server/go.sum; then
  printf '%s\n' '--- target checksums ---'
  rg -n 'github.com/(mark3labs/mcp-go|buger/jsonparser|invopop/jsonschema|google/jsonschema-go)' examples/mcps/remote-test-server/go.sum
fi
printf '%s\n' '--- dependency references ---'
rg -n 'mark3labs/mcp-go|buger/jsonparser|invopop/jsonschema|google/jsonschema-go' --glob '!vendor/**' .
printf '%s\n' '--- target diff summary ---'
git diff --stat -- examples/mcps/remote-test-server/go.mod examples/mcps/remote-test-server/go.sum

Repository: maximhq/bifrost

Length of output: 49454


🏁 Script executed:

#!/bin/bash
set -eu
cd examples/mcps/remote-test-server
if command -v go >/dev/null 2>&1; then
  go mod tidy -diff
else
  printf '%s\n' 'go command unavailable; cannot run go mod tidy -diff'
fi

Repository: maximhq/bifrost

Length of output: 619


🌐 Web query:

github.com/mark3labs/mcp-go PR 760 release version google/jsonschema-go jsonparser vulnerability

💡 Result:

In the mark3labs/mcp-go project, Pull Request #760 was merged on March 24, 2026, to replace the existing github.com/invopop/jsonschema dependency with github.com/google/jsonschema-go [1]. This change was driven by security and compliance concerns related to the transitive dependencies of the invopop/jsonschema library [2]. Specifically, the library indirectly depended on github.com/mailru/easyjson, which was subject to sanctions and flagged as a supply chain risk [2]. Although your query mentions a jsonparser vulnerability, the PR documentation highlights that the transition was primarily aimed at removing the unmaintained dependency chain through invopop/jsonschema and reducing the project's overall dependency footprint to mitigate these supply chain risks [1][2]. This migration was included in the v0.46.0 release of mcp-go [3][4]. The project has since moved forward with using google/jsonschema-go for JSON schema generation [5][6]. Users should be aware that this change involved updating API calls for schema generation, and documentation examples were subsequently updated to reflect the new syntax [1][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- cached mcp-go module metadata ---'
if command -v go >/dev/null 2>&1; then
  gomodcache="$(go env GOMODCACHE)"
  find "$gomodcache/github.com/mark3labs" -maxdepth 3 -type f \( -path '*/v0.43.2/go.mod' -o -path '*/@v/list' \) -print 2>/dev/null | sort
  if test -f "$gomodcache/github.com/mark3labs/mcp-go@v0.43.2/go.mod"; then
    cat -n "$gomodcache/github.com/mark3labs/mcp-go@v0.43.2/go.mod"
  fi
fi
printf '%s\n' '--- existing patched-module patterns ---'
for f in \
  examples/mcps/mcp-test-client/go.mod \
  examples/mcps/mcp-only/go.mod \
  tests/scripts/1millogs/go.mod \
  plugins/otel/go.mod; do
  if test -f "$f"; then
    printf '%s\n' "--- $f"
    sed -n '/github.com\/buger\/jsonparser/,+1p' "$f"
  fi
done

Repository: maximhq/bifrost

Length of output: 1345


🏁 Script executed:

#!/bin/bash
set -eu
cd examples/mcps/remote-test-server
go mod tidy -diff

Repository: maximhq/bifrost

Length of output: 153


Upgrade github.com/buger/jsonparser to v1.1.2.

github.com/mark3labs/mcp-go v0.43.2 selects vulnerable v1.1.1. Add the patched indirect version and update go.sum. Run go mod tidy from examples/mcps/remote-test-server.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/mcps/remote-test-server/go.mod` around lines 5 - 16, Update the
indirect github.com/buger/jsonparser dependency in go.mod from v1.1.1 to v1.1.2,
then run go mod tidy from the remote-test-server module so go.sum reflects the
patched version.

Sources: Coding guidelines, Linters/SAST tools, Pipeline failures

Comment thread Makefile
Comment on lines +2340 to +2355
JOBS_CAP="$(or $(HARNESS_JOBS),100)"; \
say "$(CYAN)Shard concurrency cap: $$JOBS_CAP (HARNESS_JOBS).$(NC)"; \
: "One newman invocation, called from two places: the main launch loop and the 429 retry"; \
: "pass. Factored out so the retry cannot drift from the main run - the two must send the"; \
: "same env vars and reporters or the retry would exercise a different configuration than"; \
: "the failure it is meant to clear. Args: <shard> <collection> <report-out> <provider>."; \
: "Runs in the foreground and returns the PIPELINE status (pipefail is set above), so the"; \
: "caller can record it; backgrounding stays at the call site so pids remain trackable."; \
: "Running SHARD jobs, excluding the monitor. start_monitor backgrounds the monitor into"; \
: "this same shell, so a plain 'jobs -pr | wc -l' counts it as a shard. At the default cap"; \
: "that is only an off-by-one, but with a low HARNESS_JOBS the count would sit at the cap"; \
: "with only the monitor running and 'wait -n' would block on a process that never exits."; \
shard_jobs() { \
SJ_MON="$$(cat tmp/harness-monitor.pid 2>/dev/null)"; \
jobs -pr | grep -v -x -F "$${SJ_MON:-__none__}" | wc -l | tr -d ' '; \
}; \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Require a positive numeric HARNESS_JOBS value.

If HARNESS_JOBS=0, the condition at Line 2405 is always true. wait -n then fails with no child process, || true hides the failure, and the launcher loops forever. Reject zero and non-numeric values before either wait loop.

Proposed fix
 JOBS_CAP="$(or $(HARNESS_JOBS),100)"; \
+case "$$JOBS_CAP" in
+  ''|*[!0-9]*|0)
+    say "$(RED)HARNESS_JOBS must be a positive integer$(NC)"
+    exit 2
+    ;;
+esac; \
 say "$(CYAN)Shard concurrency cap: $$JOBS_CAP (HARNESS_JOBS).$(NC)"; \

Also applies to: 2405-2407

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 2340 - 2355, Validate HARNESS_JOBS before entering
either wait loop, requiring it to be a positive integer; reject zero, negative,
empty, and non-numeric values with a clear error and nonzero exit. Update the
existing JOBS_CAP setup near shard_jobs and ensure the validation occurs before
the loop containing wait -n, while preserving the current default when
HARNESS_JOBS is unset.

Comment thread tests/e2e/api/runners/harness-monitor.mjs
Comment on lines +36 to +40
export const retryAfterSeconds = (execution) => {
const raw = headerValue(execution, "retry-after");
if (raw === null) return null;
const n = Number(raw.trim());
return Number.isFinite(n) && n >= 0 ? n : null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reject blank and non-integer Retry-After values.

Number(raw.trim()) converts an empty header value to 0. A malformed empty header then causes an immediate retry instead of the exponential fallback. Validate the trimmed value before conversion.

Proposed fix
 const raw = headerValue(execution, "retry-after");
 if (raw === null) return null;
-const n = Number(raw.trim());
-return Number.isFinite(n) && n >= 0 ? n : null;
+const trimmed = raw.trim();
+if (!/^\d+$/.test(trimmed)) return null;
+const n = Number(trimmed);
+return Number.isFinite(n) ? n : null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const retryAfterSeconds = (execution) => {
const raw = headerValue(execution, "retry-after");
if (raw === null) return null;
const n = Number(raw.trim());
return Number.isFinite(n) && n >= 0 ? n : null;
export const retryAfterSeconds = (execution) => {
const raw = headerValue(execution, "retry-after");
if (raw === null) return null;
const trimmed = raw.trim();
if (!/^\d+$/.test(trimmed)) return null;
const n = Number(trimmed);
return Number.isFinite(n) ? n : null;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/api/runners/lib/rate-limit-retry.mjs` around lines 36 - 40, Update
retryAfterSeconds to trim the Retry-After header, reject an empty trimmed value
before calling Number, and accept only finite non-negative integers; return null
for blank, fractional, or other malformed values so callers use the exponential
fallback.

Comment on lines +75 to +80
export const rateLimitedNames = (report) => {
const names = new Set();
for (const e of report?.run?.executions || []) {
if (isRateLimited(e) && e.item?.name) names.add(e.item.name);
}
return names;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use Postman item IDs for retry selection.

Request names are not unique in this collection. A 429 for one item can replay another item with the same name. If that unrelated item fails, the retry keeps the shard failed after the throttled item recovers. It also sends unnecessary provider requests.

  • tests/e2e/api/runners/lib/rate-limit-retry.mjs#L75-L80: return a set of rate-limited item.id values instead of names.
  • tests/e2e/api/runners/filter-collection.mjs#L291-L296: match the rate-limited set against item.id.
  • Makefile#L2468-L2471: keep retry collection generation on the ID-based selector and add a duplicate-name regression case.

The supplied merge policy already documents that request names repeat in this collection.

📍 Affects 3 files
  • tests/e2e/api/runners/lib/rate-limit-retry.mjs#L75-L80 (this comment)
  • tests/e2e/api/runners/filter-collection.mjs#L291-L296
  • Makefile#L2468-L2471
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/api/runners/lib/rate-limit-retry.mjs` around lines 75 - 80, Update
rateLimitedNames in tests/e2e/api/runners/lib/rate-limit-retry.mjs:75-80 to
collect rate-limited item.id values, update
tests/e2e/api/runners/filter-collection.mjs:291-296 to match against item.id,
and keep Makefile:2468-2471 using the ID-based retry selector while adding a
duplicate-name regression case.

Comment on lines 7 to +15
"name": "sse_mcp",
"connection_type": "sse",
"connection_string": "env.MCP_SSE_URL",
"auth_type": "headers",
"headers": {
"Authorization": "env.MCP_SSE_AUTHORIZATION",
"ENV_EXA_API_KEY": "env.MCP_SSE_API_KEY"
},
"tools_to_execute": ["*"],
"tools_to_auto_execute": ["*"]
"connection_string": "http://localhost:3012/sse",
"tools_to_execute": [
"*"
],
"tools_to_auto_execute": [
"*"
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 4 'setup-mcp-tests|remote-test-server|MCP_(HTTP|SSE)_PORT|localhost:3012' \
  .github/workflows/scripts/test-cli-harness.sh \
  .github/workflows/scripts/test-core.sh

Repository: maximhq/bifrost

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(config\.json|test-cli-harness\.sh|test-core\.sh|.*workflow.*|.*remote-test-server.*|.*setup-mcp-tests.*)$' | head -200

printf '%s\n' '--- relevant references ---'
rg -n -C 5 'tests/integrations/python/config\.json|config\.json|remote-test-server|setup-mcp-tests|localhost:3012|MCP_(HTTP|SSE)_PORT' \
  .github tests scripts 2>/dev/null | head -500

Repository: maximhq/bifrost

Length of output: 49765


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- test-cli-harness.sh ---'
cat -n .github/workflows/scripts/test-cli-harness.sh

printf '%s\n' '--- harness-gateway.sh ---'
cat -n .github/workflows/scripts/harness-gateway.sh

printf '%s\n' '--- test-integrations.sh fixture lifecycle ---'
sed -n '80,145p' .github/workflows/scripts/test-integrations.sh

printf '%s\n' '--- direct config consumers ---'
rg -n -C 8 'tests/integrations/python/config\.json|HARNESS_SOURCE_CONFIG|E2E_API_CONFIG' \
  .github tests --glob '!**/config.json' | head -400

printf '%s\n' '--- workflow jobs invoking the consumers ---'
rg -n -C 12 'test-cli-harness\.sh|test-integrations\.sh|test-e2e-api\.sh|run-integration-tests\.sh|remote-test-server|setup-mcp-tests' \
  .github/workflows | head -600

printf '%s\n' '--- repository stack state ---'
git status --short --branch
git branch --show-current
git log --oneline -5

Repository: maximhq/bifrost

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- CLI harness workflow context ---'
rg -n -C 25 'test-cli-harness\.sh|CLI harness|cli-harness' .github/workflows/release-pipeline.yml .github/workflows/*.yml

printf '%s\n' '--- E2E API workflow context ---'
rg -n -C 25 'test-e2e-api\.sh|test-e2e-api|e2e-api' .github/workflows/release-pipeline.yml .github/workflows/*.yml

printf '%s\n' '--- all references to the changed config ---'
rg -l 'tests/integrations/python/config\.json' . --glob '!**/node_modules/**' --glob '!**/dist/**' | sort

printf '%s\n' '--- MCP client startup and retry paths ---'
rg -n -C 6 'connection_type|connection_string|tools_to_auto_execute|SSE|sse_mcp|retry' \
  core transports tests --glob '*.go' | head -500

Repository: maximhq/bifrost

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- workflow steps around test-cli-harness and test-integration ---'
sed -n '820,945p' .github/workflows/release-pipeline.yml
sed -n '110,180p' .github/workflows/run-core-tests.yml

printf '%s\n' '--- test-e2e-api startup and cleanup ---'
sed -n '18,145p' .github/workflows/scripts/test-e2e-api.sh

printf '%s\n' '--- exact config consumers ---'
rg -n 'tests/integrations/python/config\.json|HARNESS_SOURCE_CONFIG|E2E_API_CONFIG' \
  .github tests --glob '!**/config.json' --glob '!**/*.md'

printf '%s\n' '--- MCP implementation references ---'
rg -n -C 8 'sse_mcp|SSEClient|connection_type.*sse|connection_string|tools_to_auto_execute' \
  core transports tests --glob '*.go' --glob '*.json' | head -400

Repository: maximhq/bifrost

Length of output: 45197


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- MCP setup helper ---'
find tests -type f -name 'setup-mcp.sh' -print -exec cat -n {} \;

printf '%s\n' '--- MCP source files ---'
find core transports -type f \( -iname '*mcp*.go' -o -iname '*client*.go' \) -print | head -100

printf '%s\n' '--- startup connection call sites ---'
rg -n -C 10 'Initialize\(|Connect\(|Reconnect|Start\(|Ping\(|SSEServer|New.*SSE|MCPManager' \
  core transports --glob '*.go' | head -700

Repository: maximhq/bifrost

Length of output: 50371


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- shared harness consumers ---'
rg -n -C 8 'harness_seed_app_dir|harness_start_gateway|source .*harness-gateway' \
  .github/workflows/scripts --glob '*.sh'

printf '%s\n' '--- MCP manager bootstrap ---'
rg -n -C 12 'NewMCPManager|InitializeMCP|Initialize.*Client|StartHealth|healthMonitor|StartToolSync|AddClient' \
  core/mcp transports/bifrost-http --glob '*.go' | head -500

printf '%s\n' '--- remote test server build and lifecycle ---'
rg -n -C 8 'setup-mcp-tests|remote-test-server|MCP_HTTP_PORT|MCP_SSE_PORT' \
  Makefile .github/workflows/scripts .github/workflows --glob '!release-pipeline.yml' --glob '*.yml' --glob '*.sh'

Repository: maximhq/bifrost

Length of output: 50371


Start the SSE MCP fixture for every config consumer.

harness-gateway.sh seeds this config for both CLI and provider harnesses, but neither starts remote-test-server on port 3012. test-e2e-api.sh also starts Bifrost without this fixture; its MCP setup uses port 3001. Start the 3012 fixture in the shared lifecycle, or remove sse_mcp from harness configurations that do not test MCP.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/integrations/python/config.json` around lines 7 - 15, The shared test
lifecycle must either start remote-test-server on port 3012 before consumers
load tests/integrations/python/config.json, including harness-gateway.sh and
test-e2e-api.sh, or remove the sse_mcp entry from configurations that do not
exercise MCP; keep the fixture configuration only where its server is guaranteed
to run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant