fix(mcp): add JSON struct tags and call SetDefaults before Validate#8
fix(mcp): add JSON struct tags and call SetDefaults before Validate#8
Conversation
- justification.go: handle sjson.DeleteBytes error, return found bool so callers can distinguish missing vs empty _justification - mw_hitl.go: use found bool to always strip _justification when present - configbased_provider.go: gate auditSecretLookup on non-empty resolved values in all three env-fallback paths - websearch.go: populate GetSecretRequest.Reason for credential lookup - config.go: update comment to mention Disabled flag alongside Enabled - mw_contextmode.go: implement word-boundary split fallback in chunkText, precompute lowercase chunks in scoreChunks for perf, fix QF1012 lints
MCPServerConfig, MCPConfig, and RetryConfig only had yaml/toml struct tags,
causing json.Unmarshal to use Go default field names (e.g. ServerURL instead
of server_url). This broke deserialization when MCP configs are stored as
JSONB in a database and loaded via json.Unmarshal.
Additionally, NewClient now calls SetDefaults() on each server config before
Validate(). This prevents empty sub-configs (e.g. {"retry": {}}) from
failing validation on zero-valued fields like backoff_factor.
There was a problem hiding this comment.
Pull request overview
This PR fixes MCP configuration handling when configs are stored as JSON (e.g., JSONB in Postgres) by ensuring snake_case JSON keys map correctly to Go structs, and by applying defaults before validation so empty sub-configs don’t fail validation.
Changes:
- Add
jsonstruct tags toMCPConfig,MCPServerConfig, andRetryConfigfields. - In
NewClient, callSetDefaults()on each server config before callingValidate().
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| pkg/mcp/config.go | Adds JSON tags to MCP config structs to support JSON unmarshalling alongside YAML/TOML. |
| pkg/mcp/client.go | Applies server defaults before validation during client creation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
- Remove duplicate SetDefaults() call in NewClient server loop - Fix Timeout comment: 10s -> 60s to match actual default - Fix InitialBackoff comment: document that 0 is allowed (unset/default) - Add JSON deserialization tests for snake_case JSONB/PostgreSQL use case - Add SetDefaults-before-Validate integration tests - Add comprehensive tests for shouldIncludeTool, buildStdioEnv, expandEnvValue, convertAndFilterTools, NewClient, Close, GetTools - Test coverage: 32.5% -> 79.5%
… methods - Introduce MCPCaller interface with counterfeiter fake for testability - Add 8 test scenarios for ClientTool.Call: valid args, empty args, invalid JSON, MCP server errors, multi-text, image, mixed content, empty response - Add tests for Client.shouldIncludeTool (include/exclude/both/none) - Add tests for Client.buildStdioEnv (with/without secret provider) - Add tests for Client.expandEnvValue (single/multiple vars, failures) - Add tests for GetTools and NewClient validation - Add Arrange-Act-Assert (AAA) rule to Agents.md testing requirements - All tests follow AAA pattern per new coding standard
… to README - CHANGELOG: add entries for justification error handling, audit gating, chunking word-boundary fix, scoreChunks optimization, MCPCaller interface, MCP unit tests, and AAA testing rule - README: add early-stage API stability notice — library signatures may evolve as Genie matures toward v1.0
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Update README timeout from 10s to 60s across all examples and config table - Remove omitempty from MaxRetries JSON/YAML/TOML tags to preserve explicit zero - Remove duplicate JSON round-trip tests (consolidated into JSON Deserialization section)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Change MaxRetries from int to *int so explicit 0 (disable retries)
is distinguishable from unset (nil, defaults to 2). Restore omitempty.
- Strip client_internal_test.go to only convertAndFilterTools tests;
duplicate shouldIncludeTool/buildStdioEnv/expandEnvValue/GetTools/Close
tests removed (already covered in tool_adapter_test.go via test helpers).
- Remove unreachable $NAME expansion test (buildStdioEnv only triggers
expandEnvValue for ${} patterns).
- Remove flaky NewClient subprocess test (rewrites to direct
SetDefaults+Validate are already in tool_adapter_test.go).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // SessionReconnect enables automatic session reconnection with max retry attempts | ||
| // Set to 0 to disable session reconnection | ||
| SessionReconnect int `yaml:"session_reconnect,omitempty" toml:"session_reconnect,omitempty,omitzero"` | ||
|
|
||
| // Retry configuration for MCP operations | ||
| Retry *RetryConfig `yaml:"retry,omitempty" toml:"retry,omitempty"` | ||
| } | ||
|
|
||
| // RetryConfig represents retry configuration for MCP operations. | ||
| // This matches the retry configuration pattern from trpc-agent-go. | ||
| type RetryConfig struct { | ||
| // MaxRetries is the maximum number of retry attempts (range: 0-10, default: 2) | ||
| MaxRetries int `yaml:"max_retries,omitempty" toml:"max_retries,omitempty,omitzero"` | ||
|
|
||
| // InitialBackoff is the initial delay before first retry (range: 1ms-30s, default: 500ms) | ||
| InitialBackoff time.Duration `yaml:"initial_backoff,omitempty" toml:"initial_backoff,omitempty"` | ||
|
|
||
| // BackoffFactor is the exponential backoff multiplier (range: 1.0-10.0, default: 2.0) | ||
| BackoffFactor float64 `yaml:"backoff_factor,omitempty" toml:"backoff_factor,omitempty,omitzero"` | ||
|
|
||
| // MaxBackoff is the maximum delay cap (range: up to 5 minutes, default: 8s) | ||
| MaxBackoff time.Duration `yaml:"max_backoff,omitempty" toml:"max_backoff,omitempty"` | ||
| SessionReconnect int `json:"session_reconnect,omitempty" yaml:"session_reconnect,omitempty" toml:"session_reconnect,omitempty,omitzero"` | ||
| } |
There was a problem hiding this comment.
MCPServerConfig no longer includes a Retry field / RetryConfig type, but this PR (title/description) and tests (config_test.go) still reference JSON like { "retry": {} }, and NewClient’s comment mentions it. If retry is still intended to be configurable, please reintroduce the Retry field/type with json tags and keep SetDefaults()/Validate() aligned; otherwise, remove/update the remaining retry references and adjust the PR description to reflect the schema change (this would be a breaking config change).
| // Apply defaults before validation so that empty sub-configs | ||
| // (e.g. {"retry": {}}) receive sensible values. |
There was a problem hiding this comment.
The comment here says defaults are applied so empty {"retry": {}} sub-configs validate, but MCPServerConfig.SetDefaults() no longer deals with retry (and Retry isn’t part of the struct). Please update the comment to reflect the actual defaults being applied (e.g., timeout), or reintroduce retry defaults if retry is meant to remain part of MCP config.
| // Apply defaults before validation so that empty sub-configs | |
| // (e.g. {"retry": {}}) receive sensible values. | |
| // Apply server defaults (such as timeouts and other optional fields) | |
| // before validation so that configs with omitted optional sub-fields | |
| // still receive sensible values. |
| It("should correctly map all fields from JSONB/PostgreSQL format", func() { | ||
| jsonData := `{ | ||
| "servers": [ | ||
| { | ||
| "name": "github-mcp", | ||
| "transport": "sse", | ||
| "server_url": "http://localhost:8080/sse", | ||
| "timeout": 30000000000, | ||
| "include_tools": ["github_list_prs", "github_create_issue"], | ||
| "exclude_tools": ["github_delete_repo"], | ||
| "session_reconnect": 3, | ||
| "retry": { | ||
| "max_retries": 5, | ||
| "initial_backoff": 1000000000, | ||
| "backoff_factor": 3.0, | ||
| "max_backoff": 16000000000 | ||
| } |
There was a problem hiding this comment.
This JSON deserialization test includes a retry object (and the test name says it maps “all fields”), but MCPServerConfig in this PR no longer has a Retry field, so json.Unmarshal will silently discard retry and the test won’t catch it. Either (a) assert that retry config is actually parsed (if it’s meant to be supported), or (b) remove retry from the fixture and rename the test to avoid implying coverage it doesn’t provide.
| @@ -124,14 +124,6 @@ config_sections: | |||
| - ["include_tools", "string[]", "Allowlist — only use these tools"] | |||
| - ["exclude_tools", "string[]", "Blocklist — block these tools"] | |||
| - ["session_reconnect", "int", "Auto-reconnect retries (`0` = off)"] | |||
There was a problem hiding this comment.
The MCP “Server Configuration” docs no longer mention the timeout field or its default (and this PR changes the default to 60s in code/README). Please add a timeout row (and, if retry config is still intended to exist, document retry.* here as well) so the docs stay in sync with the actual config schema.
| - ["session_reconnect", "int", "Auto-reconnect retries (`0` = off)"] | |
| - ["session_reconnect", "int", "Auto-reconnect retries (`0` = off)"] | |
| - ["timeout", "int", "Request timeout in seconds (default: `60`)"] | |
| - ["retry.*", "map", "Advanced retry configuration fields under `retry` (e.g. limits, backoff). See config schema for supported keys."] |
Problem
MCPServerConfig,MCPConfig, andRetryConfigonly hadyaml/tomlstruct tags. When MCP configs are stored as JSONB in PostgreSQL and deserialized viajson.Unmarshal, fields likeserver_urldidn't map toServerURL, causing"server_url is required for sse transport"errors.Additionally,
NewClientcalledValidate()beforeSetDefaults(), so empty sub-configs like{"retry": {}}failed validation on zero-valued fields.Changes
config.go: Addedjsonstruct tags to all fields inMCPConfig,MCPServerConfig, andRetryConfigclient.go: CallSetDefaults()on each server config beforeValidate()inNewClient()Testing
Verified end-to-end with Guild: Terraform-deployed
github-scmagent with MCP SSE transport successfully calledgithub_list_prsand returned real PR data from GitHub.