Skip to content

Commit 1e19964

Browse files
committed
feat(gemini): GM-AG-001 validate auth block in agent MCP servers (#809)
Gemini CLI v0.39.0 added support for an `auth` block inside `mcp_servers.<name>` entries in local agent markdown frontmatter - google-gemini/gemini-cli#24770. Two variants: - type: "google-credentials" - only `scopes` allowed beyond type - type: "oauth" - client_id, client_secret, scopes, authorization_url, token_url are all optional strings/arrays Schema extracted from packages/core/src/agents/agentLoader.ts Zod definitions in the upstream PR. - New FileType::GeminiAgent variant; detection for *.md files with `agents` parent and `.gemini` grandparent. - New crates/agnix-core/src/rules/gemini_agent.rs with the GeminiAgentValidator: - Extracts YAML frontmatter between --- markers - Parses via serde_yaml (workspace dep) - Walks mcp_servers.*.auth and validates each block - Enforces discriminator, rejects unknown fields per variant, type-checks strings + arrays, validates URL shape for authorization_url + token_url - Best-effort line scanner for reporting position - 24 unit tests covering: both variant happy paths, missing type, invalid type, non-object auth, unknown field per variant, non-string client_id, malformed URL, valid URL accepted, scopes shape, per- server independent validation, disable path, URL classifier unit tests, frontmatter extractor unit tests. - Valid/invalid fixtures under tests/fixtures/{valid,invalid}/gemini-agents/. - New gemini-agents rule category + GM-AG- prefix registered in rule_parity + "Gemini Agents" label in docs generator. - total_rules 413 -> 414, validator count 41 -> 42, FileType variants 45 -> 46. Closes #809.
1 parent 029f263 commit 1e19964

21 files changed

Lines changed: 920 additions & 20 deletions

File tree

AGENTS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ editors/
5757
├── vscode/ # VS Code extension
5858
├── jetbrains/ # JetBrains IDE plugin
5959
└── zed/ # Zed extension
60-
knowledge-base/ # 413 rules, 75+ sources, rules.json
60+
knowledge-base/ # 414 rules, 75+ sources, rules.json
6161
6262
tests/fixtures/ # Test cases by category
6363
```
@@ -66,7 +66,7 @@ tests/fixtures/ # Test cases by category
6666

6767
- `parsers/` - Frontmatter, JSON, Markdown parsing
6868
- `schemas/` - Type definitions (13 schemas: skill, hooks, agent, mcp, cline, roo, etc.)
69-
- `rules/` - Validators implementing Validator trait (41 validators)
69+
- `rules/` - Validators implementing Validator trait (42 validators)
7070
- `config.rs` - LintConfig, LintConfigBuilder, ConfigError, ToolVersions, SpecRevisions
7171
- `diagnostics.rs` - Diagnostic, Fix, DiagnosticLevel, ValidationOutcome, LintError (= CoreError), LintResult
7272
- `eval.rs` - Rule efficacy evaluation (precision/recall/F1)
@@ -178,7 +178,7 @@ cargo run --bin agnix-mcp # Run MCP server
178178

179179
## Rules Reference
180180

181-
413 rules defined in `knowledge-base/rules.json` (source of truth)
181+
414 rules defined in `knowledge-base/rules.json` (source of truth)
182182

183183

184184
Human-readable docs: `knowledge-base/VALIDATION-RULES.md`
@@ -190,7 +190,7 @@ Format: `[CATEGORY]-[NUMBER]` (AS-004, CC-HK-001, etc.)
190190
## Current State
191191

192192
- v0.18.0 - Production-ready with full validation pipeline
193-
- 413 validation rules across 41 validators
193+
- 414 validation rules across 42 validators
194194

195195
- 4200+ passing tests
196196
- LSP + MCP servers with VS Code extension

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- **GM-AG-001: Validate `auth` block in Gemini CLI agent MCP config (#809)**. Gemini CLI v0.39.0 added an `auth` block to `mcp_servers.*` entries in local agent markdown frontmatter (`.gemini/agents/*.md`) - see google-gemini/gemini-cli#24770. Two variants: `type: "google-credentials"` (only `scopes` allowed) and `type: "oauth"` (`client_id`, `client_secret`, `scopes`, `authorization_url`, `token_url`). GM-AG-001 enforces the discriminator, rejects unknown fields per variant, type-checks string/array values, and validates URL shape. Introduces `FileType::GeminiAgent` for `.gemini/agents/*.md` detection + a new `GeminiAgentValidator` with its own frontmatter extraction + auth-block line scanner. 24 unit tests + valid/invalid fixtures. New `gemini-agents` rule category. Closes #809.
1112
- **KR-SET-001 + KR-SET-002 + KR-SET-003: Validate Kiro Tool Search settings (#808)**. Kiro CLI 2.1 added the Tool Search feature configured via flat-key JSON in `.kiro/settings.json` (or `~/.kiro/settings.json`): `toolSearch.enabled` (boolean master toggle, default false), `toolSearch.minPct` (number, default 5), and `toolSearch.minTokens` (integer, default 50000). Docs at kiro.dev/docs/cli/mcp/tool-search/. Introduces a new `FileType::KiroSettings` variant + detection for files named `settings.json` with a `.kiro` parent directory, a new `KiroSettingsValidator`, and three rules: KR-SET-001 (HIGH, non-boolean `toolSearch.enabled`), KR-SET-002 (MEDIUM, type/negative/>100 on `toolSearch.minPct`), KR-SET-003 (MEDIUM, type/negative/fractional on `toolSearch.minTokens`). 23 unit tests + valid/invalid fixtures. New `kiro-settings` rule category. Closes #808.
1213
- **CC-HK-026 + CC-HK-027: Validate required fields on `type: "mcp_tool"` hooks (#804)**. Claude Code v2.1.118 added the `mcp_tool` hook action type; the docs at code.claude.com/docs/en/hooks#mcp-tool-hook-fields specify `server` (required string, names an already-connected MCP server) and `tool` (required string, names the tool to invoke), plus optional `input` (object for tool arguments with `${path}` substitution). CC-HK-026 flags missing/empty/non-string `server`; CC-HK-027 flags the same for `tool`. Both are HIGH severity (hook will fail at runtime), independently disableable. Also updated the typed `Hook::McpTool` schema in `schemas/hooks.rs` with the correct shape (was a stub with wrong fields). 8 new tests + valid/invalid fixtures. Closes #804.
1314
- **CC-SET-001: Validate `prUrlTemplate` in `.claude/settings.json` (#803)**. Claude Code v2.1.119 added `prUrlTemplate` as a top-level settings key that points the footer PR badge at a custom code-review URL instead of github.com. CC-SET-001 flags three misconfigurations: (a) non-string values (ERROR), (b) empty strings (ERROR, badge would never render), (c) strings with none of the documented placeholders `{host}`, `{owner}`, `{repo}`, `{number}`, `{url}` (WARNING, every PR would resolve to the same static URL). Ships with a new `ClaudeSettingsValidator` scoped to `.claude/settings.json`, `.claude/settings.local.json`, and `.claude/managed-settings.json` so sibling tools (`.amp/settings.json`, etc.) that share the Hooks FileType aren't false-positived. 19 unit tests + valid/invalid fixtures. Introduces a new `claude-settings` rule category to distinguish top-level settings rules from hooks, memory, skills, etc. Closes #803.

CLAUDE.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ editors/
5757
├── vscode/ # VS Code extension
5858
├── jetbrains/ # JetBrains IDE plugin
5959
└── zed/ # Zed extension
60-
knowledge-base/ # 413 rules, 75+ sources, rules.json
60+
knowledge-base/ # 414 rules, 75+ sources, rules.json
6161
6262
tests/fixtures/ # Test cases by category
6363
```
@@ -66,7 +66,7 @@ tests/fixtures/ # Test cases by category
6666

6767
- `parsers/` - Frontmatter, JSON, Markdown parsing
6868
- `schemas/` - Type definitions (13 schemas: skill, hooks, agent, mcp, cline, roo, etc.)
69-
- `rules/` - Validators implementing Validator trait (41 validators)
69+
- `rules/` - Validators implementing Validator trait (42 validators)
7070
- `config.rs` - LintConfig, LintConfigBuilder, ConfigError, ToolVersions, SpecRevisions
7171
- `diagnostics.rs` - Diagnostic, Fix, DiagnosticLevel, ValidationOutcome, LintError (= CoreError), LintResult
7272
- `eval.rs` - Rule efficacy evaluation (precision/recall/F1)
@@ -178,7 +178,7 @@ cargo run --bin agnix-mcp # Run MCP server
178178

179179
## Rules Reference
180180

181-
413 rules defined in `knowledge-base/rules.json` (source of truth)
181+
414 rules defined in `knowledge-base/rules.json` (source of truth)
182182

183183

184184
Human-readable docs: `knowledge-base/VALIDATION-RULES.md`
@@ -190,7 +190,7 @@ Format: `[CATEGORY]-[NUMBER]` (AS-004, CC-HK-001, etc.)
190190
## Current State
191191

192192
- v0.18.0 - Production-ready with full validation pipeline
193-
- 413 validation rules across 41 validators
193+
- 414 validation rules across 42 validators
194194

195195
- 4200+ passing tests
196196
- LSP + MCP servers with VS Code extension

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</p>
1313
</div>
1414

15-
<p align="center">Catch broken agent configs before your AI tools silently ignore them.<br>413 rules across Claude Code, Codex CLI, OpenCode, Cursor, Copilot, and more -<br>validating CLAUDE.md, SKILL.md, hooks, MCP configs, and other agent files.</p>
15+
<p align="center">Catch broken agent configs before your AI tools silently ignore them.<br>414 rules across Claude Code, Codex CLI, OpenCode, Cursor, Copilot, and more -<br>validating CLAUDE.md, SKILL.md, hooks, MCP configs, and other agent files.</p>
1616

1717
<p align="center"><strong>Auto-fix</strong> | <strong><a href="https://github.com/marketplace/actions/agnix-ci">GitHub Action</a></strong> | <strong><a href="https://marketplace.visualstudio.com/items?itemName=avifenesh.agnix">VS Code</a> + <a href="https://plugins.jetbrains.com/plugin/30087-agnix">JetBrains</a> + <a href="https://github.com/agent-sh/agnix.nvim">Neovim</a> + <a href="https://zed.dev/extensions?query=agnix">Zed</a></strong></p>
1818

@@ -37,7 +37,7 @@
3737

3838
**Bad patterns get amplified.** AI assistants don't ignore wrong configs - they [learn from them](https://www.augmentcode.com/guides/enterprise-coding-standards-12-rules-for-ai-ready-teams).
3939

40-
agnix validates all of it - 413 rules sourced from official specs, academic research, and real-world breakage patterns. Auto-fix included.
40+
agnix validates all of it - 414 rules sourced from official specs, academic research, and real-world breakage patterns. Auto-fix included.
4141

4242
> **Want to try it first?** [Open the playground](https://agent-sh.github.io/agnix/playground) - paste any agent config, see diagnostics instantly. No install, runs in your browser.
4343

crates/agnix-cli/tests/rule_parity.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ fn extract_implemented_rule_ids() -> BTreeSet<String> {
166166
// Known rule ID prefixes to filter out false positives
167167
let valid_prefixes = [
168168
"AS-", "CC-SK-", "CC-HK-", "CC-AG-", "CC-MEM-", "CC-OS-", "CC-PL-", "CC-SET-", "AGM-",
169-
"MCP-", "COP-", "CUR-", "CLN-", "CDX-", "OC-", "GM-", "XML-", "REF-", "PE-", "XP-", "VER-",
170-
"WS-", "CR-SK-", "CL-SK-", "CP-SK-", "CX-SK-", "OC-SK-", "WS-SK-", "KR-SK-", "KR-AG-",
171-
"KR-HK-", "KR-PW-", "KR-MCP-", "KR-SET-", "KIRO-", "AMP-SK-", "AMP-", "RC-SK-", "ROO-",
169+
"MCP-", "COP-", "CUR-", "CLN-", "CDX-", "OC-", "GM-", "GM-AG-", "XML-", "REF-", "PE-",
170+
"XP-", "VER-", "WS-", "CR-SK-", "CL-SK-", "CP-SK-", "CX-SK-", "OC-SK-", "WS-SK-", "KR-SK-",
171+
"KR-AG-", "KR-HK-", "KR-PW-", "KR-MCP-", "KR-SET-", "KIRO-", "AMP-SK-", "AMP-", "RC-SK-",
172+
"ROO-",
172173
];
173174

174175
fn extract_from_file(
@@ -336,6 +337,10 @@ fn infer_fixture_coverage(rules: &[RuleEntry]) -> HashMap<String, Vec<String>> {
336337
("amp-checks", vec!["amp-checks"]),
337338
("roo-code-skills", vec!["per_client_skills"]),
338339
("gemini-cli", vec!["gemini_md", "gemini_md-invalid"]),
340+
(
341+
"gemini-agents",
342+
vec!["valid/gemini-agents", "invalid/gemini-agents"],
343+
),
339344
("codex", vec!["codex", "codex-invalid"]),
340345
("roo-code", vec!["roo-code"]),
341346
("windsurf", vec!["windsurf", "windsurf-legacy"]),
@@ -526,6 +531,7 @@ fn test_rules_json_integrity() {
526531
"claude-memory",
527532
"claude-output-styles",
528533
"claude-settings",
534+
"gemini-agents",
529535
"agents-md",
530536
"claude-plugins",
531537
"mcp",

crates/agnix-core/locales/en.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,17 @@ rules:
806806
cc_hk_027:
807807
message: "MCP tool hook at %{location} is missing required 'tool' field"
808808
suggestion: "Add a 'tool' field naming the MCP tool to invoke (e.g. \"tool\": \"security_scan\")"
809+
gm_ag_001:
810+
not_object: "MCP server '%{server}': 'auth' block must be a mapping (type + fields)"
811+
missing_type: "MCP server '%{server}': 'auth' block is missing required 'type' discriminator"
812+
invalid_type: "MCP server '%{server}': 'auth.type' is '%{value}', must be one of: %{valid}"
813+
unknown_field_google: "MCP server '%{server}': auth.type is 'google-credentials' - field '%{field}' is not supported for this variant (only 'scopes' is)"
814+
unknown_field_oauth: "MCP server '%{server}': auth.type is 'oauth' - field '%{field}' is not part of the oauth schema"
815+
not_string: "MCP server '%{server}': 'auth.%{field}' must be a string"
816+
invalid_url: "MCP server '%{server}': 'auth.%{field}' is '%{value}', must be a valid http(s) URL"
817+
scopes_not_array: "MCP server '%{server}': 'auth.scopes' must be an array of strings"
818+
scope_not_string: "MCP server '%{server}': 'auth.scopes[%{index}]' must be a string"
819+
suggestion: "Align the auth block with the schema documented at google-gemini/gemini-cli#24770. For google-credentials: {type: google-credentials, scopes?: [...]}. For oauth: {type: oauth, client_id?, client_secret?, scopes?, authorization_url?, token_url?}"
809820
kr_set_001:
810821
type_error: "toolSearch.enabled must be a boolean (true or false)"
811822
suggestion: "Set toolSearch.enabled to true or false. Example: {\"toolSearch.enabled\": true}. See https://kiro.dev/docs/cli/mcp/tool-search/"

crates/agnix-core/src/file_types/detection.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,17 @@ pub fn detect_file_type(path: &Path) -> FileType {
318318
return FileType::KiroSettings;
319319
}
320320

321+
// Gemini CLI local agent markdown files live under `.gemini/agents/`.
322+
// They have YAML frontmatter with `kind: local`, a `mcp_servers` map,
323+
// and a system_prompt. Schema documented via the gemini-cli source at
324+
// packages/core/src/agents/agentLoader.ts.
325+
if ends_with_ignore_ascii_case(filename, ".md")
326+
&& parent_eq_ignore_ascii_case(parent, "agents")
327+
&& parent_eq_ignore_ascii_case(grandparent, ".gemini")
328+
{
329+
return FileType::GeminiAgent;
330+
}
331+
321332
match filename {
322333
// Amp code review checks (.agents/checks/**/*.md), excluding AGENTS
323334
// variants so AGENTS.md keeps ClaudeMd validator coverage.

crates/agnix-core/src/file_types/types.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub enum FileType {
100100
KiroMcp,
101101
/// Kiro CLI settings (.kiro/settings.json or ~/.kiro/settings.json)
102102
KiroSettings,
103+
/// Gemini CLI agent definition (.gemini/agents/*.md)
104+
GeminiAgent,
103105
/// Other .md files (for XML/import checks)
104106
GenericMarkdown,
105107
/// Skip validation
@@ -175,6 +177,7 @@ impl fmt::Display for FileType {
175177
FileType::KiroHook => "KiroHook",
176178
FileType::KiroMcp => "KiroMcp",
177179
FileType::KiroSettings => "KiroSettings",
180+
FileType::GeminiAgent => "GeminiAgent",
178181
FileType::GenericMarkdown => "GenericMarkdown",
179182
FileType::Unknown => "Unknown",
180183
})

crates/agnix-core/src/registry.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ impl ValidatorRegistryBuilder {
398398
///
399399
/// Used by `BuiltinProvider` (via `debug_assert_eq!`) and tests to catch
400400
/// accidental additions or removals without updating all providers.
401-
const EXPECTED_BUILTIN_COUNT: usize = 76;
401+
const EXPECTED_BUILTIN_COUNT: usize = 77;
402402

403403
// -- Category providers -----------------------------------------------------
404404
//
@@ -814,6 +814,11 @@ impl ValidatorProvider for MiscProvider {
814814
Some("KiroSettingsValidator"),
815815
kiro_settings_validator,
816816
),
817+
(
818+
FileType::GeminiAgent,
819+
Some("GeminiAgentValidator"),
820+
gemini_agent_validator,
821+
),
817822
(
818823
FileType::GenericMarkdown,
819824
Some("CrossPlatformValidator"),
@@ -977,6 +982,10 @@ fn kiro_settings_validator() -> Box<dyn Validator> {
977982
Box::new(crate::rules::kiro_settings::KiroSettingsValidator)
978983
}
979984

985+
fn gemini_agent_validator() -> Box<dyn Validator> {
986+
Box::new(crate::rules::gemini_agent::GeminiAgentValidator)
987+
}
988+
980989
fn kiro_mcp_validator() -> Box<dyn Validator> {
981990
Box::new(crate::rules::kiro_mcp::KiroMcpValidator)
982991
}
@@ -1475,6 +1484,7 @@ mod tests {
14751484
| FileType::KiroHook
14761485
| FileType::KiroMcp
14771486
| FileType::KiroSettings
1487+
| FileType::GeminiAgent
14781488
| FileType::GenericMarkdown => (),
14791489
FileType::Unknown => {
14801490
panic!("Unknown must not appear in validatable_types")
@@ -1963,7 +1973,7 @@ mod tests {
19631973

19641974
#[test]
19651975
fn misc_provider_count() {
1966-
assert_eq!(MiscProvider.named_validators().len(), 26);
1976+
assert_eq!(MiscProvider.named_validators().len(), 27);
19671977
}
19681978

19691979
#[test]

0 commit comments

Comments
 (0)