GEMINI LABJP
V0.60.0 — The gemini-cli stable release is still v0.60.0. Almost all of it is security work: web fetch destination checks, MCP OAuth issuer validation, sandbox isolation9/30 — gemini-omni-flash-preview shuts down on September 30, nine days from now. The replacement is gemini-omni-1.1-flashCODE13 — Uploading the same video repeatedly returns success and a code 13 failure in turn. With no visible trigger, it is worth deciding your retry policy up frontNEW — Three lines that decide image features in the Gemini app: thirteen, eighteen, and your administrator2.5GA — Gemini 2.5 Pro, Flash and Flash-Lite still have no announced shutdown date. The deprecation table reads No shutdown date announced3.8FLASH — Gemini 3.8 Flash pricing is introductory. It holds until December 31, 2026, and both input and output double on January 1, 2027V0.60.0 — The gemini-cli stable release is still v0.60.0. Almost all of it is security work: web fetch destination checks, MCP OAuth issuer validation, sandbox isolation9/30 — gemini-omni-flash-preview shuts down on September 30, nine days from now. The replacement is gemini-omni-1.1-flashCODE13 — Uploading the same video repeatedly returns success and a code 13 failure in turn. With no visible trigger, it is worth deciding your retry policy up frontNEW — Three lines that decide image features in the Gemini app: thirteen, eighteen, and your administrator2.5GA — Gemini 2.5 Pro, Flash and Flash-Lite still have no announced shutdown date. The deprecation table reads No shutdown date announced3.8FLASH — Gemini 3.8 Flash pricing is introductory. It holds until December 31, 2026, and both input and output double on January 1, 2027
Articles/Dev Tools
Dev Tools/2026-09-21Intermediate

The Day I Unplugged an MCP Server for the First Time — How I Decide Which Tools Stay On

Keep adding MCP servers and quality degrades well before you hit the API ceiling of 512 function declarations. Here is how I use includeTools and excludeTools in Gemini CLI to decide which tools stay enabled, and how I put the rest away.

Gemini CLI9MCP5tool designindie development26settings.json

I had never removed an MCP server until that night.

I was working through some routine maintenance on my Lab sites with Gemini CLI. I asked it to tidy up a directory, and the function call that came back carried arguments I didn't recognize — parameters belonging to a tool on an entirely different server. The API returned a 400, and the only thing left on screen was a complaint about a property that had nothing to do with what I'd asked for.

I opened my settings file and stopped for a moment. Every time I'd found a promising server, I had added it to mcpServers. There was no record of me ever taking one out. I had a habit for adding and no criterion for subtracting.

Here is the part I want to lead with: the number that matters is not the one the platform enforces. Quality slips long before you reach it.

512 Is Not Where Things Start Breaking

The Gemini API caps how many function declarations a single request may carry. Cross it and the request is rejected before the model ever runs.

[API Error: [{
  "error": {
    "code": 400,
    "message": "The GenerateContentRequest proto is invalid:\n  * tools[0].function_declarations: [FIELD_INVALID] At most 512 function declarations can be specified.",
    "status": "INVALID_ARGUMENT"
  }
}]]

This is reported as gemini-cli issue #19083, hit by someone running more than twenty MCP servers at once. The failure is immediate rather than partial, which makes it one of the friendlier ways to break.

The trouble starts earlier. In a case involving a single server exposing 188 tools, the developer called a login tool explicitly and still got a 400 — parameters from unrelated tools had leaked into the request. That is nowhere near 512. I suspect it's the same thing I ran into.

To the model, every tool description is a similar-shaped candidate. The more candidates there are, the blurrier the choice of which one to call and what to pass it becomes. The platform sets the hard ceiling; the workable one is mine to set. The official MCP server configuration docs explain connection and filtering carefully, but they never suggest how many tools is too many. That blank is yours to fill in, as I read it.

Count What You Have Connected

Before removing anything, I needed to know the current number. /mcp lists tool names per server, but it won't give you a total. So I wrote a small script that walks the stdio servers in my settings file and counts.

#!/usr/bin/env python3
"""Connect to each stdio MCP server in ~/.gemini/settings.json and count its tools.
 
Usage:  python3 count_mcp_tools.py
Output: server name / tool count / tools whose qualified name is too long / total
"""
import json
import os
import subprocess
from pathlib import Path
 
SETTINGS_PATH = Path.home() / ".gemini" / "settings.json"
PROTOCOL_VERSION = "2025-06-18"  # the server answers with the version it speaks
 
 
def rpc(proc, payload):
    """Write one JSON-RPC line, then read until the matching id comes back."""
    proc.stdin.write(json.dumps(payload) + "\n")
    proc.stdin.flush()
    if "id" not in payload:
        return None
    while True:
        line = proc.stdout.readline()
        if not line:
            raise RuntimeError("the server exited before answering")
        try:
            msg = json.loads(line)
        except json.JSONDecodeError:
            continue  # some servers print logs to stdout, so skip unparsable lines
        if msg.get("id") == payload["id"]:
            return msg
 
 
def list_tools(conf):
    env = dict(os.environ)
    env.update({k: os.path.expandvars(v) for k, v in conf.get("env", {}).items()})
    proc = subprocess.Popen(
        [conf["command"]] + conf.get("args", []),
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
        cwd=conf.get("cwd"), env=env, text=True, bufsize=1,
    )
    try:
        rpc(proc, {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {
            "protocolVersion": PROTOCOL_VERSION,
            "capabilities": {},
            "clientInfo": {"name": "tool-counter", "version": "0.1.0"},
        }})
        rpc(proc, {"jsonrpc": "2.0", "method": "notifications/initialized"})
        res = rpc(proc, {"jsonrpc": "2.0", "id": 2, "method": "tools/list"})
        return [t["name"] for t in res.get("result", {}).get("tools", [])]
    finally:
        proc.terminate()
        try:
            proc.wait(timeout=5)
        except subprocess.TimeoutExpired:
            proc.kill()
 
 
def main():
    servers = json.loads(SETTINGS_PATH.read_text()).get("mcpServers", {})
    total = 0
    for name, conf in sorted(servers.items()):
        if "command" not in conf:
            print(f"{name:22} skipped, connects over a URL")
            continue
        try:
            tools = list_tools(conf)
        except Exception as err:
            print(f"{name:22} could not read: {err}")
            continue
        total += len(tools)
        # the name that actually ships is mcp_{server}_{tool}, truncated past 63 chars
        long_names = [t for t in tools if len(f"mcp_{name}_{t}") > 63]
        note = f"  over 63 chars: {len(long_names)}" if long_names else ""
        print(f"{name:22} {len(tools):4} tools{note}")
    print(f"{'TOTAL':22} {total:4} tools  (API ceiling 512)")
 
 
if __name__ == "__main__":
    main()

Send initialize, follow it with the notifications/initialized notification, and only then call tools/list — the stdio handshake expects that order. A couple of my servers write startup logs to stdout, so anything that won't parse as JSON gets skipped quietly. Being strict there turns a counting script into one that dies on an exception.

The 63-character check is in there because tool names don't ship as written. Gemini CLI assigns a fully qualified name of the form mcp_{serverName}_{toolName} to avoid collisions, and anything longer than 63 characters gets its middle replaced. When similar tools sit side by side, truncation makes them harder still to tell apart.

My own total came nowhere close to 512. It was, however, considerably larger than the number I would have guessed.

includeTools Is Where You Write Down What Stays

Gemini CLI gives you two per-server filters. includeTools is an allowlist: only the tools you name stay available. excludeTools is a blocklist: the tools you name disappear. Write neither, and everything the server exposes is enabled.

{
  "mcpServers": {
    "site-ops": {
      "command": "npx",
      "args": ["-y", "@example/site-ops-mcp"],
      "includeTools": ["read_file", "write_file", "list_dir"],
      "timeout": 30000
    }
  }
}

When both are present, excludeTools wins — a tool named in both lists is removed. The same logic holds when you override a server that arrived with an extension: blocklists are unioned, allowlists are intersected. An extension can't quietly restore a tool you left out of your own allowlist. Your local settings hold the veto.

To park a whole server without deleting its configuration, use the top-level mcp block.

{
  "mcp": {
    "excluded": ["experimental-server"]
  }
}

One naming trap is worth knowing. Don't put underscores in server names. The parser splits a qualified name on the first underscore after the mcp_ prefix, so a name like my_server makes it misread where the server ends and the tool begins. It doesn't raise an error; your policy rules just stop matching. Write my-server instead.

Sort Them Into Always On, On Demand, and Off

I sorted the tools I'd counted into three piles. The question I asked wasn't whether a tool was useful — it was whether this week would have gone badly without it.

PileTestHow it's configured
Always onCalled almost daily; work stalls without itNamed in includeTools
On demandA few times a month; I can name the occasionDisabled by default, re-enabled per session
OffI can't recall why I added it, and I could do it by handMoved to mcp.excluded

That third test sped things up more than I expected. "I can't recall why I added it" described a surprising share of my configuration. Those servers were interesting on the day I tried them, and I never called them again.

The sorting works inside a single server too. Keeping the read-oriented tools in includeTools and dropping the write-oriented ones shrinks the candidate list and shrinks the blast radius if something is called by mistake. As an indie developer, the broader a tool's permissions, the more I want it folded away by default.

Filter too hard, though, and you'll lose time wondering where a tool went. So I keep a list of what I removed next to the settings file. Putting something away is not the same as throwing it out, and the list is how I keep that distinction honest.

Leave Yourself a Way Back

A sorting scheme you can't reverse in the moment turns into "just enable everything again" within a week. Gemini CLI handles servers from the command line.

# see what is currently connected
gemini mcp list
 
# keep it off by default
gemini mcp disable experimental-server
 
# bring it back for this session only
gemini mcp enable experimental-server --session

Mid-conversation, /mcp shows status and /mcp enable <name> restores a server on the spot. A disabled server still appears in the listing; it simply doesn't connect. I'd rather have that than delete the entry, because the listing keeps me from forgetting the thing exists.

If a server won't connect at all, the count isn't your problem — look at authentication and paths first. I wrote that up in Gemini CLI Won't Start or Authenticate? Where to Look First. If you're still setting up connections in the first place, Gemini CLI with MCP Servers is the better starting point.

When you write servers yourself, the number of tools you expose becomes a design decision. Splitting unrelated capabilities across small servers and connecting only the side you need has cut down my mis-calls more than any prompt change did. Building Custom MCP Servers for Gemini API covers how to build them.

If You Do One Thing

Run the counting script tonight and write down the total. Not to compare it against 512, but to compare it against the number you thought you had. For me, the size of that gap is what made removing things feel worth the afternoon.

Thank you for reading. Adding tools is the fun part, but subtracting them has been just as useful to me.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-05-04
Gemini CLI with MCP Servers: A from File I/O to Database Queries
Learn how to connect MCP servers to Gemini CLI for hands-on file operations and database integration. Covers GEMINI.md configuration, filesystem, SQLite, and GitHub MCP with working examples.
Dev Tools2026-03-11
Wiring Gemini CLI into Your Shell and CI — Headless Runs and Session Resume
Move past interactive use of Gemini CLI: headless runs, JSON output, session resume, approval modes, and MCP. Includes the five ways headless runs break and a measured pro/flash comparison.
Dev Tools2026-08-30
Why Shipped Clients Deserve a Refusal, Not a Silent Model Substitution
A model can retire, but the apps already on people's phones cannot. This is how I built a sunset ledger keyed on output contracts, and how I now back-date my own deadline from the version residue curve.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links