How to Create Custom Sub‑Agents in Gemini CLI

I have been waiting for Google to add custom sub‑agent support to the Gemini CLI for what feels like forever. Claude Code already has it, and at this point I expected feature parity.
Tracking GitHub Issue #3132 has been a daily ritual, and while someone recently hinted that it is "already implemented," the official docs at geminicli.com/docs remain silent.
Naturally, this led to a deep dive into the source code. And guess what? It is there. It's just hidden.
This post is a quick technical breakdown of how to enable the experimental sub‑agent system, how it works under the hood, and how to define your own custom agents using the undocumented TOML-based configuration.
I based my deep dive on commit 58fd00a (latest as of Dec 22, 2025), and I found the entry point.
Since this is all based on undocumented internal code, it is highly experimental!
What does that actually mean?
Beats me, I don't work on the Gemini CLI team! 🤷♀️🤪
Just don't be surprised if file paths move or your agent suddenly starts behaving weirdly after an update.
🚨 The Architecture: Static TOML Configuration
Forget complex orchestration frameworks. The CLI looks for TOML files to define agents.
I found packages/core/src/agents/toml-loader.ts, which reveals the entire logic.
🧠 Mental Model
By analyzing the source, I found that the current implementation treats agents as static, declarative capabilities.
- Declarative Graph: Agents are defined in static TOML, meaning the "agent graph" is fixed at startup.
- Single-Shot Routing: The router picks an agent based on the
description, passes the context, and awaits the result. - Flattened Inputs: This is the key constraint. The interface between the router and the sub‑agent is flattened into a single string:
${query}.
📉 The Failure Modes (Because Nothing is Perfect)
While this system is awesome, it does have a couple of quirky drawbacks you should know about:
- The "Missed Handoff": Sometimes, the main model just... ignores your agent. You'll ask for a "code review," and instead of calling your specialized
code_revieweragent, the main model will just try to read the file and give you feedback itself. Awkward! 😬 - The "Black Box": When the main model does hand off, the UI simply says "Delegating to Agent..." You don't see the specific commands or tool calls happening inside the sub‑agent. If your agent gets stuck in a loop or tries to do something silly, you can't easily see it to abort.
🪄 Magic Incantations: Using the Built-in Agents
Before building your own agents, it's worth noting that Gemini CLI ships two powerful agents, namely the Codebase Investigator and the Introspection Agent.
But first, you need to enable the experimental agent features in settings.json:
"experimental": {
"enableAgents": true,
"codebaseInvestigatorSettings": { "enabled": true },
"introspectionAgentSettings": { "enabled": true }
}
Once enabled, you can summon them explicitly:
-
Codebase Investigator: 🕵️♂️
Prompt: "Investigate this codebase. I want to <INSERT OBJECTIVE>"
What it does: Throughly digs around your project to solve for the given objective.
-
Introspection Agent: 🪞
Prompt: "How do I <INSERT OBJECTIVE> in Gemini CLI?Delegate to the introspection tool."
What it does: Reads the CLI's internal documentation to help answer the given objective.
🛠️ Hacking in a Custom Agent
Now for the fun part.
Since the CLI scans for TOML files, we can create a toml file to define our own agent inside anagents/ folder inside our .gemini directory.
The "Gotcha": Structured Inputs
As mentioned in the architecture section, the toml-loader.ts is hardcoded to pass only ${query}. If you try to define an [inputs] section, it is currently ignored.
Designing The Agent
I treat the sub‑agent like a black box microservice. I tell it to parse the natural language query itself, and I force it to output structured data (JSON) so the main agent can reliably parse the result and synthetize an appropriate response.
Example Configuration: crypto_bro.toml
Here is an example of a custom sub‑agent that fetches cryptocurrency prices and news using only CLI tools:
name = "crypto_bro"
display_name = "Crypto Data Agent 📊"
description = "Fetches crypto price and news, returning the result as structured JSON."
tools = ["run_shell_command"]
[prompts]
system_prompt = """
You are a specialized Crypto Data Agent.
Your objective is to fetch real-time price and news for a specific cryptocurrency and output the result as strict, valid JSON.
1. **IDENTIFY COIN:** Parse the user query (${query}) for the coin symbol (e.g., 'btc', 'eth', 'sol').
2. **FETCH DATA:**
- **Price:** Use `run_shell_command` to `curl` 'rate.sx' (e.g. `curl -s rate.sx/btc`) to get the current price.
- **News:** Use `run_shell_command` to `curl` 'https://www.coindesk.com/' to get recent headlines.
3. **OUTPUT:**
Return ONLY a valid JSON object with the following structure:
{
"symbol": "String",
"price_usd": "Number",
"headlines": ["String"],
"summary": "String (A manic, high-energy crypto bro market vibe. Must include phrases like 'TO THE MOON', 'HODL', 'DIAMOND HANDS', 'WAGMI', 'LAMBO SOON', etc. based on the trend!)"
}
"""
# This maps the user's message to the ${query} variable
query = "The user wants data for: ${query}"
Once defined, you can invoke it with a prompt like:
"Ask crypto_bro for the price of USDT."
and the sub-agent's output will look something like this:

🔮 The Wish List
While the current implementation is already good enough to implement many sub agent use cases to help improve developer productivity, there's definitely room for improvement. As I look at the potential for agentic workflows in Gemini CLI, here is what I am hoping to see in future releases (Google, are you listening? 👀):
- Native Structured I/O: Dumping everything into
${query}feels like a hack. I'd like the ability to define typed inputs (and outputs too!) in the TOML that map directly to agent's tool signature. - Explicit Command Triggers: Relying on natural language routing is cool, but sometimes I just want to be certain. I'd love a specific slash command to force delegation without the main model having to "decide."
- Orchestrating Multiple Subagents Calls: Right now, it feels like a simple "Main -> Sub‑agent -> Main" handoff. But imagine if the Main agent could chain calls: "Get the price from
crypto_bro, then pass that price to aspreadsheet_agentto update my portfolio." And in between, the main model could even do some reasoning or calculations to synthesize results or decide if other agents are needed.
Wrap Up
The native support for sub agents is here, it's just undocumented (yet!) and experimental.
Go forth and build your agent armies and become a 10x developer 😛. And if anyone asks... you heard it here first.
