Summary
ADK v1.3.0's tool/skilltoolset ships a default SystemInstruction that tells the LLM to call load_skill with a parameter named skill_name, but the tool's actual LoadSkillArgs struct uses name. The LLM follows the documented instruction and is rejected by ADK's own input-schema validator on every session that loads a skill — typically self-correcting only on retry.
A secondary issue: the sibling tool load_skill_resource actually does use skill_name (plus resource_path), so the two tools' parameter naming diverges. Even after fixing the default instruction, this inconsistency is a footgun.
The contradiction
tool/skilltoolset/toolset.go — the default instruction:
defaultSkillSystemInstruction = `... ` +
"1. If a skill seems relevant to the current user query, you MUST use the `load_skill` " +
"tool with `skill_name=\"<SKILL_NAME>\"` to read its full instructions before proceeding.\n" +
...
tool/skilltoolset/internal/skilltool/load_skill.go — the args:
type LoadSkillArgs struct {
Name string `json:"name" jsonschema:"The name of the skill to load."`
}
The JSON schema derived from this struct lists name as the only property — so the LLM-emitted call following the default instruction is rejected with unexpected additional properties [skill_name].
Observed behavior
Trace from a test session, in order:
[tool] load_skill {"skill_name":"generate-playbook"}
→ error: validating root: unexpected additional properties ["skill_name"]
[tool] load_skill {"name":"generate-playbook"} # self-correct
→ success (returns the frontmatter + instructions)
The self-correction tends to work because the response field is named skill_name (LoadSkillResult.SkillName) and the model's pattern flips. But every fresh session pays the cost.
Compounding inconsistency
load_skill_resource.go:
type LoadSkillResourceArgs struct {
SkillName string `json:"skill_name" jsonschema:"The name of the skill."`
ResourcePath string `json:"resource_path" jsonschema:"..."`
}
So:
load_skill → name
load_skill_resource → skill_name
Even when the default instruction is fixed, callers will keep being confused — the same logical thing (the skill name) has different parameter keys across two co-resident tools.
Suggested fix
Option A (minimal, non-breaking): change the default instruction's reference from skill_name="<SKILL_NAME>" to name="<SKILL_NAME>". One line in toolset.go. Eliminates the misdirection without touching schemas.
Option B (more consistent, breaking for direct callers of load_skill_resource): also rename LoadSkillResourceArgs.SkillName → Name (with json tag name) so both tools share the same parameter key. Update the default instruction accordingly.
Happy to send a PR for Option A in the meantime.
Workaround
For now, downstream agents can override the default by passing their own SystemInstruction:
ts, err := skilltoolset.New(ctx, skilltoolset.Config{
Source: source,
SystemInstruction: corrected, // mentions `name=` for load_skill, `skill_name=`+`resource_path=` for load_skill_resource
})
Discovered while building a Bedrock-Claude agent on ADK v1.3.0 — the misdirection cost about 2s per session and confused the model's parameter-name inference in subsequent tool calls on the same conversation.
Summary
ADK v1.3.0's
tool/skilltoolsetships a defaultSystemInstructionthat tells the LLM to callload_skillwith a parameter namedskill_name, but the tool's actualLoadSkillArgsstruct usesname. The LLM follows the documented instruction and is rejected by ADK's own input-schema validator on every session that loads a skill — typically self-correcting only on retry.A secondary issue: the sibling tool
load_skill_resourceactually does useskill_name(plusresource_path), so the two tools' parameter naming diverges. Even after fixing the default instruction, this inconsistency is a footgun.The contradiction
tool/skilltoolset/toolset.go— the default instruction:tool/skilltoolset/internal/skilltool/load_skill.go— the args:The JSON schema derived from this struct lists
nameas the only property — so the LLM-emitted call following the default instruction is rejected withunexpected additional properties [skill_name].Observed behavior
Trace from a test session, in order:
The self-correction tends to work because the response field is named
skill_name(LoadSkillResult.SkillName) and the model's pattern flips. But every fresh session pays the cost.Compounding inconsistency
load_skill_resource.go:So:
load_skill→nameload_skill_resource→skill_nameEven when the default instruction is fixed, callers will keep being confused — the same logical thing (the skill name) has different parameter keys across two co-resident tools.
Suggested fix
Option A (minimal, non-breaking): change the default instruction's reference from
skill_name="<SKILL_NAME>"toname="<SKILL_NAME>". One line intoolset.go. Eliminates the misdirection without touching schemas.Option B (more consistent, breaking for direct callers of
load_skill_resource): also renameLoadSkillResourceArgs.SkillName→Name(with json tagname) so both tools share the same parameter key. Update the default instruction accordingly.Happy to send a PR for Option A in the meantime.
Workaround
For now, downstream agents can override the default by passing their own
SystemInstruction:Discovered while building a Bedrock-Claude agent on ADK v1.3.0 — the misdirection cost about 2s per session and confused the model's parameter-name inference in subsequent tool calls on the same conversation.