google-gemini / google-gemini/gemini-cli
Any settings.tools.core value (even []) emits a wildcard DENY that silently excludes all MCP tools – breaks run-gemini-cli's shipped pr-review example
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### What happened?
Setting **any** value for `tools.core` in settings – including the empty array `[]` – causes the policy engine to emit a wildcard `*` DENY rule that statically excludes **every MCP tool** (plus non-allowlisted built-ins such as `activate_skill`) from the tool registry. The exclusion is active in YOLO mode, and no MCP allow mechanism (`mcpServers..trust: true`, `mcp.allowed`, `mcp.autoAllowInHeadless`) can override it.
This silently breaks run-gemini-cli's own shipped [`examples/workflows/pr-review`](https://github.com/google-github-actions/run-gemini-cli/blob/main/examples/workflows/pr-review/gemini-review.yml) example, whose settings contain:
```json
"tools": {
"core": []
}
```
Symptom chain observed with that example as shipped (gemini-cli-extensions/code-review extension + github-mcp-server v0.27.0, GitHub Actions non-interactive mode):
1. Startup logs `Refresh for 'github' discovered 3 tools` – the MCP server connects and discovery works.
2. But the excluded tools are filtered out of `ToolRegistry.getFunctionDeclarations()`, so the model receives **no MCP tool declarations** and blind-guesses tool names.
3. `getTool()` excludes them too, so every guess fails with `Tool "" not found. Did you mean "activate_skill"?`.
4. The model burns through `maxSessionTurns` trying 22+ name variants (`get_pull_request`, `github.get_pull_request`, `pull_request_read`, prefixed/unprefixed FQNs, ...) and the run dies with `FatalTurnLimitedError`.
5. With `--output-format json`, the CLI embeds `INVALID_STREAM` / `FatalTurnLimitedError` in stdout while **exiting 0**, so the surrounding action reports success and simply posts no review (the action-side half of that is google-github-actions/run-gemini-cli#519).
The net effect: anyone deploying the pr-review example as documented gets a green workflow that never reviews anything, with no error surfaced anywhere.
### Root cause
At tag v0.50.0, [`packages/core/src/policy/config.ts` lines 545–562](https://github.com/google-gemini/gemini-cli/blob/v0.50.0/packages/core/src/policy/config.ts#L545-L562): any truthy `settings.tools.core` (an empty array is truthy) adds a wildcard DENY one notch below the core allowlist's ALLOW priority:
```ts
if (settings.tools?.core) {
mapToolsToRules(
settings.tools.core,
CORE_TOOLS_FLAG_PRIORITY,
'Settings (Core Tools)',
nonPlanModes,
);
// If core tools are restricted, we should add a default DENY rule for everything else
// at a slightly lower priority than the explicit allows.
rules.push({
toolName: '*',
decision: PolicyDecision.DENY,
priority: CORE_TOOLS_FLAG_PRIORITY - 0.01,
source: 'Settings (Core Tools Allowlist Enforcement)',
modes: nonPlanModes,
});
}
```
With the priority constants from the same file ([lines 75–81](https://github.com/google-gemini/gemini-cli/blob/v0.50.0/packages/core/src/policy/config.ts#L75-L81)), the resulting ranking is:
| Rule | Priority | Decision |
| --- | --- | --- |
| `tools.allowed` | 4.3 | ALLOW |
| `tools.core` entries | 4.25 | ALLOW |
| core-allowlist wildcard `*` | **4.24** | **DENY** |
| `mcpServers..trust: true` | 4.2 | ALLOW |
| `mcp.allowed` / `mcp.autoAllowInHeadless` (#27215) | 4.1 | ALLOW |
| YOLO allow-all | 1.998 | ALLOW |
Every MCP allow mechanism ranks below the wildcard DENY; only `tools.allowed` and explicit `tools.core` entries outrank it. And because [`PolicyEngine.getExcludedTools()`](https://github.com/google-gemini/gemini-cli/blob/v0.50.0/packages/core/src/policy/policy-engine.ts#L872) converts static DENYs into registry exclusions consumed by `ToolRegistry.getActiveTools()`, the tools are not merely denied at execution time – their declarations are never sent to the model, which is what produces the blind-guessing loop instead of a clean policy error.
Behaviour introduced in v0.41.0 by #25720.
This also contradicts the documentation: [`docs/reference/configuration.md`](https://github.com/google-gemini/gemini-cli/blob/v0.50.0/docs/reference/configuration.md) describes `tools.core` as "Restrict the set of built-in tools with an allowlist" – nothing indicates it also disables all MCP tools regardless of MCP trust/allow settings.
### What did you expect to happen?
`tools.core: []` should restrict built-in tools (as documented) while leaving MCP tools governed by the MCP policy mechanisms – or at minimum fail loudly rather than silently stripping tool declarations.
Suggested fixes, in descending order of preference:
1. Scope the core-allowlist wildcard DENY to built-in tools (exclude `mcp_*` / declared MCP server tools from the `*` match), so `tools.core` matches its documented semantics and `trust`/`mcp.allowed`/`autoAllowInHeadless` keep working.
2. Failing that, fix the shipped run-gemini-cli pr-review example settings so they don't ship a config that disables the very MCP server the workflow depends on (I'm filing a linked issue on google-github-actions/run-gemini-cli).
3. At minimum, document the interaction in `docs/reference/configuration.md` and consider warning at startup when `tools.core` is set and MCP servers are configured but end up fully excluded.
### Steps to reproduce
Minimal, generic repro (no GitHub Actions needed):
`settings.json`:
```json
{
"mcpServers": {
"github": {
"command": "github-mcp-server",
"args": ["stdio"],
"trust": true
}
},
"tools": {
"core": []
}
}
```
Run non-interactively:
```console
gemini --approval-mode yolo -p "Call any tool from the github MCP server"
```
Expected: MCP tools callable (server is trusted, YOLO mode).
Actual: discovery logs the server's tools, but the model gets no declarations for them and any attempted call returns `Tool "" not found.`
As-shipped repro: deploy `examples/workflows/pr-review/gemini-review.yml` from run-gemini-cli unmodified and open a PR – the review run exhausts `maxSessionTurns` and posts nothing while the workflow stays green.
Verified workarounds (end-to-end):
- add `"mcp_github_*"` (and/or exact FQNs) to `tools.core`, or use `"tools": { "allowed": ["mcp_github_*"] }` (4.3 outranks the 4.24 DENY);
- also allowlist `"activate_skill"` – the code-review extension's mandatory skill activation is a built-in tool call that `"core": []` denies.
### Client information
- gemini-cli v0.50.0 (behaviour present since v0.41.0 / #25720; source references above are at tag v0.50.0)
- Linux (GitHub Actions `ubuntu-latest` runner), non-interactive mode via google-github-actions/run-gemini-cli
- github-mcp-server v0.27.0, gemini-cli-extensions/code-review extension
### Login information
API key (`GEMINI_API_KEY`) in CI.
### Anything else we need to know?
Related issues that compound this one in the pr-review workflow:
- google-github-actions/run-gemini-cli#519 – the action keys failure on exit code and reads `.error` from stderr only, so the `INVALID_STREAM`/`FatalTurnLimitedError` embedded in stdout with exit 0 is reported as success.
- google-github-actions/run-gemini-cli#510 – the code-review extension's prompts reference stale hard-coded MCP tool names, which makes the blind-guessing described above even less likely to accidentally land on a valid name.
Contributor guide
Research direction
Start with packages/core/src/policy/config.ts, especially the tools.core rule construction and priority constants, then inspect PolicyEngine.getExcludedTools() in packages/core/src/policy/policy-engine.ts. Reproduce the behavior with the minimal settings.json and non-interactive command from the issue. Done means tools.core: [] restricts built-in tools without silently removing trusted or otherwise allowed MCP tools, with relevant behavior covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100