Skip to content

feat: add custom JSON unmarshaling for MCP duration fields and update config schema#2181

Open
Pratham-Mishra04 wants to merge 1 commit intographite-base/2181from
03-20-fix_config_json_schema_fixes
Open

feat: add custom JSON unmarshaling for MCP duration fields and update config schema#2181
Pratham-Mishra04 wants to merge 1 commit intographite-base/2181from
03-20-fix_config_json_schema_fixes

Conversation

@Pratham-Mishra04
Copy link
Copy Markdown
Collaborator

Summary

Enhances MCP (Model Context Protocol) configuration handling by adding custom JSON unmarshaling for duration fields and improves the configuration schema with better validation and documentation.

Changes

  • Added custom JSON unmarshaling for MCPConfig, MCPClientConfig, and MCPToolManagerConfig to handle duration fields properly
  • tool_sync_interval now accepts Go duration strings (e.g. "10m", "1h30m") with fallback to nanosecond integers
  • tool_execution_timeout accepts integer seconds as documented in the schema
  • Enhanced virtual key MCP configuration to support both mcp_client_id and mcp_client_name options
  • Deprecated enforce_governance_header in favor of enforce_auth_on_inference
  • Removed provider-level pricing overrides from multiple provider configurations
  • Consolidated pricing override definitions and added enum validation for request types
  • Improved schema descriptions and validation rules

Type of change

  • Feature
  • Refactor

Affected areas

  • Core (Go)
  • Transports (HTTP)

How to test

Validate MCP configuration parsing with duration strings:

# Test Go duration parsing
go test ./core/schemas -v -run TestMCP

# Test schema validation
go test ./transports -v -run TestConfig

Test configuration files with the new duration format:

{
  "mcp": {
    "tool_sync_interval": "10m",
    "tool_execution_timeout": 30
  }
}

Screenshots/Recordings

N/A

Breaking changes

  • Yes

The virtual_key_mcp_config no longer requires mcp_client_id as a mandatory field since mcp_client_name can be used as an alternative. Provider-level pricing overrides have been removed from provider configurations.

Related issues

N/A

Security considerations

No security implications - this change only affects configuration parsing and validation.

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

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Collaborator Author

Pratham-Mishra04 commented Mar 20, 2026

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 20, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • MCP configuration can identify clients by name or ID (added mcp_client_name; either may be provided).
  • Deprecations

    • client.enforce_governance_header is deprecated; use client.enforce_auth_on_inference instead.
  • Bug Fixes

    • More robust parsing for sync interval and execution timeout fields: accept numeric or string formats and surface clearer validation errors.
  • Documentation

    • Updated schema descriptions and validation guidance; removed strict requirement for mcp_client_id.

Walkthrough

Added custom JSON unmarshalling for MCP config types to accept durations as strings or integers; updated transport schema to deprecate client.enforce_governance_header, add mcp_client_name, and remove the schema-level required: ["mcp_client_id"].

Changes

Cohort / File(s) Summary
MCP JSON unmarshalling
core/schemas/mcp.go
Added parseMCPDurationJSON and UnmarshalJSON implementations for MCPConfig, MCPClientConfig, and MCPToolManagerConfig. tool_sync_interval now accepts either a duration string (parsed via time.ParseDuration) or an integer nanoseconds value; tool_execution_timeout is decoded as integer seconds and converted to time.Duration. Added encoding/json and fmt imports and field-specific error messages.
Transport schema adjustments
transports/config.schema.json
Prepended deprecation note to client.enforce_governance_header description; added mcp_client_name property to $defs.virtual_key_mcp_config; removed schema-level required: ["mcp_client_id"] so either mcp_client_id or mcp_client_name may be provided.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I parse the ticks and strings with glee,

Nanoseconds, seconds — whatever they be,
A header hushed, a name now shown,
I hop through schemas, tidy and known,
Small changes made, and off I flee.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the primary changes: adding custom JSON unmarshaling for MCP duration fields and updating the config schema, matching the main objectives of the PR.
Description check ✅ Passed The description addresses all key template sections including summary, changes, type, affected areas, testing instructions, breaking changes, and security considerations, providing sufficient detail about the modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 03-20-fix_config_json_schema_fixes

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
core/schemas/mcp.go (3)

119-138: Logic is correct; consider adding MarshalJSON for consistency.

The seconds-to-Duration conversion aligns with the schema definition. Same round-trip concern applies—without MarshalJSON, re-serialization will output nanoseconds rather than seconds.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 119 - 138, The custom UnmarshalJSON on
MCPToolManagerConfig parses tool_execution_timeout as integer seconds but there
is no corresponding MarshalJSON, so re-serializing will emit time.Duration
nanoseconds; add a MarshalJSON method on MCPToolManagerConfig that formats
ToolExecutionTimeout as an integer number of seconds (int64) under the
"tool_execution_timeout" JSON key, mirror the Alias pattern used in
UnmarshalJSON, and ensure zero/omitted durations are handled consistently with
the Unmarshal behavior.

77-96: Consider adding MarshalJSON for round-trip consistency.

Without a corresponding MarshalJSON, ToolSyncInterval will serialize as nanoseconds (Go's default for time.Duration). If the original JSON had "10m", re-marshaling produces 600000000000. This asymmetry can break config round-tripping.

♻️ Proposed MarshalJSON for MCPConfig
// MarshalJSON implements custom JSON encoding for MCPConfig, outputting
// tool_sync_interval as a Go duration string.
func (c MCPConfig) MarshalJSON() ([]byte, error) {
	type Alias MCPConfig
	aux := &struct {
		ToolSyncInterval string `json:"tool_sync_interval,omitempty"`
		*Alias
	}{Alias: (*Alias)(&c)}
	if c.ToolSyncInterval != 0 {
		aux.ToolSyncInterval = c.ToolSyncInterval.String()
	}
	return Marshal(aux)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 77 - 96, Add a complementary MarshalJSON
for MCPConfig so ToolSyncInterval round-trips as a human-readable duration
string: implement func (c MCPConfig) MarshalJSON() ([]byte, error) using the
same Alias struct pattern as UnmarshalJSON (referencing MCPConfig, UnmarshalJSON
and ToolSyncInterval), expose ToolSyncInterval as a string
(c.ToolSyncInterval.String()) when non-zero, and return the marshaled bytes via
the same JSON marshal helper used elsewhere (e.g. sonic.Marshal or project
Marshal). Ensure the auxiliary struct tags include
`json:"tool_sync_interval,omitempty"` so zero durations are omitted.

98-117: Same round-trip concern applies here.

Consider adding a corresponding MarshalJSON to MCPClientConfig as noted for MCPConfig above.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 98 - 117, Add a corresponding MarshalJSON
for MCPClientConfig to mirror UnmarshalJSON: implement a MarshalJSON method on
MCPClientConfig that creates an alias struct matching the aux used in
UnmarshalJSON, converts c.ToolSyncInterval back into the same JSON form (a Go
duration string) using the inverse of parseMCPDurationJSON, and then calls
sonic.Marshal on that alias; this ensures ToolSyncInterval round-trips correctly
with the existing UnmarshalJSON logic in MCPClientConfig and keeps
encoding/decoding consistent.
transports/config.schema.json (1)

1552-1581: Schema doesn't enforce the mutual exclusivity constraint.

The description states "Exactly one of mcp_client_id or mcp_client_name must be provided," but the schema allows both, neither, or one. Consider adding a oneOf constraint to enforce this at validation time:

♻️ Proposed schema constraint
     "virtual_key_mcp_config": {
       "type": "object",
       "description": "MCP configuration for a virtual key. Exactly one of mcp_client_id or mcp_client_name must be provided.",
       "properties": {
         ...
       },
-      "additionalProperties": false
+      "additionalProperties": false,
+      "oneOf": [
+        { "required": ["mcp_client_id"], "not": { "required": ["mcp_client_name"] } },
+        { "required": ["mcp_client_name"], "not": { "required": ["mcp_client_id"] } }
+      ]
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transports/config.schema.json` around lines 1552 - 1581, The schema for the
virtual_key_mcp_config object does not enforce the declared "exactly one of
mcp_client_id or mcp_client_name" constraint; update the virtual_key_mcp_config
definition to add a oneOf clause that expresses two alternatives: one where
"mcp_client_id" is required and "mcp_client_name" is prohibited, and the other
where "mcp_client_name" is required and "mcp_client_id" is prohibited (use
required and not/required semantics) so validation fails if both or neither are
present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core/schemas/mcp.go`:
- Around line 63-75: The integer parsing currently treats raw integer JSON as
nanoseconds in parseMCPDurationJSON, causing tool_sync_interval submitted as
e.g. 10 (minutes) to be interpreted as 10ns; fix by changing
MCPClientConfig.UnmarshalJSON (the code path that reads tool_sync_interval) to
interpret integer values as minutes (i.e., convert int -> time.Duration(value) *
time.Minute) instead of using parseMCPDurationJSON's nanoseconds interpretation,
or add a dedicated parse helper (e.g., parseMCPDurationJSONMinutes) and call it
from MCPClientConfig.UnmarshalJSON for tool_sync_interval; mirror the
explicit-unit approach used in MCPToolManagerConfig.UnmarshalJSON for
tool_execution_timeout and consider adding corresponding MarshalJSON to keep API
round-trips consistent.

---

Nitpick comments:
In `@core/schemas/mcp.go`:
- Around line 119-138: The custom UnmarshalJSON on MCPToolManagerConfig parses
tool_execution_timeout as integer seconds but there is no corresponding
MarshalJSON, so re-serializing will emit time.Duration nanoseconds; add a
MarshalJSON method on MCPToolManagerConfig that formats ToolExecutionTimeout as
an integer number of seconds (int64) under the "tool_execution_timeout" JSON
key, mirror the Alias pattern used in UnmarshalJSON, and ensure zero/omitted
durations are handled consistently with the Unmarshal behavior.
- Around line 77-96: Add a complementary MarshalJSON for MCPConfig so
ToolSyncInterval round-trips as a human-readable duration string: implement func
(c MCPConfig) MarshalJSON() ([]byte, error) using the same Alias struct pattern
as UnmarshalJSON (referencing MCPConfig, UnmarshalJSON and ToolSyncInterval),
expose ToolSyncInterval as a string (c.ToolSyncInterval.String()) when non-zero,
and return the marshaled bytes via the same JSON marshal helper used elsewhere
(e.g. sonic.Marshal or project Marshal). Ensure the auxiliary struct tags
include `json:"tool_sync_interval,omitempty"` so zero durations are omitted.
- Around line 98-117: Add a corresponding MarshalJSON for MCPClientConfig to
mirror UnmarshalJSON: implement a MarshalJSON method on MCPClientConfig that
creates an alias struct matching the aux used in UnmarshalJSON, converts
c.ToolSyncInterval back into the same JSON form (a Go duration string) using the
inverse of parseMCPDurationJSON, and then calls sonic.Marshal on that alias;
this ensures ToolSyncInterval round-trips correctly with the existing
UnmarshalJSON logic in MCPClientConfig and keeps encoding/decoding consistent.

In `@transports/config.schema.json`:
- Around line 1552-1581: The schema for the virtual_key_mcp_config object does
not enforce the declared "exactly one of mcp_client_id or mcp_client_name"
constraint; update the virtual_key_mcp_config definition to add a oneOf clause
that expresses two alternatives: one where "mcp_client_id" is required and
"mcp_client_name" is prohibited, and the other where "mcp_client_name" is
required and "mcp_client_id" is prohibited (use required and not/required
semantics) so validation fails if both or neither are present.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d7faf93e-efa4-402c-bf04-abc651f55ece

📥 Commits

Reviewing files that changed from the base of the PR and between 3f5a8bc and 572c5f5.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json

Comment thread core/schemas/mcp.go
Comment on lines +63 to +75
// parseMCPDurationJSON parses a JSON duration value that is either a Go duration
// string (e.g. "10m", "1h30m") or, as a fallback, an integer number of nanoseconds.
func parseMCPDurationJSON(raw json.RawMessage) (time.Duration, error) {
var s string
if sonic.Unmarshal(raw, &s) == nil {
return time.ParseDuration(s)
}
var n int64
if err := sonic.Unmarshal(raw, &n); err != nil {
return 0, fmt.Errorf("expected Go duration string (e.g. \"10m\") or integer nanoseconds, got %s", string(raw))
}
return time.Duration(n), nil
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how tool_sync_interval is used in UI/frontend to confirm expected units
rg -n "tool_sync_interval" --type=ts -C 3

Repository: maximhq/bifrost

Length of output: 11212


🏁 Script executed:

cat -n core/schemas/mcp.go | sed -n '1,15p'

Repository: maximhq/bifrost

Length of output: 433


🏁 Script executed:

cat -n core/schemas/mcp.go | sed -n '60,145p'

Repository: maximhq/bifrost

Length of output: 3543


🏁 Script executed:

rg -n "MarshalJSON" core/schemas/mcp.go

Repository: maximhq/bifrost

Length of output: 41


🏁 Script executed:

# Also check what the struct fields are for MCPConfig and MCPClientConfig
ast-grep --pattern $'type MCPConfig struct {
  $$$
}'

Repository: maximhq/bifrost

Length of output: 1983


🏁 Script executed:

ast-grep --pattern $'type MCPClientConfig struct {
  $$$
}'

Repository: maximhq/bifrost

Length of output: 3779


🏁 Script executed:

# Check the toolSyncIntervalToMinutes function implementation in detail
sed -n '33,41p' ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx

Repository: maximhq/bifrost

Length of output: 409


🏁 Script executed:

# Check how the form data flows when submitting - what units does the UI send?
sed -n '100,110p' ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx

Repository: maximhq/bifrost

Length of output: 426


🏁 Script executed:

# Also check if there are any MarshalJSON methods for duration fields
rg -A 5 "func.*MarshalJSON" core/schemas/mcp.go

Repository: maximhq/bifrost

Length of output: 41


🏁 Script executed:

# Check what the default JSON serialization does for time.Duration
# Also verify if there are any custom marshaling methods for the config structs
rg -B 5 -A 10 "json.Marshal" core/schemas/mcp.go | head -50

Repository: maximhq/bifrost

Length of output: 41


🏁 Script executed:

# Let's understand the actual flow: when API sends tool_sync_interval back to UI,
# what format is it in? Check if there's any conversion in the API responses
rg -n "tool_sync_interval" core/schemas/mcp.go

Repository: maximhq/bifrost

Length of output: 809


🏁 Script executed:

# Check the mcpApi.ts to see how data flows from API to UI
sed -n '60,75p' ui/lib/store/apis/mcpApi.ts

Repository: maximhq/bifrost

Length of output: 1124


🏁 Script executed:

# Let's trace the complete flow - check what happens when API returns MCPClientConfig
# Look for where MCPClientConfig is serialized back to JSON
rg -n "MCPClientConfig" core/schemas/mcp.go | head -20

Repository: maximhq/bifrost

Length of output: 928


🏁 Script executed:

# Check if there's any explicit conversion/transformation when returning API responses
rg -n "ToolSyncInterval\|tool_sync_interval" core/ -t go | grep -v "test" | head -30

Repository: maximhq/bifrost

Length of output: 41


🏁 Script executed:

# Verify the actual default JSON serialization behavior of time.Duration
# by checking if there are any tests or documentation
fd ".*test.*\.go" core/schemas/ | head -5

Repository: maximhq/bifrost

Length of output: 200


🏁 Script executed:

# Let's check what actually gets sent from UI to the API when updating tool_sync_interval
# Look at the API request/response handling
rg -B 5 -A 5 "tool_sync_interval.*nanoseconds" ui/

Repository: maximhq/bifrost

Length of output: 1078


🏁 Script executed:

# Check if there are any API endpoint handlers that show what format is expected
rg -n "tool_sync_interval" core/ -t go | grep -i "handler\|api\|endpoint\|request"

Repository: maximhq/bifrost

Length of output: 41


🏁 Script executed:

# Let's verify the actual round-trip - when the form sends data.tool_sync_interval
# is it sending minutes? Check the form validation or comments
sed -n '320,360p' ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx

Repository: maximhq/bifrost

Length of output: 1877


Tool sync interval unit mismatch causes data loss in API round-trips.

The parseMCPDurationJSON function interprets integer values as nanoseconds, but the UI sends tool_sync_interval as minutes (confirmed by schema at ui/lib/types/schemas.ts:854 and form labels). When the form submits a value like 10 (minutes), Go interprets it as time.Duration(10) nanoseconds—effectively zero.

The UI already has a workaround (toolSyncIntervalToMinutes) that converts API responses from nanoseconds to minutes, but this creates a broken round-trip:

  1. API sends nanoseconds (from default time.Duration JSON serialization)
  2. UI converts to minutes for form
  3. Form submits minutes back
  4. Go interprets minutes as nanoseconds (60 billion times smaller)

Fix: Update MCPClientConfig.UnmarshalJSON to handle integer values as minutes (not nanoseconds) when parsing tool_sync_interval. Alternatively, add corresponding MarshalJSON implementations to serialize durations consistently and document the expected unit (minutes vs. nanoseconds) across the API boundary.

Also note: MCPToolManagerConfig.UnmarshalJSON correctly treats integer values as seconds for tool_execution_timeout—apply similar clarity for tool_sync_interval.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 63 - 75, The integer parsing currently
treats raw integer JSON as nanoseconds in parseMCPDurationJSON, causing
tool_sync_interval submitted as e.g. 10 (minutes) to be interpreted as 10ns; fix
by changing MCPClientConfig.UnmarshalJSON (the code path that reads
tool_sync_interval) to interpret integer values as minutes (i.e., convert int ->
time.Duration(value) * time.Minute) instead of using parseMCPDurationJSON's
nanoseconds interpretation, or add a dedicated parse helper (e.g.,
parseMCPDurationJSONMinutes) and call it from MCPClientConfig.UnmarshalJSON for
tool_sync_interval; mirror the explicit-unit approach used in
MCPToolManagerConfig.UnmarshalJSON for tool_execution_timeout and consider
adding corresponding MarshalJSON to keep API round-trips consistent.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from 572c5f5 to efc055d Compare March 20, 2026 11:03
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch from 3f5a8bc to 311efe5 Compare March 20, 2026 11:03
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
core/schemas/mcp.go (1)

63-117: ⚠️ Potential issue | 🟠 Major

tool_sync_interval numeric unit is still ambiguous and causes broken round-trips.

Line 65-Line 75 parse integer JSON as nanoseconds, while ui/lib/types/mcp.ts documents tool_sync_interval as minutes. A payload like 10 is decoded as 10ns in both Line 89 and Line 110 paths. Also, only custom unmarshalers were added; without matching marshalers, serialized output still uses raw time.Duration encoding and keeps unit asymmetry.

Suggested direction
-func parseMCPDurationJSON(raw json.RawMessage) (time.Duration, error) {
+func parseMCPToolSyncIntervalJSON(raw json.RawMessage) (time.Duration, error) {
 	var s string
 	if sonic.Unmarshal(raw, &s) == nil {
 		return time.ParseDuration(s)
 	}
-	var n int64
-	if err := sonic.Unmarshal(raw, &n); err != nil {
-		return 0, fmt.Errorf("expected Go duration string (e.g. \"10m\") or integer nanoseconds, got %s", string(raw))
+	var minutes int64
+	if err := sonic.Unmarshal(raw, &minutes); err != nil {
+		return 0, fmt.Errorf("expected Go duration string (e.g. \"10m\") or integer minutes, got %s", string(raw))
 	}
-	return time.Duration(n), nil
+	return time.Duration(minutes) * time.Minute, nil
}

Also add matching MarshalJSON for MCPConfig and MCPClientConfig so the API emits a single documented unit/format consistently.

As per coding guidelines, "Custom time.Duration fields in config structs must use custom MarshalJSON/UnmarshalJSON to convert between nanoseconds (Go) and milliseconds (JSON)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 63 - 117, The integer parsing currently
treats numeric values as nanoseconds causing unit mismatch and no MarshalJSON
exists to emit a consistent JSON unit; update parseMCPDurationJSON so numeric
raw JSON is interpreted as milliseconds (return time.Duration(n) *
time.Millisecond) and accept both Go duration strings and integer milliseconds,
and add MarshalJSON methods on MCPConfig and MCPClientConfig that serialize
ToolSyncInterval as an integer number of milliseconds (convert
c.ToolSyncInterval to milliseconds) so decoding and encoding use the same
documented unit; reference parseMCPDurationJSON, MCPConfig.UnmarshalJSON,
MCPClientConfig.UnmarshalJSON and implement MCPConfig.MarshalJSON and
MCPClientConfig.MarshalJSON to enforce the millisecond convention.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@core/schemas/mcp.go`:
- Around line 63-117: The integer parsing currently treats numeric values as
nanoseconds causing unit mismatch and no MarshalJSON exists to emit a consistent
JSON unit; update parseMCPDurationJSON so numeric raw JSON is interpreted as
milliseconds (return time.Duration(n) * time.Millisecond) and accept both Go
duration strings and integer milliseconds, and add MarshalJSON methods on
MCPConfig and MCPClientConfig that serialize ToolSyncInterval as an integer
number of milliseconds (convert c.ToolSyncInterval to milliseconds) so decoding
and encoding use the same documented unit; reference parseMCPDurationJSON,
MCPConfig.UnmarshalJSON, MCPClientConfig.UnmarshalJSON and implement
MCPConfig.MarshalJSON and MCPClientConfig.MarshalJSON to enforce the millisecond
convention.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 921a0cc2-e0a7-4d0d-b112-e49bd8cefa21

📥 Commits

Reviewing files that changed from the base of the PR and between 572c5f5 and efc055d.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • transports/config.schema.json

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from efc055d to 6d5aff7 Compare March 20, 2026 11:50
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch 2 times, most recently from 6554a67 to c12b50f Compare March 20, 2026 14:08
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from 6d5aff7 to 56cb80a Compare March 20, 2026 14:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
transports/config.schema.json (1)

1554-1581: ⚠️ Potential issue | 🟠 Major

Add oneOf constraint to enforce the declared mcp_client_id / mcp_client_name exclusivity.

The schema description at line 1554 states "Exactly one of mcp_client_id or mcp_client_name must be provided," but the schema currently allows both fields to be set or both to be absent. Add a structural constraint to match the documented contract.

Proposed fix
     "virtual_key_mcp_config": {
       "type": "object",
       "description": "MCP configuration for a virtual key. Exactly one of mcp_client_id or mcp_client_name must be provided.",
       "properties": {
         "id": {
           "type": "integer",
           "description": "MCP config ID"
         },
         "virtual_key_id": {
           "type": "string",
           "description": "Associated virtual key ID"
         },
         "mcp_client_id": {
           "type": "integer",
           "description": "Associated MCP client ID (use this or mcp_client_name)"
         },
         "mcp_client_name": {
           "type": "string",
           "description": "MCP client name (config file alternative to mcp_client_id; resolved to mcp_client_id at load time)"
         },
         "tools_to_execute": {
           "type": "array",
           "description": "Tools to execute for this MCP config",
           "items": {
             "type": "string"
           }
         }
       },
+      "oneOf": [
+        { "required": ["mcp_client_id"] },
+        { "required": ["mcp_client_name"] }
+      ],
       "additionalProperties": false
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transports/config.schema.json` around lines 1554 - 1581, The schema block
describing the MCP configuration must enforce that exactly one of mcp_client_id
or mcp_client_name is present; update the object schema (the JSON object that
currently lists properties id, virtual_key_id, mcp_client_id, mcp_client_name,
tools_to_execute and has additionalProperties:false) to include a oneOf
constraint with two subschemas—one requiring ["mcp_client_id"] and the other
requiring ["mcp_client_name"]—so the validator accepts only configs that provide
exactly one of those fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@transports/config.schema.json`:
- Around line 1554-1581: The schema block describing the MCP configuration must
enforce that exactly one of mcp_client_id or mcp_client_name is present; update
the object schema (the JSON object that currently lists properties id,
virtual_key_id, mcp_client_id, mcp_client_name, tools_to_execute and has
additionalProperties:false) to include a oneOf constraint with two
subschemas—one requiring ["mcp_client_id"] and the other requiring
["mcp_client_name"]—so the validator accepts only configs that provide exactly
one of those fields.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 280930e2-933e-4c73-86bc-ff5ca1f2d812

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5aff7 and 56cb80a.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/schemas/mcp.go

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from 56cb80a to 3c27241 Compare March 21, 2026 07:08
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch from c12b50f to 85ba95e Compare March 21, 2026 07:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
transports/config.schema.json (1)

1554-1581: ⚠️ Potential issue | 🟠 Major

Enforce the "exactly one" rule in schema (not only in description).

The description at line 1554 states an exclusive requirement, but the schema allows both or neither of mcp_client_id/mcp_client_name. Add a structural constraint so invalid configs fail schema validation.

Proposed fix
     "virtual_key_mcp_config": {
       "type": "object",
       "description": "MCP configuration for a virtual key. Exactly one of mcp_client_id or mcp_client_name must be provided.",
       "properties": {
         "id": {
           "type": "integer",
           "description": "MCP config ID"
         },
         "virtual_key_id": {
           "type": "string",
           "description": "Associated virtual key ID"
         },
         "mcp_client_id": {
           "type": "integer",
           "description": "Associated MCP client ID (use this or mcp_client_name)"
         },
         "mcp_client_name": {
           "type": "string",
           "description": "MCP client name (config file alternative to mcp_client_id; resolved to mcp_client_id at load time)"
         },
         "tools_to_execute": {
           "type": "array",
           "description": "Tools to execute for this MCP config",
           "items": {
             "type": "string"
           }
         }
       },
+      "oneOf": [
+        {
+          "required": ["mcp_client_id"],
+          "not": { "required": ["mcp_client_name"] }
+        },
+        {
+          "required": ["mcp_client_name"],
+          "not": { "required": ["mcp_client_id"] }
+        }
+      ],
       "additionalProperties": false
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transports/config.schema.json` around lines 1554 - 1581, The schema
description says exactly one of mcp_client_id or mcp_client_name must be present
but the schema doesn't enforce it; add a oneOf constraint to the MCP
configuration object (the object with description "MCP configuration for a
virtual key") that requires either ["mcp_client_id"] or ["mcp_client_name"] so
configs with both or neither fail validation; reference the existing property
names mcp_client_id and mcp_client_name when adding this oneOf.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@transports/config.schema.json`:
- Around line 1554-1581: The schema description says exactly one of
mcp_client_id or mcp_client_name must be present but the schema doesn't enforce
it; add a oneOf constraint to the MCP configuration object (the object with
description "MCP configuration for a virtual key") that requires either
["mcp_client_id"] or ["mcp_client_name"] so configs with both or neither fail
validation; reference the existing property names mcp_client_id and
mcp_client_name when adding this oneOf.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d383e34e-bcff-4e7e-88cd-fee447df0898

📥 Commits

Reviewing files that changed from the base of the PR and between 56cb80a and 3c27241.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/schemas/mcp.go

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch from 85ba95e to 32ed269 Compare March 21, 2026 07:31
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch 2 times, most recently from 85d1302 to f955b3d Compare March 22, 2026 10:15
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch 2 times, most recently from 458d8cf to f76f7be Compare March 22, 2026 13:06
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from f955b3d to f25ea39 Compare March 22, 2026 13:06
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
core/schemas/mcp.go (1)

63-75: ⚠️ Potential issue | 🟠 Major

tool_sync_interval numeric fallback is still unit-ambiguous.

Line 64-75 still interprets raw integers as nanoseconds. Any client posting plain numeric “minutes” values will be misread as near-zero durations, causing broken round-trips.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 63 - 75, parseMCPDurationJSON currently
treats numeric JSON values as nanoseconds which makes plain numeric
minute/second values wrong; change the numeric fallback to parse numbers as
seconds (or a float seconds) instead of nanoseconds: in parseMCPDurationJSON,
unmarshal into a float64 (or int64 if you only want integers) and return
time.Duration(seconds * float64(time.Second)) (i.e. time.Duration(n)*time.Second
for ints), and update the error message to reflect "Go duration string or number
of seconds" and adjust any callers/tests that assumed nanoseconds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core/schemas/mcp.go`:
- Around line 77-138: Add symmetric MarshalJSON methods to avoid asymmetric
encoding: implement MCPConfig.MarshalJSON and MCPClientConfig.MarshalJSON to
serialize c.ToolSyncInterval as an integer JSON value in milliseconds (convert
time.Duration to int64(milliseconds)), and implement
MCPToolManagerConfig.MarshalJSON to serialize ToolExecutionTimeout as an integer
JSON value in seconds (convert time.Duration to int64(seconds)); in each
MarshalJSON, build an alias struct matching the Unmarshal aux (including the raw
duration fields), set the converted numeric fields, and then use sonic.Marshal
to produce the output so encoding mirrors the existing Unmarshal logic.
- Around line 130-136: The current Unmarshal/parse block reads
aux.ToolExecutionTimeout into seconds and converts it to time.Duration allowing
negative values; add a validation after unmarshalling that returns an error if
seconds < 0 (e.g., fmt.Errorf("invalid tool_execution_timeout: must be
non-negative")) so that negative integers are rejected before assigning to
c.ToolExecutionTimeout; update the block that unmarshals
aux.ToolExecutionTimeout, checks the err, then checks seconds < 0, and only then
sets c.ToolExecutionTimeout = time.Duration(seconds) * time.Second.

---

Duplicate comments:
In `@core/schemas/mcp.go`:
- Around line 63-75: parseMCPDurationJSON currently treats numeric JSON values
as nanoseconds which makes plain numeric minute/second values wrong; change the
numeric fallback to parse numbers as seconds (or a float seconds) instead of
nanoseconds: in parseMCPDurationJSON, unmarshal into a float64 (or int64 if you
only want integers) and return time.Duration(seconds * float64(time.Second))
(i.e. time.Duration(n)*time.Second for ints), and update the error message to
reflect "Go duration string or number of seconds" and adjust any callers/tests
that assumed nanoseconds.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 316f77b7-75fe-4e87-8ab9-49f98034fcc0

📥 Commits

Reviewing files that changed from the base of the PR and between f955b3d and f25ea39.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • transports/config.schema.json

Comment thread core/schemas/mcp.go
Comment on lines +77 to +138
// UnmarshalJSON implements custom JSON decoding for MCPConfig, handling
// tool_sync_interval as a Go duration string (e.g. "10m", "1h").
func (c *MCPConfig) UnmarshalJSON(data []byte) error {
type Alias MCPConfig
aux := &struct {
ToolSyncInterval json.RawMessage `json:"tool_sync_interval,omitempty"`
*Alias
}{Alias: (*Alias)(c)}
if err := sonic.Unmarshal(data, aux); err != nil {
return err
}
if len(aux.ToolSyncInterval) > 0 {
d, err := parseMCPDurationJSON(aux.ToolSyncInterval)
if err != nil {
return fmt.Errorf("invalid tool_sync_interval: %w", err)
}
c.ToolSyncInterval = d
}
return nil
}

// UnmarshalJSON implements custom JSON decoding for MCPClientConfig, handling
// tool_sync_interval as a Go duration string (e.g. "10m", "1h").
func (c *MCPClientConfig) UnmarshalJSON(data []byte) error {
type Alias MCPClientConfig
aux := &struct {
ToolSyncInterval json.RawMessage `json:"tool_sync_interval,omitempty"`
*Alias
}{Alias: (*Alias)(c)}
if err := sonic.Unmarshal(data, aux); err != nil {
return err
}
if len(aux.ToolSyncInterval) > 0 {
d, err := parseMCPDurationJSON(aux.ToolSyncInterval)
if err != nil {
return fmt.Errorf("invalid tool_sync_interval: %w", err)
}
c.ToolSyncInterval = d
}
return nil
}

// UnmarshalJSON implements custom JSON decoding for MCPToolManagerConfig, handling
// tool_execution_timeout as an integer number of seconds (as documented in the schema).
func (c *MCPToolManagerConfig) UnmarshalJSON(data []byte) error {
type Alias MCPToolManagerConfig
aux := &struct {
ToolExecutionTimeout json.RawMessage `json:"tool_execution_timeout,omitempty"`
*Alias
}{Alias: (*Alias)(c)}
if err := sonic.Unmarshal(data, aux); err != nil {
return err
}
if len(aux.ToolExecutionTimeout) > 0 {
var seconds int64
if err := sonic.Unmarshal(aux.ToolExecutionTimeout, &seconds); err != nil {
return fmt.Errorf("invalid tool_execution_timeout: expected integer (seconds): %w", err)
}
c.ToolExecutionTimeout = time.Duration(seconds) * time.Second
}
return nil
}
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.

⚠️ Potential issue | 🟠 Major

Add matching MarshalJSON for these duration fields; decode-only support is asymmetric.

These structs now have custom decode paths, but default marshaling will still emit time.Duration as raw nanoseconds. That makes request/response units inconsistent and unstable across edit/save cycles.

As per coding guidelines "Custom time.Duration fields in config structs must use custom MarshalJSON/UnmarshalJSON to convert between nanoseconds (Go) and milliseconds (JSON)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 77 - 138, Add symmetric MarshalJSON methods
to avoid asymmetric encoding: implement MCPConfig.MarshalJSON and
MCPClientConfig.MarshalJSON to serialize c.ToolSyncInterval as an integer JSON
value in milliseconds (convert time.Duration to int64(milliseconds)), and
implement MCPToolManagerConfig.MarshalJSON to serialize ToolExecutionTimeout as
an integer JSON value in seconds (convert time.Duration to int64(seconds)); in
each MarshalJSON, build an alias struct matching the Unmarshal aux (including
the raw duration fields), set the converted numeric fields, and then use
sonic.Marshal to produce the output so encoding mirrors the existing Unmarshal
logic.

Comment thread core/schemas/mcp.go
Comment on lines +130 to +136
if len(aux.ToolExecutionTimeout) > 0 {
var seconds int64
if err := sonic.Unmarshal(aux.ToolExecutionTimeout, &seconds); err != nil {
return fmt.Errorf("invalid tool_execution_timeout: expected integer (seconds): %w", err)
}
c.ToolExecutionTimeout = time.Duration(seconds) * time.Second
}
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.

⚠️ Potential issue | 🟡 Minor

Reject negative tool_execution_timeout values.

Line 131-136 accepts negative integers and converts them into negative durations. That can cause immediate timeout/cancellation behavior and hard-to-debug failures; add a seconds < 0 validation error.

Suggested guard
  if len(aux.ToolExecutionTimeout) > 0 {
    var seconds int64
    if err := sonic.Unmarshal(aux.ToolExecutionTimeout, &seconds); err != nil {
      return fmt.Errorf("invalid tool_execution_timeout: expected integer (seconds): %w", err)
    }
+   if seconds < 0 {
+     return fmt.Errorf("invalid tool_execution_timeout: must be >= 0")
+   }
    c.ToolExecutionTimeout = time.Duration(seconds) * time.Second
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 130 - 136, The current Unmarshal/parse
block reads aux.ToolExecutionTimeout into seconds and converts it to
time.Duration allowing negative values; add a validation after unmarshalling
that returns an error if seconds < 0 (e.g., fmt.Errorf("invalid
tool_execution_timeout: must be non-negative")) so that negative integers are
rejected before assigning to c.ToolExecutionTimeout; update the block that
unmarshals aux.ToolExecutionTimeout, checks the err, then checks seconds < 0,
and only then sets c.ToolExecutionTimeout = time.Duration(seconds) *
time.Second.

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from f25ea39 to 6081007 Compare March 22, 2026 15:10
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch from f76f7be to af59c05 Compare March 22, 2026 15:10
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (4)
core/schemas/mcp.go (4)

130-136: ⚠️ Potential issue | 🟡 Minor

Reject negative tool_execution_timeout values to prevent undefined behavior.

Negative durations can cause immediate context cancellation or unpredictable timeout behavior. Add validation before the conversion.

Suggested fix
  if len(aux.ToolExecutionTimeout) > 0 {
    var seconds int64
    if err := sonic.Unmarshal(aux.ToolExecutionTimeout, &seconds); err != nil {
      return fmt.Errorf("invalid tool_execution_timeout: expected integer (seconds): %w", err)
    }
+   if seconds < 0 {
+     return fmt.Errorf("invalid tool_execution_timeout: must be non-negative")
+   }
    c.ToolExecutionTimeout = time.Duration(seconds) * time.Second
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 130 - 136, When unmarshalling
aux.ToolExecutionTimeout into seconds in the current custom Unmarshal logic,
validate that the parsed int64 is non-negative before converting to
time.Duration to prevent negative durations; after sonic.Unmarshal into the
local variable seconds, check if seconds < 0 and return a descriptive error
(e.g., "tool_execution_timeout must be non-negative") instead of assigning to
c.ToolExecutionTimeout, then only set c.ToolExecutionTimeout =
time.Duration(seconds) * time.Second when the value is valid.

98-117: ⚠️ Potential issue | 🟠 Major

Implementation pattern is correct; same MarshalJSON gap applies.

The Alias pattern and sonic usage are appropriate. However, this struct also lacks symmetric MarshalJSON, causing the same round-trip issues noted above.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 98 - 117, Add a symmetric MarshalJSON
implementation for MCPClientConfig to mirror the custom UnmarshalJSON behavior:
when serializing MCPClientConfig, encode ToolSyncInterval as a string duration
(matching parseMCPDurationJSON's expected format) instead of raw duration bytes
so round-trips succeed; implement func (c *MCPClientConfig) MarshalJSON()
([]byte, error) that uses an Alias struct similar to UnmarshalJSON, converts
c.ToolSyncInterval to the appropriate JSON string (or omits if zero) and then
calls sonic.Marshal on the alias, ensuring names match the existing
ToolSyncInterval and MCPClientConfig symbols.

77-96: ⚠️ Potential issue | 🟠 Major

Missing symmetric MarshalJSON causes asymmetric encode/decode.

Without a corresponding MarshalJSON, time.Duration fields will serialize as raw nanoseconds. This breaks round-trips: API returns nanoseconds → UI converts to minutes → UI submits minutes → Go interprets as nanoseconds (60 billion times smaller).

As per coding guidelines: "Custom time.Duration fields in config structs must use custom MarshalJSON/UnmarshalJSON to convert between nanoseconds (Go) and milliseconds (JSON)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 77 - 96, Add a symmetric MarshalJSON method
for MCPConfig to mirror UnmarshalJSON: implement func (c *MCPConfig)
MarshalJSON() ([]byte, error) that uses an Alias struct (matching the aux in
UnmarshalJSON) and sets the tool_sync_interval JSON field to the duration in
milliseconds (int64) instead of raw nanoseconds, preserving the
"tool_sync_interval" key and omitempty behavior; return sonic.Marshal on the
alias to avoid recursion. This ensures round-trip encoding/decoding between
MCPConfig.MarshalJSON and MCPConfig.UnmarshalJSON.

63-75: ⚠️ Potential issue | 🟠 Major

Integer fallback interprets values as nanoseconds, but UI sends minutes.

The PR summary states integer fallback is nanoseconds, but the UI TypeScript interface documents tool_sync_interval as minutes: // Per-client override in minutes (0 = use global, -1 = disabled). When the UI sends 10 (meaning 10 minutes), this code interprets it as 10 nanoseconds—effectively zero.

Consider treating integer fallback as minutes for tool_sync_interval to match UI expectations, or update the UI to send duration strings exclusively.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 63 - 75, The integer fallback currently
treats numeric values as nanoseconds, causing UI-sent minute values (e.g.
tool_sync_interval = 10) to be misinterpreted; update parseMCPDurationJSON so
that when it unmarshals an integer it returns time.Duration(n) * time.Minute
(and adjust the error text to reflect "integer minutes" instead of "integer
nanoseconds") so the server interprets UI minute values correctly (or
alternatively add a separate helper for tool_sync_interval that multiplies by
time.Minute and keep the generic behavior unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@core/schemas/mcp.go`:
- Around line 130-136: When unmarshalling aux.ToolExecutionTimeout into seconds
in the current custom Unmarshal logic, validate that the parsed int64 is
non-negative before converting to time.Duration to prevent negative durations;
after sonic.Unmarshal into the local variable seconds, check if seconds < 0 and
return a descriptive error (e.g., "tool_execution_timeout must be non-negative")
instead of assigning to c.ToolExecutionTimeout, then only set
c.ToolExecutionTimeout = time.Duration(seconds) * time.Second when the value is
valid.
- Around line 98-117: Add a symmetric MarshalJSON implementation for
MCPClientConfig to mirror the custom UnmarshalJSON behavior: when serializing
MCPClientConfig, encode ToolSyncInterval as a string duration (matching
parseMCPDurationJSON's expected format) instead of raw duration bytes so
round-trips succeed; implement func (c *MCPClientConfig) MarshalJSON() ([]byte,
error) that uses an Alias struct similar to UnmarshalJSON, converts
c.ToolSyncInterval to the appropriate JSON string (or omits if zero) and then
calls sonic.Marshal on the alias, ensuring names match the existing
ToolSyncInterval and MCPClientConfig symbols.
- Around line 77-96: Add a symmetric MarshalJSON method for MCPConfig to mirror
UnmarshalJSON: implement func (c *MCPConfig) MarshalJSON() ([]byte, error) that
uses an Alias struct (matching the aux in UnmarshalJSON) and sets the
tool_sync_interval JSON field to the duration in milliseconds (int64) instead of
raw nanoseconds, preserving the "tool_sync_interval" key and omitempty behavior;
return sonic.Marshal on the alias to avoid recursion. This ensures round-trip
encoding/decoding between MCPConfig.MarshalJSON and MCPConfig.UnmarshalJSON.
- Around line 63-75: The integer fallback currently treats numeric values as
nanoseconds, causing UI-sent minute values (e.g. tool_sync_interval = 10) to be
misinterpreted; update parseMCPDurationJSON so that when it unmarshals an
integer it returns time.Duration(n) * time.Minute (and adjust the error text to
reflect "integer minutes" instead of "integer nanoseconds") so the server
interprets UI minute values correctly (or alternatively add a separate helper
for tool_sync_interval that multiplies by time.Minute and keep the generic
behavior unchanged).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 28ba5d32-be16-446f-b2c7-f6889550d976

📥 Commits

Reviewing files that changed from the base of the PR and between f25ea39 and 6081007.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • transports/config.schema.json

@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_adds_support_for_bedrock_stable-image_models branch from af59c05 to 0d15eae Compare March 23, 2026 05:39
@Pratham-Mishra04 Pratham-Mishra04 force-pushed the 03-20-fix_config_json_schema_fixes branch from 6081007 to 47b1c60 Compare March 23, 2026 05:39
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
core/schemas/mcp.go (3)

130-136: ⚠️ Potential issue | 🟡 Minor

Reject negative tool_execution_timeout values.

Negative integers are currently accepted and converted to negative durations. Please validate seconds >= 0 before assignment.

Proposed guard
  if len(aux.ToolExecutionTimeout) > 0 {
  	var seconds int64
  	if err := sonic.Unmarshal(aux.ToolExecutionTimeout, &seconds); err != nil {
  		return fmt.Errorf("invalid tool_execution_timeout: expected integer (seconds): %w", err)
  	}
+ 	if seconds < 0 {
+ 		return fmt.Errorf("invalid tool_execution_timeout: must be non-negative")
+ 	}
  	c.ToolExecutionTimeout = time.Duration(seconds) * time.Second
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 130 - 136, The JSON unmarshal block for
aux.ToolExecutionTimeout accepts negative integers and assigns them to
c.ToolExecutionTimeout; after decoding into the local seconds variable (via
sonic.Unmarshal on aux.ToolExecutionTimeout) add a guard that returns an error
when seconds < 0 (e.g., fmt.Errorf("invalid tool_execution_timeout: must be
non-negative")) instead of assigning a negative time.Duration to
c.ToolExecutionTimeout, ensuring only seconds >= 0 are converted to
time.Duration.

63-75: ⚠️ Potential issue | 🟠 Major

Integer fallback unit is incompatible with MCP client tool_sync_interval contract.

Numeric tool_sync_interval values are parsed as nanoseconds here, but the client contract uses minutes (ui/lib/types/mcp.ts). A value like 10 is currently interpreted as 10ns, causing broken edit/save round-trips.

Suggested direction
+func parseMCPDurationJSONMinutes(raw json.RawMessage) (time.Duration, error) {
+	var s string
+	if sonic.Unmarshal(raw, &s) == nil {
+		return time.ParseDuration(s)
+	}
+	var n int64
+	if err := sonic.Unmarshal(raw, &n); err != nil {
+		return 0, fmt.Errorf("expected Go duration string (e.g. \"10m\") or integer minutes, got %s", string(raw))
+	}
+	return time.Duration(n) * time.Minute, nil
+}

Then use this parser for MCPClientConfig.tool_sync_interval (and MCPConfig too if its numeric API contract is also minutes).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 63 - 75, parseMCPDurationJSON currently
treats numeric JSON values as nanoseconds which conflicts with the MCP client
contract that uses minutes; update parseMCPDurationJSON so that when the numeric
fallback path (variable n) is used it interprets the value as minutes (i.e.,
return time.Duration(n) * time.Minute) instead of time.Duration(n), and ensure
this parser is used for MCPClientConfig.tool_sync_interval (and MCPConfig if
applicable) so numeric values like 10 are parsed as 10 minutes.

77-138: ⚠️ Potential issue | 🟠 Major

Add matching MarshalJSON for these duration fields.

These types now customize decoding only; encoding still uses default time.Duration JSON (nanoseconds), which makes units asymmetric and unstable across API round-trips.

As per coding guidelines "core/schemas/*.go: Custom duration fields in config structs must implement MarshalJSON/UnmarshalJSON to convert between Go nanoseconds and JSON milliseconds." Based on learnings "In the core/schemas package, any custom MarshalJSON methods must invoke the package-level Marshal() wrapper rather than encoding/json.Marshal directly."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/schemas/mcp.go` around lines 77 - 138, Add matching MarshalJSON
implementations for MCPConfig, MCPClientConfig and MCPToolManagerConfig so
encoding matches the custom decoding: for MCPConfig and MCPClientConfig
serialize ToolSyncInterval as an integer number of milliseconds (convert
time.Duration to int64 milliseconds) and for MCPToolManagerConfig serialize
ToolExecutionTimeout as an integer number of seconds (convert time.Duration to
int64 seconds) to mirror the existing UnmarshalJSON behavior in
MCPConfig.UnmarshalJSON, MCPClientConfig.UnmarshalJSON and
MCPToolManagerConfig.UnmarshalJSON; each MarshalJSON must build an aux struct
mirroring the Unmarshal aux (including ToolSyncInterval or
ToolExecutionTimeout), call the package-level Marshal() wrapper (not
encoding/json.Marshal) to encode the aux, and return its bytes/errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@core/schemas/mcp.go`:
- Around line 130-136: The JSON unmarshal block for aux.ToolExecutionTimeout
accepts negative integers and assigns them to c.ToolExecutionTimeout; after
decoding into the local seconds variable (via sonic.Unmarshal on
aux.ToolExecutionTimeout) add a guard that returns an error when seconds < 0
(e.g., fmt.Errorf("invalid tool_execution_timeout: must be non-negative"))
instead of assigning a negative time.Duration to c.ToolExecutionTimeout,
ensuring only seconds >= 0 are converted to time.Duration.
- Around line 63-75: parseMCPDurationJSON currently treats numeric JSON values
as nanoseconds which conflicts with the MCP client contract that uses minutes;
update parseMCPDurationJSON so that when the numeric fallback path (variable n)
is used it interprets the value as minutes (i.e., return time.Duration(n) *
time.Minute) instead of time.Duration(n), and ensure this parser is used for
MCPClientConfig.tool_sync_interval (and MCPConfig if applicable) so numeric
values like 10 are parsed as 10 minutes.
- Around line 77-138: Add matching MarshalJSON implementations for MCPConfig,
MCPClientConfig and MCPToolManagerConfig so encoding matches the custom
decoding: for MCPConfig and MCPClientConfig serialize ToolSyncInterval as an
integer number of milliseconds (convert time.Duration to int64 milliseconds) and
for MCPToolManagerConfig serialize ToolExecutionTimeout as an integer number of
seconds (convert time.Duration to int64 seconds) to mirror the existing
UnmarshalJSON behavior in MCPConfig.UnmarshalJSON, MCPClientConfig.UnmarshalJSON
and MCPToolManagerConfig.UnmarshalJSON; each MarshalJSON must build an aux
struct mirroring the Unmarshal aux (including ToolSyncInterval or
ToolExecutionTimeout), call the package-level Marshal() wrapper (not
encoding/json.Marshal) to encode the aux, and return its bytes/errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f50f36af-3434-4b9d-ac58-30a3144621c2

📥 Commits

Reviewing files that changed from the base of the PR and between 6081007 and 47b1c60.

📒 Files selected for processing (2)
  • core/schemas/mcp.go
  • transports/config.schema.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • transports/config.schema.json

@Radheshg04 Radheshg04 changed the base branch from 03-20-fix_adds_support_for_bedrock_stable-image_models to graphite-base/2181 March 23, 2026 09:39
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.

2 participants