Gemini CLI MCP Server Setup 2026: settings.json Configuration Guide
Configure MCP servers in Google's Gemini CLI: the settings.json mcpServers shape for stdio, SSE, and HTTP transports, OAuth discovery, the /mcp command, and troubleshooting servers that connect but discover zero tools.
Key Takeaways
mcpServers in settings.json — user-scoped at ~/.gemini/settings.json, project-scoped at .gemini/settings.json in the repo root.command for local stdio, url for SSE, or httpUrl for Streamable HTTP — mixing more than one on the same entry is not the intended shape.trust (boolean, default false) skips the per-tool confirmation prompt, and timeout (milliseconds, default 600,000) controls how long the CLI waits before giving up on a slow server./mcp command lists every configured server with its live connection status (CONNECTED, CONNECTING, DISCONNECTED) and the tools each one exposes — this is the fastest way to check a config without leaving the CLI.http://localhost:7777/oauth/callback, which means OAuth-based servers will not work in headless or remote environments without browser access — use env-based API keys instead in those cases.Where Gemini CLI's config lives
Two settings.json files, merged, with project settings taking precedence over user settings for the same key:
~/.gemini/settings.json.gemini/settings.jsonOAuth tokens for authenticated servers are stored separately at ~/.gemini/mcp-oauth-tokens.json — deleting that file forces re-authentication on next connect, which is useful if a token gets stuck in a bad state.
The mcpServers JSON shape
Local stdio server:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"],
"env": {},
"cwd": "/path/to/project",
"timeout": 600000,
"trust": false
}
}
}
SSE server:
{
"mcpServers": {
"sse-server": {
"url": "http://localhost:8080/sse",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}
Streamable HTTP server:
{
"mcpServers": {
"hosted-server": {
"httpUrl": "https://mcp.example.com/mcp"
}
}
}
Only one of command, url, or httpUrl should be set per server entry — the CLI picks the transport based on which field is present.
Step 1: Create or open your settings.json
mkdir -p ~/.gemini
Open ~/.gemini/settings.json for a config that applies everywhere, or create .gemini/settings.json inside a specific repo for a project-only config. Project settings win on key conflicts, so a project file is the right place for a server that only makes sense for that codebase (a Postgres connection string pointed at that project's dev database, for example).
Step 2: Add a stdio server
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/yourname/Projects"]
}
}
}
Restart Gemini CLI, or start a new session — config changes are read at startup.
Step 3: Add a server with environment variable substitution
env values support $VAR_NAME or ${VAR_NAME} syntax, so you can reference a variable already set in your shell instead of hardcoding a secret into the file:
{
"mcpServers": {
"github": {
"command": "/path/to/github-mcp-server",
"args": ["stdio"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "$GITHUB_MCP_PAT"
}
}
}
}
Set GITHUB_MCP_PAT in your shell profile (.zshrc, .bashrc) rather than pasting the raw token into settings.json — this keeps the token out of a file you might commit by accident.
Step 4: Scope tools with includeTools / excludeTools
If a server exposes more tools than you want the model choosing from, narrow it per-entry:
{
"mcpServers": {
"github": {
"command": "/path/to/github-mcp-server",
"args": ["stdio"],
"includeTools": ["get_issue", "list_issues", "create_issue"]
}
}
}
excludeTools takes precedence over includeTools if a tool name appears in both — useful for blocking a specific destructive tool (a delete_repo-style action, say) while otherwise allowing everything.
Step 5: Verify with /mcp
Inside a Gemini CLI session, run:
/mcp
This prints every configured server, its connection status, and the tools it's exposing. CONNECTED with a nonzero tool count means it's working. DISCONNECTED means the process failed to start or the URL is unreachable — check the error text printed alongside the status before digging further.
OAuth-protected servers
For servers that require OAuth rather than a static API key, Gemini CLI auto-discovers the OAuth requirement on first connection and opens a browser to complete the flow. Three provider types exist:
dynamic_discovery (default) — the CLI figures out the OAuth configuration from the server itselfgoogle_credentials — uses your Google Application Default Credentials instead of a separate flowservice_account_impersonation — impersonates a service account, relevant for IAP-protected internal servicesTokens are cached in ~/.gemini/mcp-oauth-tokens.json and refreshed automatically once expired. Manage a specific server's auth manually with:
/mcp auth server-name
This will not work over SSH or in a container without forwarded browser access, since the callback listens on http://localhost:7777/oauth/callback on the machine running the CLI. For headless setups, prefer a server that accepts an API key via env over one that requires OAuth.
Troubleshooting Gemini CLI MCP connections
Server shows CONNECTED but /mcp lists zero tools
This usually means the server started and the handshake succeeded, but it isn't registering tools correctly, or the CLI's tool-listing request is failing silently. Test the server standalone outside Gemini CLI first — run the exact command/args by hand and confirm it responds to a raw tools/list request. If it works standalone, check for a malformed JSON Schema in one of the tool definitions; a schema Gemini CLI can't parse can cause the whole list to come back empty rather than partially populated.
Server never leaves CONNECTING
Increase timeout (default 600,000ms / 10 minutes, which is already generous, but a server doing heavy first-run setup — like installing a Python virtualenv, or a slow database schema introspection — can still exceed it). If it's still stuck well past a reasonable timeout, the process likely isn't responding to the initial handshake at all — check that command points to something executable and cwd (if set) actually exists.
"OAuth flow doesn't complete" in a remote dev environment
Expected if you're SSH'd into a box or running inside a container without a forwarded browser. The callback URL is hardcoded to localhost on the machine running the CLI. Either port-forward 7777 from the remote machine to your local browser, or switch to a server variant that supports an API-key env value instead of OAuth.
Environment variable in env isn't being substituted
Confirm the variable is set in the shell Gemini CLI was launched from, not just in a .env file the server itself might read separately — $VAR_NAME substitution in settings.json happens using the CLI process's own environment, so a variable only defined inside a project's .env won't be visible unless something has already exported it into your shell.
A tool call fails with a schema validation error
Check includeTools/excludeTools first — if you've scoped the list, confirm the tool you're calling wasn't accidentally excluded. If it's not a scoping issue, the server's declared input schema and what the model is actually sending have diverged; this is more common with servers under active development where the schema changed but the CLI cached an older tool list — restart the session to force a fresh tools/list.
Frequently Asked Questions
Q: What's the difference between url and httpUrl in a Gemini CLI server entry?
A: url is for SSE (Server-Sent Events) transport; httpUrl is for Streamable HTTP transport. They're different protocols under the MCP spec, so a server built for one won't work if you point it at the wrong field.
Q: Do I need to restart Gemini CLI after editing settings.json?
A: Yes for most changes — config is read at startup. Start a new session after editing either the user or project settings.json.
Q: What does the trust field actually skip?
A: trust: true bypasses the per-tool-call confirmation prompt for that server, so tool calls execute without you approving each one. Leave it false for any server that can take destructive actions (writing files, running database mutations) unless you fully trust the server's behavior.
Q: Can project settings.json and user settings.json define the same server differently?
A: Yes, and project scope wins for keys they both define. This is intentional — it lets a repo pin a project-specific connection (like a dev database URL) without editing your global config.
Q: Why would OAuth work on my laptop but not in CI or a remote container?
A: The OAuth callback is bound to localhost:7777 on the machine running Gemini CLI. Without a way to reach that port from a browser, the flow can't complete — CI and headless remote environments need an API-key-based server instead.