Summary
The Gemini planner builds SkillsTool and BashTool and stores them on the planner struct, but the call to agentsToTools in process() never passes them as nativeTools. As a result, the function
declarations for activate_skill / run_skill_script / bash never appear in the Gemini request. Skills are silently invisible to the model even when skills_dir is set correctly.
Repro
ax.yaml:
server:
address: ":8494"
eventlog:
sqlite:
filename: "eventlog/log.sqlite"
planner:
gemini:
model: "gemini-2.5-pro"
timeout: "60s"
skills_dir: "./skills"
Skill at ./skills/lowercase/SKILL.md + ./skills/lowercase/scripts/lowercase.sh (copied verbatim from examples/skills/lowercase).
ax serve
ax exec --server localhost:8494 --input "lowercase: HELLO WORLD"
Expected
Gemini receives activate_skill and run_skill_script as available tools; planner emits a function call; skill script runs; lowercased text returned.
Actual
Two failure modes depending on model:
-
gemini-2.5-pro → 400 INVALID_ARGUMENT: Function calling config is set without function_declarations.
Because ToolConfig is always set (with FunctionCallingConfigModeAuto), but Tools ends up empty when no remote agents are registered.
-
gemini-3.5-flash → MALFORMED_FUNCTION_CALL (no content in candidates).
The model sees skills described in the appended system prompt (from SkillsTool.SystemPrompt()) and tries to call a function that wasn't declared, so it produces a malformed tool call.
In neither case does the skill ever execute.
Root cause
internal/gemini/gemini_planner.go:198:
tools, err := agentsToTools(p.registry)
agentsToTools signature (line 516):
func agentsToTools(registry AgentRegistry, nativeTools ...Tool) ([]*genai.Tool, error)
The variadic nativeTools is the intended channel for native tools (skills, bash). But the call site passes only the registry. p.skillsTool (constructed at line 127 and shown to have a working FuncDecl() at
gemini.go:347) is never wired into the request.
The only other call site (gemini_planner_test.go) has the same shape, so it's not covered by tests either.
Suggested fix
tools, err := agentsToTools(p.registry, p.skillsTool, p.bashTool)
Add a planner test that asserts at least the skills tool function declarations appear in the generated GenerateContentConfig.Tools when skills_dir resolves to a non-empty directory.
Environment
- AX:
go install github.com/google/ax/cmd/ax@latest (built from main)
- Go: 1.26.2 darwin/arm64
- macOS 15 (Darwin 25.5.0)
Summary
The Gemini planner builds
SkillsToolandBashTooland stores them on the planner struct, but the call toagentsToToolsinprocess()never passes them asnativeTools. As a result, the functiondeclarations for
activate_skill/run_skill_script/bashnever appear in the Gemini request. Skills are silently invisible to the model even whenskills_diris set correctly.Repro
ax.yaml:Skill at
./skills/lowercase/SKILL.md+./skills/lowercase/scripts/lowercase.sh(copied verbatim fromexamples/skills/lowercase).Expected
Gemini receives
activate_skillandrun_skill_scriptas available tools; planner emits a function call; skill script runs; lowercased text returned.Actual
Two failure modes depending on model:
gemini-2.5-pro→400 INVALID_ARGUMENT: Function calling config is set without function_declarations.Because
ToolConfigis always set (withFunctionCallingConfigModeAuto), butToolsends up empty when no remote agents are registered.gemini-3.5-flash→MALFORMED_FUNCTION_CALL(no content in candidates).The model sees skills described in the appended system prompt (from
SkillsTool.SystemPrompt()) and tries to call a function that wasn't declared, so it produces a malformed tool call.In neither case does the skill ever execute.
Root cause
internal/gemini/gemini_planner.go:198:agentsToToolssignature (line 516):The variadic
nativeToolsis the intended channel for native tools (skills, bash). But the call site passes only the registry.p.skillsTool(constructed at line 127 and shown to have a workingFuncDecl()atgemini.go:347) is never wired into the request.The only other call site (
gemini_planner_test.go) has the same shape, so it's not covered by tests either.Suggested fix
Add a planner test that asserts at least the skills tool function declarations appear in the generated
GenerateContentConfig.Toolswhenskills_dirresolves to a non-empty directory.Environment
go install github.com/google/ax/cmd/ax@latest(built frommain)