Skip to content

fix(mcp): reject unknown module and category names instead of scanning nothing - #735

Merged
kaifcodec merged 1 commit into
kaifcodec:mainfrom
VamikaSinghal:fix/mcp-unknown-scope-error
Oct 23, 2026
Merged

kaifcodec merged 1 commit into
kaifcodec:mainfrom
VamikaSinghal:fix/mcp-unknown-scope-error

Conversation

@VamikaSinghal

Copy link
Copy Markdown
Contributor

Summary

Closes #727.

scan_username and scan_email accepted any module or category value. When the name matched nothing, _run_scan fell through with return [] and the tool returned a normal envelope with total_scanned: 0, so a typo or a stale agent plan looked exactly like a scan with no findings.

This PR validates the scope up front and reports a structured error instead:

  • _validate_scope() runs in execute_scan before the scan lock is taken, so a rejected name never touches proxies, timeouts or concurrency (same place the existing concurrency check lives).
  • The error names the rejected value, suggests the closest valid names (difflib.get_close_matches, a different-case spelling always first) and points at list_available_modules. The MCP server surfaces it as a tool error (isError: true), the same way Missing 'username' argument and the concurrency errors are reported today.
  • _run_scan raises the same error instead of return [], so no silent path remains on the thread side either.
  • Tool schema descriptions and the README MCP section mention the new behaviour.

Behaviour kept on purpose (matches the CLI):

  • Module names resolve case-insensitively, like -m (find_module lowercases both sides), so "GitHub" still scans github.
  • Category names are exact, like -c, so "Social" is rejected with 'social' as the first suggestion.
  • A valid category with no modules in it would still return an empty envelope: the scope is known, so the result stays distinguishable from an unknown name.

One existing test (test_execute_scan_module_normalization) used "Made.porn" with is_email=False; that module only exists under email_scan, so with validation it is now an unknown username module. The test keeps its purpose (dot to underscore normalisation) and scans as email instead.

Before / after

Real MCP server over stdio (python -m user_scanner.mcp.server), driven with the mcp client library:

# main
scan_username({"username": "vamikasinghal", "module": "githb"}) -> isError=False
    { "summary": { "total_scanned": 0, "found": 0, "not_found": 0, "errors": 0, "skipped": 0 }, "results": [] }
scan_username({"username": "vamikasinghal", "category": "Social"}) -> isError=False
    { "summary": { "total_scanned": 0, ... }, "results": [] }
scan_email({"email": "someone@example.com", "category": "gamming"}) -> isError=False
    { "summary": { "total_scanned": 0, ... }, "results": [] }

# this branch
scan_username({"username": "vamikasinghal", "module": "githb"}) -> isError=True
    Unknown username module 'githb'. Did you mean: 'github', 'giveth', 'gitlab'? Use list_available_modules to see the valid choices.
scan_username({"username": "vamikasinghal", "category": "Social"}) -> isError=True
    Unknown username category 'Social'. Did you mean: 'social'? Use list_available_modules to see the valid choices.
scan_email({"email": "someone@example.com", "category": "gamming"}) -> isError=True
    Unknown email category 'gamming'. Did you mean: 'gaming', 'dating'? Use list_available_modules to see the valid choices.

Tests

New tests in tests/test_mcp_handlers.py, following the existing patterns there:

  • unknown module (username and email): explicit ValueError, suggestion present or absent as expected, _scan_lock not left held
  • unknown category: different-case Social rejected with 'social' first; gamming suggests gaming
  • adult is rejected when no_nsfw excludes it
  • an unknown scope never calls set_proxy_manager or _run_scan
  • known scopes still run (GitHub case variant, social, email module)
  • _run_scan itself raises for an unknown module or category
$ pytest -q
433 passed, 4 skipped in 5.60s        # main: 427 passed, 4 skipped
$ ruff check .
All checks passed!
$ mypy user_scanner
Success: no issues found in 2809 source files

AI disclosure

This change was written with Claude Code (Anthropic) assisting, per the AI contributor guide in AGENTS.md. I reviewed the diff, ran the gates above and drove the real server over stdio for the before/after output.

🤖 Generated with Claude Code

…g nothing

A `module` or `category` value that matched nothing made `scan_username`
and `scan_email` return a normal envelope with `total_scanned: 0`, so a
typo or a stale agent plan was indistinguishable from a scan with no
findings. `_run_scan` fell through with `return []` for both cases.

Validate the scope in `execute_scan` before the scan lock is taken and
raise a ValueError naming the rejected value, suggesting the closest valid
names (a different-case spelling first) and pointing at
list_available_modules. The MCP server surfaces this as a tool error, the
same way the existing argument errors are reported. `_run_scan` raises the
same error instead of returning [] so no silent path remains.

Module names keep resolving case-insensitively, like the CLI's -m flag;
category names stay exact, like the CLI's -c flag, so 'Social' is rejected
with 'social' as the first suggestion.

Closes kaifcodec#727

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kaifcodec kaifcodec left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@VamikaSinghal,
Thank you for the clean fix and thorough test coverage! Everything looks great and all checks are green.

Feel free to check out any other open issues or submit more PRs if you're interested. Thanks again!

@kaifcodec kaifcodec added the bug fix Fixed a bug in existing files label Oct 23, 2026
@kaifcodec
kaifcodec merged commit 631827c into kaifcodec:main Oct 23, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug fix Fixed a bug in existing files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Audit] Medium — MCP silently treats unknown modules/categories as successful empty scans

2 participants