fix: tolerate empty toolNames on RemoteMCPServer refs#1798
Draft
reilly3000 wants to merge 1 commit intokagent-dev:mainfrom
Draft
fix: tolerate empty toolNames on RemoteMCPServer refs#1798reilly3000 wants to merge 1 commit intokagent-dev:mainfrom
reilly3000 wants to merge 1 commit intokagent-dev:mainfrom
Conversation
Author
|
Hi folks, I hate to be the ai slop PR guy, but ran into this issue and thought others may make the same assumption as well. I have a couple tweaks inbound before I raise it for review. |
…dev#1797) When an Agent's `tools[*].mcpServer` references a RemoteMCPServer without an explicit `toolNames` filter, the controller serializes `http_tools[*].tools` (and `sse_tools[*].tools`) as JSON `null`, because Go's `encoding/json` marshals a nil `[]string` slice without `omitempty` as `null`. The Python ADK runtime then crashes on startup with a pydantic ValidationError, leaving the agent pod in CrashLoopBackOff: http_tools.0.tools Input should be a valid list [type=list_type, input_value=None, input_type=NoneType] Whether `toolNames` is intended to be required or optional is a call for maintainers (see kagent-dev#1797 for discussion); either way, the controller should not produce a config the runtime cannot load. This change is defensive on both sides: * Controller (Go): when `toolNames` is empty, expand to the RemoteMCPServer's `.status.discoveredTools` list so the rendered config is explicit and observable. Explicit `toolNames` continue to take precedence. Falls back to an empty array (rather than `null`) if the status has no discovered tools yet. * Runtime (Python): coerce `tools: None` to `[]` via a pydantic BeforeValidator on `HttpMcpServerConfig` / `SseMcpServerConfig`, so older controller versions don't crash newer runtimes. Adds a translator regression test asserting the rendered AgentConfig contains a non-null `tools` array, and a pydantic-level test for the runtime coercion. Happy to rework toward a different direction (e.g. CRD validation making `toolNames` required) if that better matches the intended semantics. Signed-off-by: Chris <chris@chris-reilly.com> Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4448624 to
0aea970
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #1797. An Agent that references a
RemoteMCPServerwithout anexplicit
toolNamesfilter currently CrashLoops with a pydanticValidationErrorbecause the controller emitshttp_tools[*].tools = nulland the runtime rejects
None. WhethertoolNamesis meant to be requiredor optional is a call for maintainers — either way, the controller
shouldn't produce a config the runtime can't load.
This PR is intentionally defensive on both sides; happy to rework toward
a different direction (e.g. CRD validation making
toolNamesrequired, orruntime-only fix) if maintainers prefer.
toolNamesis empty, expand toRemoteMCPServer.status.discoveredToolsso the rendered config isexplicit and observable in the agent's
config.jsonSecret. ExplicittoolNamescontinue to take precedence. Falls back to an empty arrayrather than
nullif status has no discovered tools yet.tools: None→[]via a pydanticBeforeValidatoronHttpMcpServerConfig/SseMcpServerConfig, soolder controllers don't crash newer runtimes.
See #1797 for the full repro and root-cause analysis.
Test plan
(
Test_AdkApiTranslator_RemoteMCPServer_NoToolNames) asserts therendered
AgentConfig.HttpTools[0].Toolsis non-nil, expanded fromdiscovered tools, and serializes as a JSON array (not
null).(
test_http_tool_null_tools.py) coverstools: None, missingtools, and explicittools: [...]for bothHttpandSsevariants.
go test ./core/internal/controller/translator/agent/...) — pass.uv run pytest packages/kagent-adk/tests/unittests/) — pass (9 unrelated TLS e2e failures from missing local fixtures).kagent-adk static --filepath ...startup path (the same command the runtime container runs) against aconfig.jsoncontaininghttp_tools[*].tools = null.- Pre-fix: reproduces the exact
ValidationError ... type=list_type, input_value=Noneand the process exits.- Post-fix:
Application startup complete. / Uvicorn running on http://127.0.0.1:....RemoteMCPServer: built an overlay image of the v0.9.1 runtime with only the patchedtypes.py, swapped it in viacontroller.agentImage, and deployed against a liveRemoteMCPServerthat fronts ~50 federated MCP tools.- Pre-fix: a fresh Agent with no
toolNamesCrashLoops with the exactValidationError ... input_value=Nonefrom the issue.- Post-fix: same Agent reaches
Application startup complete/Uvicorn running on http://0.0.0.0:8080and serves200on/.well-known/agent-card.json.controller.image, and applied a fresh Agent referencing the sameRemoteMCPServer(23discoveredTools) with notoolNamesspecified.- Resulting agent pod reaches
Application startup complete/Uvicorn runningcleanly.Open questions for reviewers
toolNamesintended to be required, or optional with omissionmeaning "all discovered tools"? If the former, this PR can be reduced
to CRD validation + a clear admission error.
discoveredToolsdesirable forobservability, or would you prefer the controller emit
[](oromit the field entirely) and have the runtime handle the "all tools"
semantics on its own?
Notes
nulldefect existed forsse_tools[*].tools;fixed via the same
resolveToolNameshelper.