Skip to main content
Glama
mcp-secret-sentinel

mcp-secret-sentinel

mcp-secret-sentinel

tests Python 3.10+ MCP SDK 1.x | 2.x License: MIT

Local secret guardrails for AI coding agents. An MCP server, a CLI and agent hooks that catch API keys, tokens, private keys and passwords in code, commits, prompts, tool calls and MCP configs, before they leave the machine. 64 detectors, base64-aware, every finding redacted. No account, no network calls.

Secrets rarely leak through hackers; they leak through commits, and now through agents. GitGuardian's State of Secrets Sprawl 2026 counted 28.65 million new secrets on public GitHub in 2025 (+34%), AI-service keys up 81%, 24,008 unique secrets in MCP configuration files, and a higher leak rate in commits co-written with AI coding assistants. A key pasted into .mcp.json, echoed by cat .env into a transcript, or written into a config by an agent in a hurry is compromised from that moment, even if the next commit deletes it.

Secret Sentinel puts a checkpoint at each of those moments:

Moment

What stops it

The agent writes a key into a tracked file, a shell command or an MCP call

Agent hook denies the tool call (Claude Code, Codex, Cursor)

You paste a key into a prompt

Agent hook blocks the prompt before it reaches the model

A tool prints a key (cat .env, a config Read, a verbose log)

Agent hook replaces it with [REDACTED:<detector>] before the model sees it

A key sits in .mcp.json, claude_desktop_config.json, CLAUDE.md...

scan_agent_configs audits every MCP client and agent file, and flags the ones git tracks

A commit or a push is about to publish one

scan_git_staged / scan_git_range, the pre-commit hook and the GitHub Action

Logs or output have to be shared

redact_text masks every secret and keeps everything else

The full secret value never appears in any output, so it never enters the conversation transcript either.

Install

Claude Code plugin (skill + agent hooks + MCP server in one step):

/plugin marketplace add AleBrito124356/mcp-secret-sentinel
/plugin install secret-sentinel@secret-sentinel

The hooks run straight from the plugin checkout with any Python 3.10+ (they use only the standard library). The MCP server starts with uv.

MCP server only (any MCP client):

claude mcp add secret-sentinel -- uvx mcp-secret-sentinel
{
  "mcpServers": {
    "secret-sentinel": { "command": "uvx", "args": ["mcp-secret-sentinel"] }
  }
}

CLI (git hooks, CI, scripts): pipx install mcp-secret-sentinel or pip install mcp-secret-sentinel. To run the latest code from GitHub instead of PyPI, use uvx --from git+https://github.com/AleBrito124356/mcp-secret-sentinel mcp-secret-sentinel.

Agent skill only (Agensi, skills.sh, any agent that reads SKILL.md): the skill lives in skills/secret-sentinel. It tells the agent when to scan, how to fix a finding and how to rotate a leaked credential, and uses the MCP tools or the CLI when they are available.

Related MCP server: re-leak-scan

Agent hooks

mcp-secret-sentinel hook reads one hook event as JSON on stdin and prints the agent's decision. The plugin wires it up for you; without the plugin:

mcp-secret-sentinel install-agent-hooks --agent claude              # .claude/settings.json (shared)
mcp-secret-sentinel install-agent-hooks --agent claude --scope local # .claude/settings.local.json
mcp-secret-sentinel install-agent-hooks --agent cursor              # .cursor/hooks.json

The installer merges into the existing settings and keeps every other hook. Running it again refreshes its own entries instead of duplicating them.

Agent event

Behaviour

Claude Code / Codex PreToolUse on Write, Edit, MultiEdit, NotebookEdit, Bash, PowerShell and mcp__* tools

Denies the call when the new content, the command or the MCP arguments contain a secret (permissionDecision: "deny", with the redacted reason and the fix). Writes to files git ignores (a local .env) and files outside any repository are allowed: that is where secrets belong. A tracked .env is still protected. Edits to .secret-sentinel.toml, the baseline or the [tool.secret-sentinel] table answer "ask", so the guarded agent cannot quietly loosen its own guard.

UserPromptSubmit

Blocks the prompt and sets suppressOriginalPrompt, so the key is not echoed back into the block message.

PostToolUse on Read, Bash, PowerShell, Grep, WebFetch and mcp__* tools

Returns updatedToolOutput with the same shape and every secret replaced (a private key goes as a whole block, header to footer), plus a note telling the model not to try to recover it.

Cursor beforeShellExecution, beforeMCPExecution, beforeReadFile, beforeSubmitPrompt

permission: "deny" / continue: false with a redacted message.

By default the hook blocks high and critical findings and lets medium ones (JWTs, high-entropy strings) through; hook --fail-on medium is stricter. Excluded paths in .secret-sentinel.toml are respected. The event is read as UTF-8 whatever the console code page, so accented project paths work on Windows. Anything unexpected (bad JSON, a broken config, an internal error) fails open with a non-blocking exit status, so the hook can never wedge the agent; a git failure other than "not a repository" keeps scanning on. Our own MCP tools are exempt, since they receive secrets on purpose.

MCP tools

Tool

Arguments

Returns

scan_text

text, source_name="input", max_findings=200

Findings for a raw snippet (code, config, diff, logs)

scan_file

path, max_findings=200

Findings for one file; decodes UTF-8/16/32 by BOM, skips binaries and files over 5 MB

scan_directory

path, max_files=500, max_findings=200

Recursive scan. Inside git, the file list comes from git itself (every .gitignore, negations and global excludes honoured)

scan_git_staged

repo_path, max_findings=200

Only the lines added in git diff --cached: exactly what the next commit would publish

scan_git_range

repo_path, base="@{upstream}", head="HEAD", max_commits=200, max_findings=200

The commits in base..head: by default exactly what the next git push would publish

scan_git_history

repo_path, max_commits=50, all_branches=false, max_findings=200

Lines added by the last N commits (of HEAD, or every branch, tag and the stash), each tagged with its commit

scan_agent_configs

project_path, include_user=true, include_transcripts=false, max_findings=200

Audit of MCP client configs and agent instruction files (see below)

redact_text

text

{"text", "redactions", "by_pattern"}: the text with every secret replaced by [REDACTED:<detector>]

list_patterns

—

Active detectors with severity and remediation advice, plus the allowlist rules

Every tool is annotated read-only (readOnlyHint, no open-world access), so clients can run them without a confirmation prompt. The scan tools publish an outputSchema and return structuredContent (MCP SDK 1.10+ / 2.x). The server also offers three prompts, pre_push_check, leak_response and audit_agent_configs, and a secret-sentinel://patterns resource.

All scan tools return the same shape:

{
  "clean": false,
  "findings": [
    {
      "file": "config/notify.yaml",
      "line": 14,
      "pattern": "Slack incoming webhook",
      "severity": "high",
      "redacted": "hook…(77 chars)",
      "advice": "Anyone with this URL can post messages to your workspace. Regenerate the webhook in your Slack app settings and load the URL from an environment variable."
    }
  ],
  "files_scanned": 6,
  "summary": "Found 1 potential secret(s) across 6 scanned file(s): 1 high. Do NOT commit or push until these are removed or rotated."
}

suppressed is added when inline comments silenced findings, baselined when the baseline accepted some. Bounded responses: every scan lists at most max_findings findings (default 200, most severe first, 0 = no limit). When it has to cut, the report says truncated: true and keeps total_findings, counts_by_severity, counts_by_pattern and top_files, so a noisy tree cannot flood the agent's context.

Auditing agent and MCP client configs

scan_agent_configs (CLI: mcp-secret-sentinel agents [PROJECT]) checks:

  • Project files: .mcp.json, .claude/settings.json, .claude/settings.local.json, .cursor/mcp.json, .vscode/mcp.json, .vscode/settings.json, .gemini/settings.json, .codex/config.toml, .windsurf/mcp.json, .amazonq/mcp.json, .kiro/settings/mcp.json, .roo/mcp.json, opencode.json, and the agent instruction files CLAUDE.md, CLAUDE.local.md, AGENTS.md, GEMINI.md, .cursorrules, .windsurfrules, .github/copilot-instructions.md. Each one reports whether git tracks it.

  • User files: Claude Code (~/.claude.json, ~/.claude/settings.json, ~/.claude/CLAUDE.md), Claude Desktop, Cursor, VS Code, Cline, Windsurf, Gemini CLI, Codex, Amazon Q, Kiro, Continue, Zed, opencode, Goose and LM Studio.

  • Session transcripts (opt-in): the newest Claude Code and Codex *.jsonl logs. A key found there was also sent to the model provider.

Files with findings carry a hint on how that client references the environment instead of a literal, for example "env": {"API_KEY": "${API_KEY}"} in Claude Code's .mcp.json or ${input:...} prompts in VS Code.

What it detects

64 regex detectors, plus an entropy check and base64 decoding:

Family

Detectors

AI and LLM

OpenAI (sk-proj-, sk-svcacct-, sk-admin-, legacy), Anthropic, xAI, DeepSeek-style sk-<hex>, Perplexity, OpenRouter, Groq, NVIDIA, Hugging Face, Replicate, Together AI, Cerebras, AWS Bedrock API keys

LLM tooling

LangSmith, Langfuse, Pinecone, Firecrawl, Tavily

Code hosting and packages

GitHub (ghp_, gho_, ghu_, ghs_, ghr_, github_pat_), GitLab (glpat-, glptt-, gldt-, glrt-), npm, PyPI

Cloud and infrastructure

AWS access keys and secret keys, Azure storage keys, Google API keys and OAuth client secrets, DigitalOcean, Cloudflare, Vercel, Supabase (sb_secret_, sbp_), Neon, PlanetScale, Fly.io, Databricks, HashiCorp Vault, Doppler

Payments, messaging, SaaS

Stripe live keys, Square, Shopify, SendGrid, Resend, Mailchimp, Twilio, Slack tokens and webhooks, Discord webhooks, Telegram bots, Linear, Notion, Atlassian, Postman, Sentry, Grafana, PostHog

Keys and tokens

Private key blocks (RSA, EC, DSA, OPENSSH, PGP, encrypted), age secret keys, JWTs

Credentials in URLs

Database and queue connection strings (Postgres, MySQL/MariaDB, MongoDB, AMQP, Redis) and http(s)/ftp/smtp URLs with user:password@

Generic

password / secret / api_key / token-style assignments in quoted code, and dotenv-style UPPER_CASE=value lines

  • Entropy: quoted strings of 20+ characters assigned to a variable, with an empirical entropy of at least 4.5 bits/char, when no specific detector claimed the span.

  • Base64: runs of 20+ base64 characters (standard or URL-safe) that decode to printable text are decoded once and scanned again, so a credential in a Kubernetes Secret manifest, a Basic auth header or an encoded PEM key is found. The finding is named after the inner detector with (base64).

  • Formats come from the gitleaks, betterleaks and trufflehog rule sets. sb_publishable_ keys, which are meant to be public, are not reported.

For credentials in URLs the reported value is the password, and the allowlist judges the password alone: postgres://app:<literal>@${DB_HOST}/app is still reported.

What it deliberately ignores (allowlist)

Each candidate value is checked against these heuristics before being reported:

  • Too short: values under 8 characters.

  • Masked: values that are mostly (≥ 80%) X, x, * or dots, including vendor prefixes followed by an XXXX… run.

  • example, placeholder, changeme and your-…-here markers, and <angle brackets>.

  • Templates: ${VAR}, {{ jinja }}, {% … %}, <%= … %>, #{…}.

  • Variable references: a value that is only $UPPER_CASE_VAR, $(command), $env:VAR or %VAR%.

  • Format placeholders: %(name)s or {name}.

  • Environment lookups: values referencing os.environ or process.env.

  • Generic assignments and entropy only: code rather than a literal (os.getenv("X"), settings.DB_PASSWORD, SECRETS[0], one side of a concatenation), values shaped like a regular expression (^(Write|Edit)$, [a-z]{32}), plain links with no credentials, query or fragment, and filesystem paths (C:\Users\..., ~/..., /usr/local/...), which fill agent configs. These rules never apply to vendor formats.

  • Reserved documentation hosts: a credentialed URL pointing at example.com / .net / .org, *.example or *.invalid.

Inline suppression

TEST_DSN = "postgres://ci:ci-only-password@db:5432/test"  # secret-sentinel: ignore

detect-secrets' pragma: allowlist secret and gitleaks' gitleaks:allow work too, so files annotated for those tools need no changes. Markers are case-insensitive, cover only their own line, and apply to staged and committed lines. Suppressed findings are counted in suppressed, never hidden silently.

Project configuration

mcp-secret-sentinel init writes a commented .secret-sentinel.toml. The same keys work in a [tool.secret-sentinel] table of pyproject.toml. The file is found from the scanned path upwards, stopping at the repository root, and applies to the CLI, the MCP tools and the agent hooks alike.

exclude_paths = ["tests/fixtures/**", "*.snap"]   # globs relative to this file; ** spans directories
allowlist = ["^sk_test_"]                         # regexes matched against the secret value
disable = ["JSON Web Token"]                      # detector names, "High-entropy string" included
baseline = ".secret-sentinel-baseline.json"       # optional

[[rules]]
name = "Acme internal token"
regex = 'acme_[a-f0-9]{32}'
severity = "critical"        # critical | high | medium
advice = "Revoke it in the Acme console."

Unknown keys, unknown detector names and invalid regexes are errors that name the file and the key, so a typo cannot silently weaken a scan. Custom rules run before the built-in detectors and show up in SARIF as their own rules.

Baselines

Adopting a scanner on an existing repository usually means a few known findings (old test fixtures, revoked keys). Review them, then accept them:

mcp-secret-sentinel baseline          # writes .secret-sentinel-baseline.json

Later scans report only secrets that are not in the baseline, and count the rest in baselined. The file stores a salted PBKDF2 fingerprint of each value (never the value, never a fast hash), plus the file and line to help reviewers. Matching ignores the line, so an accepted value stays accepted when the code around it moves; private keys, whose detected header is the same for every key, are tied to their file name as well. The file is written where scans look for it: the config's baseline path, else the project root. Commit it; scan --no-config ignores it.

Command line

mcp-secret-sentinel with no arguments starts the stdio server, so MCP client configs stay one word long. With a command it becomes a CLI:

mcp-secret-sentinel scan                       # the current directory
mcp-secret-sentinel scan src config/app.yaml   # files and directories
git diff | mcp-secret-sentinel scan -          # stdin
mcp-secret-sentinel scan --staged              # what the next commit would add
mcp-secret-sentinel scan --range               # what the next push would publish (@{upstream}..HEAD)
mcp-secret-sentinel scan --history 100 --all-branches
mcp-secret-sentinel scan --format sarif -o secrets.sarif
mcp-secret-sentinel redact app.log > app.redacted.log
mcp-secret-sentinel agents .                   # audit agent / MCP client configs
mcp-secret-sentinel install-hook               # git pre-commit hook
mcp-secret-sentinel install-agent-hooks --agent claude
mcp-secret-sentinel baseline
mcp-secret-sentinel init
mcp-secret-sentinel patterns                   # detectors, allowlist rules, suppression markers
$ mcp-secret-sentinel scan src
src/billing.py:2  [critical] Stripe live key  sk_l…(32 chars)
    Roll the key in the Stripe Dashboard (Developers -> API keys). Live keys grant access to real payment data.
src/notify.py:3  [high] Slack incoming webhook  hook…(69 chars)
    Anyone with this URL can post messages to your workspace. Regenerate the webhook in your Slack app settings and load the URL from an environment variable.

Found 2 potential secret(s) across 2 scanned file(s): 1 critical, 1 high. Do NOT commit or push until these are removed or rotated.

Option (scan)

Meaning

--format text|json|sarif

Human report (default), the MCP JSON report, or SARIF 2.1.0

--fail-on critical|high|medium

Lowest severity that makes the exit status 1 (default medium, i.e. any finding)

--max-findings N

List at most N findings, most severe first (default 200; 0 = no limit; SARIF lists everything unless set)

--max-files N

Stop a directory scan after N files (default 5000)

--max-commits N

With --range: scan at most the N newest commits (default 200)

--config FILE / --no-config

Use a specific configuration, or ignore configuration and baseline

--baseline FILE

Accept the findings recorded in FILE

-o, --output FILE

Write the report to a file; the one-line summary goes to stderr

Exit status: 0 clean (or only findings below --fail-on), 1 findings at or above --fail-on, 2 usage error (bad option, missing path, not a git repository, unknown revision, broken config). Output is redacted in every format.

Block commits with a git hook

mcp-secret-sentinel install-hook            # in the repository, or: install-hook path/to/repo

This writes .git/hooks/pre-commit (or the directory core.hooksPath points at). The hook runs scan --staged with the Python that installed it, so a commit is refused while its staged lines contain a secret; git commit --no-verify skips it once. The installer never overwrites another tool's hook unless you pass --force, and then keeps the old one as pre-commit.bak.

With the pre-commit framework:

repos:
  - repo: https://github.com/AleBrito124356/mcp-secret-sentinel
    rev: v0.3.0
    hooks:
      - id: secret-sentinel

GitHub Action

name: secrets
on: [push, pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write   # for the SARIF upload
    steps:
      - uses: actions/checkout@v4
      - uses: AleBrito124356/mcp-secret-sentinel@v0.3.0
        with:
          fail-on: high          # critical | high | medium
          # range: origin/main..HEAD   # scan only new commits (checkout with fetch-depth: 0)
          use-config: ${{ github.event_name != 'pull_request' }}  # a PR could edit the config

The action writes a redacted SARIF report to code scanning (one rule per detector, with security-severity), puts the text report in the job summary, and fails the step on findings at or above fail-on. A scan that cannot run (a bad range, a shallow clone, a broken config) fails with its own error instead of being reported as findings. --format sarif on the CLI produces the same SARIF 2.1.0 log for any other CI.

How it works

flowchart TD
    A[Agent hook, MCP tool or CLI] --> B{Source}
    B -->|text / file| C[Decode by BOM, split on newline only]
    B -->|directory| D[git ls-files, or a walk outside git;<br/>skip caches, envs, binaries, lockfiles,<br/>config excludes] --> C
    B -->|staged / range / history| E[git diff / git log -p, zero context,<br/>diff config overridden, no external programs] --> P[Hunk-counting parser:<br/>added lines, real paths, commit tags] --> C
    C --> S{Inline marker?}
    S -->|yes| U[Counted in suppressed]
    S -->|no| F[Custom rules, then 64 detectors;<br/>specific patterns claim spans first]
    F --> G[Base64 runs decoded and rescanned]
    G --> H2[Entropy >= 4.5 bits/char on<br/>assigned strings of 20+ chars]
    H2 --> H{Allowlist: placeholders, templates,<br/>variables, code, regexes, config allowlist}
    H -->|placeholder| I[Dropped]
    H -->|real candidate| BL{In the baseline?}
    BL -->|yes| BB[Counted in baselined]
    BL -->|no| J[Redact: at most 4 chars and<br/>a quarter of the value, + length]
    J --> K[Report, SARIF, hook decision<br/>or REDACTED marker]

Encodings and line numbers

Files are decoded by byte-order mark (UTF-8, UTF-16 LE/BE, UTF-32) before the binary check, and BOM-less UTF-16 is recognised too, so the output of Windows PowerShell 5.1's > and Out-File is scanned instead of skipped. Lines are split on \n only, so form feeds, \x85, U+2028 and U+2029 inside a line never shift the reported line numbers.

Redaction guarantee

Every finding shows at most the first 4 characters, and never more than a quarter of the value, plus the total length: "hook…(77 chars)", "Tr…(8 chars)". The full value never appears in the output, the transcript, hook decisions or logs. This is enforced in code (a single redact() choke point) and in the test suite, which asserts that raw values are absent from serialized results, hook decisions and real MCP stdio traffic, structured content included.

Git scans read the text, not your diff settings

The git tools parse git diff / git log -p output, and local settings could change it: an external diff tool replaces the diff, diff.mnemonicPrefix or diff.noprefix corrupt the file names. Every git call therefore passes --no-ext-diff --no-textconv --src-prefix=a/ --dst-prefix=b/ -U0 --inter-hunk-context=0 --no-color, overrides core.quotePath, core.fsmonitor, diff.noprefix, diff.mnemonicPrefix, diff.relative, diff.submodule, log.showSignature and log.showRoot with -c, drops GIT_EXTERNAL_DIFF / GIT_DIFF_OPTS, takes no optional locks, closes stdin, and rejects revisions that start with -. The diff parser is a state machine driven by the hunk line counts, so an added line that reads +++ something is content, never a file header.

Limitations

  • Regex detection, not verification: findings are not checked against the provider (that would send your secrets over the network). Dedicated scanners such as gitleaks, betterleaks or trufflehog, with hundreds of rules and optional live verification, belong in your pipeline too; this is the fast local checkpoint an agent runs before the commit exists.

  • Outside git, only simple root-.gitignore patterns apply (no negations, no **, no nested files). Inside a git work tree the file list comes from git and every rule applies.

  • Entropy needs diversity: a candidate needs at least 23 distinct characters to clear 4.5 bits/char. Short random strings are covered by the regex detectors instead.

  • Unquoted generic assignments are only detected in dotenv-style UPPER_CASE=value lines, a deliberate trade against false positives.

  • Line-by-line matching: a secret split across lines is only caught by its first line (such as a BEGIN PRIVATE KEY header). Base64 is decoded one level deep.

  • Inline suppression is same-line only, because in a zero-context diff the line above is usually not visible.

  • Merge commits are not diffed (git's default for git log -p), so a secret introduced only while resolving a conflict is missed by the range and history scans; scan_git_staged still sees it before the merge commit is made.

  • Binary files in git are not scanned; the summary lists them by name.

  • Cursor hook support follows Cursor's published hook protocol; Claude Code's is covered by end-to-end tests over a real pipe.

Development

git clone https://github.com/AleBrito124356/mcp-secret-sentinel
cd mcp-secret-sentinel
pip install -e ".[dev]"
python -m pytest

From the source tree, python -m mcp_secret_sentinel runs the CLI (the MCP server with no arguments). core.py, config.py, agents.py and hooks.py use only the standard library; server.py holds the MCP wiring.

The test suite never contains a realistic secret literal: every positive fixture is assembled at runtime by concatenation, and a test scans this repository and requires it to be clean. It covers every detector, the allowlist, base64 decoding, entropy, redaction, encodings, line numbers, git parsing against real repositories, the CLI as a subprocess, hook decisions (including through the plugin's shell launcher), the agent-config audit, configuration and baselines, and the MCP server over real stdio with the SDK's own client, structured output included. CI runs it on Linux, Windows and macOS, on Python 3.10 to 3.13, and on both majors of the MCP SDK.

Part of a family of small, dependency-light MCP servers:

License

MIT

Available Tools

9 tools
list_patternsA
Read-onlyIdempotent

List the active secret detectors and allowlist rules.

Returns: {"count": int, "patterns": [{"name", "severity", "advice"}], "entropy_detector": {...threshold and advice...}, "allowlist_rules": [{"name", "description"}]} Raw regexes are intentionally not exposed.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so safety is covered by structured data. The description adds genuinely useful behavioral context beyond that: the return shape and the explicit note that raw regexes are intentionally withheld, which prevents an agent from hunting for them.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence of purpose followed by a compact return-shape block; nothing is padded. The purpose is front-loaded and the structured detail follows it logically.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema present, the description carries the return-value burden and does so concretely (count, patterns with name/severity/advice, entropy_detector, allowlist_rules). It also closes the obvious follow-up question about regex exposure, so an agent has everything needed to call and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, which is the baseline-4 case. There is nothing to disambiguate and the description correctly implies a no-argument listing call.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb and resource ('List the active secret detectors and allowlist rules'). This is clearly distinct from the sibling scan_* tools, which perform scanning rather than config introspection, so an agent can route correctly without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The purpose implies this is for discovering what detectors exist, but the description never states when to prefer it (e.g., before a scan to know coverage) or names any alternative. Usage is inferable but not spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

redact_textA
Read-onlyIdempotent

Return the text with every detected secret replaced by [REDACTED:].

Use it before quoting logs, stack traces, configs, command output or diffs that may contain credentials, in the conversation, an issue, a commit message or a document. Everything that is not a secret is kept exactly as it was.

Args: text: The text to clean.

Returns: {"text": the redacted text, "redactions": how many secrets were replaced, "by_pattern": {detector name: count}}

NameRequiredDescriptionDefault
textYes

Output Schema

NameRequiredDescription
textYes
by_patternYes
redactionsYes

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnly, idempotent and non-destructive, so the description focuses on what it adds: everything non-secret is preserved verbatim, and the result includes a redaction count plus a per-detector breakdown. That is meaningful context beyond the structured fields, though nothing is said about failure modes or text size limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The opening two sentences are well front-loaded and earn their place. The trailing Args and Returns blocks largely duplicate the input schema and the existing output schema, which is wasted text for an agent that already receives both.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter pure transformation with full annotations and an output schema, the description covers purpose, usage and preservation semantics. The redundant Returns block is the only real flaw; nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single parameter with 0% schema description coverage, so the description carries the whole burden. 'text: The text to clean' restates the parameter name and adds only that cleaning means secret substitution, which is already implied by the summary. Adequate but thin.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a precise verb and resource — return the text with every detected secret replaced by [REDACTED:<detector>] — and names the exact output format. It implicitly separates itself from the scan_* siblings (detection only) by producing a cleaned copy, though it never names an alternative tool explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete call-site guidance: use it before quoting logs, stack traces, configs, command output or diffs in a conversation, issue, commit message or document. That is clear context for when to reach for it, but there are no exclusions and no pointer to the scan_* tools for the detect-only case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_agent_configsA
Read-onlyIdempotent

Audit AI agent and MCP client configuration files for hardcoded secrets.

Checks the project's .mcp.json, .claude/settings*.json, .cursor/mcp.json, .vscode/mcp.json, .gemini/, .codex/ and other client configs, plus agent instruction files (CLAUDE.md, AGENTS.md, GEMINI.md, .cursorrules...), and the user-level configs of Claude Code, Claude Desktop, Cursor, VS Code, Cline, Windsurf, Gemini CLI, Codex, Kiro, Continue, Zed and others. Project files report whether git tracks them: a key in a tracked .mcp.json ships with the repository.

Args: project_path: Absolute path to the project to audit (optional). include_user: Also scan configs in the user's home folder (default true). include_transcripts: Also scan the newest Claude Code / Codex session transcripts (default false; slower). max_findings: List at most this many findings, most severe first.

Returns: The standard redacted report plus "agent_files": [{"client", "scope", "path", "findings", "tracked_by_git", "hint"}] for every file found.

NameRequiredDescriptionDefault
include_userNo
max_findingsNo
project_pathNo
include_transcriptsNo

Output Schema

NameRequiredDescription
cleanYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
agent_filesYes
files_scannedYes
total_findingsNo
counts_by_patternNo
counts_by_severityNo

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-destructive, so the description adds real context: it reports whether git tracks a project config (a key in a tracked .mcp.json ships with the repo), orders findings by severity, and flags that transcript scanning is slower and off by default. It stops short of saying what the report contains beyond the agent_files array.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded one-line purpose followed by scope, per-parameter notes, and return shape. The client enumeration is long but each item expands coverage meaningfully; no sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, yet the description still names the key return field (agent_files) and its members, and covers all four optional parameters and their defaults. Nothing an agent needs to invoke or interpret this scan correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden and does: project_path is identified as an optional absolute path, include_user and include_transcripts explain defaults and the cost trade-off, and max_findings specifies 'most severe first' ordering. Every parameter gains meaning absent from the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Audit AI agent and MCP client configuration files for hardcoded secrets'. The scope (client configs plus agent instruction files, project and user level) clearly separates it from siblings like scan_file, scan_directory, and scan_text.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context on the files it targets (`.mcp.json`, `.claude/settings*.json`, `.cursorrules`, user-level configs for named clients) and the defaults that shape a run (include_user=true, include_transcripts=false and 'slower'). It never states explicit exclusions or names a sibling alternative such as scan_directory, so routing is implied rather than spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_directoryA
Read-onlyIdempotent

Recursively scan a directory tree for exposed secrets.

Inside a git repository the file list comes from git (tracked files plus untracked files no .gitignore excludes), so every ignore rule is honoured. Always skips .git, node_modules, virtualenvs, caches, dist, build, minified bundles, lockfiles, binaries, files over 5 MB and the paths the project's .secret-sentinel.toml excludes.

Args: path: Absolute path to the directory to scan. max_files: Stop after scanning this many files (default 500). max_findings: List at most this many findings, most severe first (default 200, 0 = no limit).

Returns: The standard redacted findings report; finding paths are relative to the scanned root, using forward slashes.

NameRequiredDescriptionDefault
pathYes
max_filesNo
max_findingsNo

Output Schema

NameRequiredDescription
cleanYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
files_scannedYes
total_findingsNo
counts_by_patternNo
counts_by_severityNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description adds substantive behavior on top: git-derived file enumeration honoring .gitignore, an explicit skip list (.git, node_modules, venvs, caches, dist, build, minified bundles, lockfiles, binaries, >5 MB, .secret-sentinel.toml exclusions). It also discloses truncation semantics and ordering of findings, which an agent cannot infer from annotations alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, then a well-structured Args/Returns block. The skip-list sentence is dense but each item earns its place; only minor trimming would improve it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given three parameters, real filesystem traversal complexity, and an output schema that already defines the report shape, the description covers everything an agent needs: scope, ignore/skip rules, git awareness, truncation limits, and finding-path conventions. Nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry the parameter burden and it does: path is documented as an absolute directory path, max_files as a stop-after cap with default 500, and max_findings as a list cap with default 200 where 0 means unlimited and results are most-severe-first. This meaningfully exceeds the bare typed schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource+scope: 'Recursively scan a directory tree for exposed secrets.' This clearly separates it from siblings like scan_file, scan_text, scan_git_staged, and list_patterns, which an agent can distinguish without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The scope ('recursively ... directory tree', git-aware file listing) makes the intended use case obvious and implicitly routes file/text/git-index scanning to siblings. However, it never explicitly names an alternative or states when not to use it, so it stops short of full routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_fileA
Read-onlyIdempotent

Scan a single file for exposed secrets.

UTF-8, UTF-16 and UTF-32 text is decoded by byte-order mark. Binary files (null-byte heuristic) and files larger than 5 MB are skipped and reported as such in the summary. The project's .secret-sentinel.toml and baseline apply.

Args: path: Absolute path to the file to scan. max_findings: List at most this many findings, most severe first (default 200, 0 = no limit).

Returns: The standard redacted findings report (see scan_text). Returns an error result if the file does not exist.

NameRequiredDescriptionDefault
pathYes
max_findingsNo

Output Schema

NameRequiredDescription
cleanYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
files_scannedYes
total_findingsNo
counts_by_patternNo
counts_by_severityNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses several behaviors beyond what the annotations declare. It states that UTF-8, UTF-16 and UTF-32 text is decoded by its byte-order mark, that binary files and files larger than 5 MB are skipped and reported in the summary, that the .secret-sentinel.toml and baseline settings apply, and that the tool returns an error when the file does not exist. Because the annotations already cover the read-only and idempotent safety profile, this level of added detail is strong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with a one-line summary followed by clearly labelled Args and Returns sections, and every sentence carries information. The Returns section slightly duplicates what the output schema already provides, but the pointer to scan_text for the report format is still useful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read-only scanner, the description covers encoding handling, size and binary skip rules, project-config influence, error conditions, and result ordering, which is everything an agent needs in order to call it correctly. An output schema exists, so the brief note about the redacted report is sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description has to carry the full burden, and it documents both parameters: path as an absolute path, and max_findings with its default of 200, its 0 = no limit semantics, and the ordering (most severe first). This adds meaning the bare schema cannot convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb+resource statement, "Scan a single file for exposed secrets," which tells an agent exactly what this tool does and implicitly separates it from the sibling tools scan_directory and scan_text: it handles exactly one file. The scope of "single file" is narrow enough that the description disambiguates this tool from the directory and text siblings without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase "single file" and the error behavior when the file does not exist show the intended usage context, but the description never explicitly says when to prefer this tool over scan_directory or scan_text, nor does it state any prerequisites. The implied scope is enough to infer the correct choice, but there is no explicit routing rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_git_historyA
Read-onlyIdempotent

Scan the lines added by the most recent commits (git log -p).

Each finding is tagged with the short hash of the commit that introduced it. A secret that was later deleted is still reported — history retains it, so the credential must be rotated regardless.

Args: repo_path: Absolute path to a git repository (or any path inside one). max_commits: How many commits back to inspect (default 50). all_branches: Walk every branch, tag and the stash instead of only the history of HEAD (default false). max_findings: List at most this many findings, most severe first (default 200, 0 = no limit).

Returns: The standard redacted findings report, with a "commit" field on each finding.

NameRequiredDescriptionDefault
repo_pathYes
max_commitsNo
all_branchesNo
max_findingsNo

Output Schema

NameRequiredDescription
cleanYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
files_scannedYes
total_findingsNo
counts_by_patternNo
counts_by_severityNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare this a read-only, idempotent, non-destructive, closed-world operation, so the safety bar is low. The description still adds real behavioral value beyond them: findings carry the short commit hash, and a secret deleted in a later commit is still reported because history retains it, which tells the agent that results imply mandatory rotation rather than a false positive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first line, followed by the most decision-relevant caveat (deleted secrets are still reported), then parameters and returns. The structure is clean and every section earns its space; the Returns block is slightly redundant given an output schema exists, though it does flag the added commit field.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With four parameters at 0% schema coverage and an output schema present, the description covers everything needed to invoke the tool correctly, including defaults and the 0-means-unlimited edge case. The one gap is routing: with eight siblings including two other git scanners, it never states how results or scope differ from scan_git_range or scan_git_staged.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry all four parameters — and it does, documenting repo_path as an absolute path (or any path inside one), max_commits' default of 50, all_branches' inclusion of branches/tags/stash versus HEAD-only, and max_findings' default 200 with the non-obvious '0 = no limit' and severity ordering.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line gives a concrete verb and resource (scan the lines added by recent commits, git log -p), which is unambiguous on its own. It does not, however, name or contrast itself with the obvious siblings scan_git_staged and scan_git_range, leaving the boundary between those three git scanners to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied: the description explains what history scope is scanned but never states when to prefer this over scan_git_range (a specific commit span) or scan_git_staged. The all_branches note hints at the scope trade-off, but no explicit when/when-not guidance or alternative is offered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_git_rangeA
Read-onlyIdempotent

Scan the commits in base..head: by default, what git push would send.

Run this before pushing. With the defaults it scans every commit on the current branch that its upstream does not have yet. If the branch has no upstream, the error says so: pass the branch you will push to as base (for example "origin/main").

Args: repo_path: Absolute path to a git repository (or any path inside one). base: Commits reachable from here are excluded (default "@{upstream}"). head: Last commit to include (default "HEAD"). max_commits: Scan at most this many of the newest commits in the range. max_findings: List at most this many findings, most severe first (default 200, 0 = no limit).

Returns: The standard redacted findings report with a "commit" field on each finding, plus "commits_scanned" and "range".

NameRequiredDescriptionDefault
baseNo@{upstream}
headNoHEAD
repo_pathYes
max_commitsNo
max_findingsNo

Output Schema

NameRequiredDescription
cleanYes
rangeYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
files_scannedYes
total_findingsNo
commits_scannedYes
counts_by_patternNo
counts_by_severityNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive/closed-world, so the safety profile is covered; the description adds real value beyond that with default range resolution (@{upstream}..HEAD), the error behavior when no upstream exists, and finding ordering ('most severe first'). It does not mention scan cost or performance on large ranges, which is the main remaining unknown for a read-only scan.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The lead sentence is front-loaded and high-signal, and the Args entries are tight. The Returns block is somewhat redundant given an output schema exists, and the docstring-style layout adds length without adding selection value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 5-parameter read-only git scanner with an output schema, everything an agent needs to invoke it correctly is present: what the range means, defaults, the upstream failure path, limits, and result ordering. No material gaps remain.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry the load and it does: the Args block defines each parameter's meaning, defaults, and edge semantics ('0 = no limit', base excludes reachable commits, head is the last commit included, repo_path may be any path inside a repo). This is materially more than the bare titles/defaults in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence gives a precise verb+resource and defines the range semantics ('Scan the commits in base..head'), with the default identified as 'what `git push` would send'. This clearly separates it from scan_git_history (all history) and scan_git_staged (index-only) without needing the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Run this before pushing' gives an explicit trigger condition, and the description explains the no-upstream failure mode plus the workaround (pass the branch you will push to as base, e.g. "origin/main"). It stops short of naming sibling alternatives such as scan_git_staged or scan_git_history when the pre-push range is not the right scope.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_git_stagedA
Read-onlyIdempotent

Scan ONLY the lines currently staged for commit (git diff --cached).

This is the pre-commit checkpoint: it inspects exactly the content the next commit would publish, and reports the file and post-commit line number of every added secret. Local diff settings (external diff tools, textconv filters, prefixes, path quoting) are overridden, and git runs no configured programs.

Args: repo_path: Absolute path to a git repository (or any path inside one). max_findings: List at most this many findings, most severe first (default 200, 0 = no limit).

Returns: The standard redacted findings report. If nothing is staged the result is clean with an explanatory summary.

NameRequiredDescriptionDefault
repo_pathYes
max_findingsNo

Output Schema

NameRequiredDescription
cleanYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
files_scannedYes
total_findingsNo
counts_by_patternNo
counts_by_severityNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnly, idempotent, non-destructive, closed-world), it discloses that local diff settings — external diff tools, textconv filters, prefixes, path quoting — are overridden and that git runs no configured programs, which is meaningful safety context for a scanner executing inside a repo. It also explains the empty-staging behavior (clean result with explanatory summary), which annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the single most important constraint ('Scan ONLY the lines currently staged'), then structured Args/Returns sections. Slightly verbose in the diff-override sentence, but every clause adds real behavioral information given the empty schema descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 2-parameter read-only scanner with an output schema, everything needed is present: scope, path semantics, result capping, edge-case behavior, and the internal git invocation guarantees. No gap an agent would need to guess at.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must carry the load — and it does: repo_path is defined as an absolute path to a repo or any path inside one, and max_findings is explained as severity-ordered truncation with default 200 and the non-obvious semantics that 0 means no limit (a detail absent from the schema).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (scan) and a precisely bounded resource: only lines currently staged, i.e. git diff --cached. The scope clause plus the explicit 'post-commit line number' detail makes it distinguishable from siblings like scan_git_history, scan_git_range, and scan_file.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Frames the tool as 'the pre-commit checkpoint' and states that it inspects exactly what the next commit would publish, which gives an agent a clear when-to-use condition. It does not explicitly name the alternative tools (e.g. scan_git_range for unpushed commits) or state when not to use it, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

scan_textA
Read-onlyIdempotent

Scan a snippet of text or code for exposed secrets.

64 detectors: GitHub/GitLab/npm/PyPI tokens; OpenAI, Anthropic, xAI, DeepSeek, Perplexity, OpenRouter, Groq, NVIDIA, Hugging Face, Replicate, Together, Cerebras, LangSmith, Langfuse, Pinecone, Firecrawl and Tavily keys; AWS (incl. Bedrock), Azure, Google, DigitalOcean, Cloudflare, Vercel, Supabase, Neon, PlanetScale, Fly.io, Databricks, Vault and Doppler credentials; Stripe, Square, Shopify, SendGrid, Resend, Mailchimp, Twilio, Slack, Discord, Telegram, Linear, Notion, Atlassian, Postman, Sentry, Grafana and PostHog tokens; JWTs, private and age keys, credentialed connection strings and URLs, generic password/secret/token assignments, plus high-entropy strings. Placeholders ($VAR, ${VAR}, {{ templates }}, changeme, masked values...) are skipped, and a line carrying "secret-sentinel: ignore", "pragma: allowlist secret" or "gitleaks:allow" is counted in "suppressed" instead of reported.

Args: text: The raw text to scan (code, config, diff output, logs...). source_name: Label used in each finding's "file" field. max_findings: List at most this many findings, most severe first (default 200, 0 = no limit). When capped, the result adds truncated, total_findings, counts_by_severity, counts_by_pattern and top_files.

Returns: {"clean", "findings": [{"file", "line", "pattern", "severity", "redacted", "advice"}], "files_scanned", "summary"}. Secret values are ALWAYS redacted (at most 4 chars, never more than a quarter of the value, + length); the full value is never included.

NameRequiredDescriptionDefault
textYes
source_nameNoinput
max_findingsNo

Output Schema

NameRequiredDescription
cleanYes
summaryYes
findingsYes
baselinedNo
top_filesNo
truncatedNo
suppressedNo
files_scannedYes
total_findingsNo
counts_by_patternNo
counts_by_severityNo

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description discloses important behavior: placeholder handling, suppression-comment semantics, redaction guarantees, and max_findings truncation effects including added result fields. This is substantial operational context that helps an agent invoke and interpret the tool correctly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded, but the long enumeration of dozens of provider and service detectors is verbose for a tool description. That list is informative but not essential for correct invocation, and it makes the definition less concise than it could be.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, the description is complete: it covers what is scanned, how findings are suppressed, how redaction works, parameter behavior, and expected return fields. The presence of an output schema reduces the burden, but the added behavioral detail makes the definition self-sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully compensates by documenting all three parameters: text as raw input, source_name as the finding label, and max_findings with its default, cap behavior, and special 0 value. It also explains the extra result fields produced when truncation occurs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Scan a snippet of text or code for exposed secrets.' It clearly distinguishes this tool from file/directory/git scanners by emphasizing raw text snippets, so an agent can tell what it does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear usage context by naming raw text inputs such as code, config, diff output, and logs, and by marking the tool as snippet-oriented. It does not explicitly name sibling alternatives like scan_file or redact_text, so it lacks full when-to-use/when-not-to-use routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv0.3.0
    • Addedredact_text
    • Addedscan_agent_configs
    • Changedscan_directory1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "FileCount": {
        +      "properties": {
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "findings": {
        +          "title": "Findings",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "findings"
        +      ],
        +      "title": "FileCount",
        +      "type": "object"
        +    },
        +    "Finding": {
        +      "properties": {
        +        "advice": {
        +          "title": "Advice",
        +          "type": "string"
        +        },
        +        "commit": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "title": "Commit"
        +        },
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "line": {
        +          "title": "Line",
        +          "type": "integer"
        +        },
        +        "pattern": {
        +          "title": "Pattern",
        +          "type": "string"
        +        },
        +        "redacted": {
        +          "title": "Redacted",
        +          "type": "string"
        +        },
        +        "severity": {
        +          "enum": [
        +            "critical",
        +            "high",
        +            "medium"
        +          ],
        +          "title": "Severity",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "line",
        +        "pattern",
        +        "severity",
        +        "redacted",
        +        "advice"
        +      ],
        +      "title": "Finding",
        +      "type": "object"
        +    }
        +  },
        +  "properties": {
        +    "baselined": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Baselined"
        +    },
        +    "clean": {
        +      "title": "Clean",
        +      "type": "boolean"
        +    },
        +    "counts_by_pattern": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Pattern"
        +    },
        +    "counts_by_severity": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Severity"
        +    },
        +    "files_scanned": {
        +      "title": "Files Scanned",
        +      "type": "integer"
        +    },
        +    "findings": {
        +      "items": {
        +        "$ref": "#/$defs/Finding"
        +      },
        +      "title": "Findings",
        +      "type": "array"
        +    },
        +    "summary": {
        +      "title": "Summary",
        +      "type": "string"
        +    },
        +    "suppressed": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Suppressed"
        +    },
        +    "top_files": {
        +      "anyOf": [
        +        {
        +          "items": {
        +            "$ref": "#/$defs/FileCount"
        +          },
        +          "type": "array"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Top Files"
        +    },
        +    "total_findings": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Total Findings"
        +    },
        +    "truncated": {
        +      "anyOf": [
        +        {
        +          "type": "boolean"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Truncated"
        +    }
        +  },
        +  "required": [
        +    "clean",
        +    "findings",
        +    "files_scanned",
        +    "summary"
        +  ],
        +  "title": "ScanReport",
        +  "type": "object"
        +}
    • Changedscan_file1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "FileCount": {
        +      "properties": {
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "findings": {
        +          "title": "Findings",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "findings"
        +      ],
        +      "title": "FileCount",
        +      "type": "object"
        +    },
        +    "Finding": {
        +      "properties": {
        +        "advice": {
        +          "title": "Advice",
        +          "type": "string"
        +        },
        +        "commit": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "title": "Commit"
        +        },
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "line": {
        +          "title": "Line",
        +          "type": "integer"
        +        },
        +        "pattern": {
        +          "title": "Pattern",
        +          "type": "string"
        +        },
        +        "redacted": {
        +          "title": "Redacted",
        +          "type": "string"
        +        },
        +        "severity": {
        +          "enum": [
        +            "critical",
        +            "high",
        +            "medium"
        +          ],
        +          "title": "Severity",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "line",
        +        "pattern",
        +        "severity",
        +        "redacted",
        +        "advice"
        +      ],
        +      "title": "Finding",
        +      "type": "object"
        +    }
        +  },
        +  "properties": {
        +    "baselined": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Baselined"
        +    },
        +    "clean": {
        +      "title": "Clean",
        +      "type": "boolean"
        +    },
        +    "counts_by_pattern": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Pattern"
        +    },
        +    "counts_by_severity": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Severity"
        +    },
        +    "files_scanned": {
        +      "title": "Files Scanned",
        +      "type": "integer"
        +    },
        +    "findings": {
        +      "items": {
        +        "$ref": "#/$defs/Finding"
        +      },
        +      "title": "Findings",
        +      "type": "array"
        +    },
        +    "summary": {
        +      "title": "Summary",
        +      "type": "string"
        +    },
        +    "suppressed": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Suppressed"
        +    },
        +    "top_files": {
        +      "anyOf": [
        +        {
        +          "items": {
        +            "$ref": "#/$defs/FileCount"
        +          },
        +          "type": "array"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Top Files"
        +    },
        +    "total_findings": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Total Findings"
        +    },
        +    "truncated": {
        +      "anyOf": [
        +        {
        +          "type": "boolean"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Truncated"
        +    }
        +  },
        +  "required": [
        +    "clean",
        +    "findings",
        +    "files_scanned",
        +    "summary"
        +  ],
        +  "title": "ScanReport",
        +  "type": "object"
        +}
    • Changedscan_git_history1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "FileCount": {
        +      "properties": {
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "findings": {
        +          "title": "Findings",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "findings"
        +      ],
        +      "title": "FileCount",
        +      "type": "object"
        +    },
        +    "Finding": {
        +      "properties": {
        +        "advice": {
        +          "title": "Advice",
        +          "type": "string"
        +        },
        +        "commit": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "title": "Commit"
        +        },
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "line": {
        +          "title": "Line",
        +          "type": "integer"
        +        },
        +        "pattern": {
        +          "title": "Pattern",
        +          "type": "string"
        +        },
        +        "redacted": {
        +          "title": "Redacted",
        +          "type": "string"
        +        },
        +        "severity": {
        +          "enum": [
        +            "critical",
        +            "high",
        +            "medium"
        +          ],
        +          "title": "Severity",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "line",
        +        "pattern",
        +        "severity",
        +        "redacted",
        +        "advice"
        +      ],
        +      "title": "Finding",
        +      "type": "object"
        +    }
        +  },
        +  "properties": {
        +    "baselined": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Baselined"
        +    },
        +    "clean": {
        +      "title": "Clean",
        +      "type": "boolean"
        +    },
        +    "counts_by_pattern": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Pattern"
        +    },
        +    "counts_by_severity": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Severity"
        +    },
        +    "files_scanned": {
        +      "title": "Files Scanned",
        +      "type": "integer"
        +    },
        +    "findings": {
        +      "items": {
        +        "$ref": "#/$defs/Finding"
        +      },
        +      "title": "Findings",
        +      "type": "array"
        +    },
        +    "summary": {
        +      "title": "Summary",
        +      "type": "string"
        +    },
        +    "suppressed": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Suppressed"
        +    },
        +    "top_files": {
        +      "anyOf": [
        +        {
        +          "items": {
        +            "$ref": "#/$defs/FileCount"
        +          },
        +          "type": "array"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Top Files"
        +    },
        +    "total_findings": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Total Findings"
        +    },
        +    "truncated": {
        +      "anyOf": [
        +        {
        +          "type": "boolean"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Truncated"
        +    }
        +  },
        +  "required": [
        +    "clean",
        +    "findings",
        +    "files_scanned",
        +    "summary"
        +  ],
        +  "title": "ScanReport",
        +  "type": "object"
        +}
    • Changedscan_git_range1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "FileCount": {
        +      "properties": {
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "findings": {
        +          "title": "Findings",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "findings"
        +      ],
        +      "title": "FileCount",
        +      "type": "object"
        +    },
        +    "Finding": {
        +      "properties": {
        +        "advice": {
        +          "title": "Advice",
        +          "type": "string"
        +        },
        +        "commit": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "title": "Commit"
        +        },
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "line": {
        +          "title": "Line",
        +          "type": "integer"
        +        },
        +        "pattern": {
        +          "title": "Pattern",
        +          "type": "string"
        +        },
        +        "redacted": {
        +          "title": "Redacted",
        +          "type": "string"
        +        },
        +        "severity": {
        +          "enum": [
        +            "critical",
        +            "high",
        +            "medium"
        +          ],
        +          "title": "Severity",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "line",
        +        "pattern",
        +        "severity",
        +        "redacted",
        +        "advice"
        +      ],
        +      "title": "Finding",
        +      "type": "object"
        +    }
        +  },
        +  "properties": {
        +    "baselined": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Baselined"
        +    },
        +    "clean": {
        +      "title": "Clean",
        +      "type": "boolean"
        +    },
        +    "commits_scanned": {
        +      "title": "Commits Scanned",
        +      "type": "integer"
        +    },
        +    "counts_by_pattern": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Pattern"
        +    },
        +    "counts_by_severity": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Severity"
        +    },
        +    "files_scanned": {
        +      "title": "Files Scanned",
        +      "type": "integer"
        +    },
        +    "findings": {
        +      "items": {
        +        "$ref": "#/$defs/Finding"
        +      },
        +      "title": "Findings",
        +      "type": "array"
        +    },
        +    "range": {
        +      "title": "Range",
        +      "type": "string"
        +    },
        +    "summary": {
        +      "title": "Summary",
        +      "type": "string"
        +    },
        +    "suppressed": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Suppressed"
        +    },
        +    "top_files": {
        +      "anyOf": [
        +        {
        +          "items": {
        +            "$ref": "#/$defs/FileCount"
        +          },
        +          "type": "array"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Top Files"
        +    },
        +    "total_findings": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Total Findings"
        +    },
        +    "truncated": {
        +      "anyOf": [
        +        {
        +          "type": "boolean"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Truncated"
        +    }
        +  },
        +  "required": [
        +    "clean",
        +    "findings",
        +    "files_scanned",
        +    "summary",
        +    "commits_scanned",
        +    "range"
        +  ],
        +  "title": "RangeReport",
        +  "type": "object"
        +}
    • Changedscan_git_staged1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "FileCount": {
        +      "properties": {
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "findings": {
        +          "title": "Findings",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "findings"
        +      ],
        +      "title": "FileCount",
        +      "type": "object"
        +    },
        +    "Finding": {
        +      "properties": {
        +        "advice": {
        +          "title": "Advice",
        +          "type": "string"
        +        },
        +        "commit": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "title": "Commit"
        +        },
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "line": {
        +          "title": "Line",
        +          "type": "integer"
        +        },
        +        "pattern": {
        +          "title": "Pattern",
        +          "type": "string"
        +        },
        +        "redacted": {
        +          "title": "Redacted",
        +          "type": "string"
        +        },
        +        "severity": {
        +          "enum": [
        +            "critical",
        +            "high",
        +            "medium"
        +          ],
        +          "title": "Severity",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "line",
        +        "pattern",
        +        "severity",
        +        "redacted",
        +        "advice"
        +      ],
        +      "title": "Finding",
        +      "type": "object"
        +    }
        +  },
        +  "properties": {
        +    "baselined": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Baselined"
        +    },
        +    "clean": {
        +      "title": "Clean",
        +      "type": "boolean"
        +    },
        +    "counts_by_pattern": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Pattern"
        +    },
        +    "counts_by_severity": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Severity"
        +    },
        +    "files_scanned": {
        +      "title": "Files Scanned",
        +      "type": "integer"
        +    },
        +    "findings": {
        +      "items": {
        +        "$ref": "#/$defs/Finding"
        +      },
        +      "title": "Findings",
        +      "type": "array"
        +    },
        +    "summary": {
        +      "title": "Summary",
        +      "type": "string"
        +    },
        +    "suppressed": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Suppressed"
        +    },
        +    "top_files": {
        +      "anyOf": [
        +        {
        +          "items": {
        +            "$ref": "#/$defs/FileCount"
        +          },
        +          "type": "array"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Top Files"
        +    },
        +    "total_findings": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Total Findings"
        +    },
        +    "truncated": {
        +      "anyOf": [
        +        {
        +          "type": "boolean"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Truncated"
        +    }
        +  },
        +  "required": [
        +    "clean",
        +    "findings",
        +    "files_scanned",
        +    "summary"
        +  ],
        +  "title": "ScanReport",
        +  "type": "object"
        +}
    • Changedscan_text1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$defs": {
        +    "FileCount": {
        +      "properties": {
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "findings": {
        +          "title": "Findings",
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "findings"
        +      ],
        +      "title": "FileCount",
        +      "type": "object"
        +    },
        +    "Finding": {
        +      "properties": {
        +        "advice": {
        +          "title": "Advice",
        +          "type": "string"
        +        },
        +        "commit": {
        +          "anyOf": [
        +            {
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "title": "Commit"
        +        },
        +        "file": {
        +          "title": "File",
        +          "type": "string"
        +        },
        +        "line": {
        +          "title": "Line",
        +          "type": "integer"
        +        },
        +        "pattern": {
        +          "title": "Pattern",
        +          "type": "string"
        +        },
        +        "redacted": {
        +          "title": "Redacted",
        +          "type": "string"
        +        },
        +        "severity": {
        +          "enum": [
        +            "critical",
        +            "high",
        +            "medium"
        +          ],
        +          "title": "Severity",
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "file",
        +        "line",
        +        "pattern",
        +        "severity",
        +        "redacted",
        +        "advice"
        +      ],
        +      "title": "Finding",
        +      "type": "object"
        +    }
        +  },
        +  "properties": {
        +    "baselined": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Baselined"
        +    },
        +    "clean": {
        +      "title": "Clean",
        +      "type": "boolean"
        +    },
        +    "counts_by_pattern": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Pattern"
        +    },
        +    "counts_by_severity": {
        +      "anyOf": [
        +        {
        +          "additionalProperties": {
        +            "type": "integer"
        +          },
        +          "type": "object"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Counts By Severity"
        +    },
        +    "files_scanned": {
        +      "title": "Files Scanned",
        +      "type": "integer"
        +    },
        +    "findings": {
        +      "items": {
        +        "$ref": "#/$defs/Finding"
        +      },
        +      "title": "Findings",
        +      "type": "array"
        +    },
        +    "summary": {
        +      "title": "Summary",
        +      "type": "string"
        +    },
        +    "suppressed": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Suppressed"
        +    },
        +    "top_files": {
        +      "anyOf": [
        +        {
        +          "items": {
        +            "$ref": "#/$defs/FileCount"
        +          },
        +          "type": "array"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Top Files"
        +    },
        +    "total_findings": {
        +      "anyOf": [
        +        {
        +          "type": "integer"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Total Findings"
        +    },
        +    "truncated": {
        +      "anyOf": [
        +        {
        +          "type": "boolean"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "title": "Truncated"
        +    }
        +  },
        +  "required": [
        +    "clean",
        +    "findings",
        +    "files_scanned",
        +    "summary"
        +  ],
        +  "title": "ScanReport",
        +  "type": "object"
        +}
  2. 7 tool updatesv0.2.0
    • First observedlist_patterns
    • First observedscan_directory
    • First observedscan_file
    • First observedscan_git_history
    • First observedscan_git_range
    • First observedscan_git_staged
    • First observedscan_text

TDQS

A4.4/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a clearly distinct scope: listing patterns, scanning text/files/directories, git staged/history/range scanning, agent config auditing, and redaction. The multiple scan_* tools are differentiated by explicit target (text, file, directory, git staged, git history, git range, agent configs) rather than overlapping behavior. An agent can reliably choose the right tool from names and descriptions.

Naming Consistency5/5

All tool names use consistent snake_case with a predictable verb_noun pattern (list_patterns, scan_text, redact_text, scan_file, scan_directory, scan_git_staged, scan_git_history, scan_git_range, scan_agent_configs). The scan_ prefix groups related scanning operations, while list_ and redact_ are used appropriately. There is no mixing of camelCase or inconsistent verb styles.

Tool Count5/5

Nine tools is well scoped for a secret-scanning server. The set covers distinct scanning targets and a redaction utility without excessive fragmentation. Each tool has a clear purpose and earns its place.

Completeness4/5

The surface covers the core secret-detection workflow comprehensively: pattern listing, text/file/directory scanning, git staged/history/range scanning, agent config auditing, and redaction. Minor gaps exist around managing custom patterns, allowlist rules, or baselines, but these are handled by project configuration files and are not critical for the scanning lifecycle.

Maintenance

ActivityNo data
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    Not graded
    maintenance
    Enterprise-grade MCP (Model Context Protocol) server for detecting secrets and sensitive information in GitHub repositories. Scans for 35+ types of secrets including API keys, passwords, tokens, and credentials with production-ready reliability features.
    5
    0
    -
  • A
    license
    A
    quality
    C
    maintenance
    MCP server for detecting publisher telemetry pipeline leaks in binary artifacts by scanning for sensitive strings like Sentry DSNs, Slack tokens, and AWS keys.
    6
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    An MCP server for autonomous AI agents to scan and detect hardcoded secrets, API keys, and passwords in source code files.
    1
    MIT