How MCP Least Privilege Detection Works in NVIDIA SkillSpector

MCP least privilege detection works by comparing declared permissions in a skill's SKILL.md manifest against actual capabilities extracted from source code using regex patterns, emitting four rule-based findings (LP1-LP4) that identify under-declared, over-declared, wildcard, or missing permissions.

The MCP least privilege detection mechanism in NVIDIA SkillSpector ensures that Model Context Protocol (MCP) skills operate with minimal required permissions. Implemented as the mcp_least_privilege analyzer node, this static analysis tool bridges the gap between manifest declarations and real code behavior without requiring LLM calls.

How the MCP Least Privilege Analyzer Works

The analyzer operates as a stateless pipeline node that consumes pre-built context from upstream components. It systematically cross-references declared permissions against extracted code capabilities to identify security gaps.

Required State and Inputs

The analyzer requires three specific state keys supplied by earlier pipeline nodes (such as build_context):

  • manifest: Parsed front-matter of SKILL.md containing the permissions list
  • file_cache: Dictionary mapping file paths to their string contents
  • component_metadata: List of dictionaries with per-file metadata including path and executable flags

These inputs are read directly in src/skillspector/nodes/analyzers/mcp_least_privilege.py with no LLM inference performed during analysis.

Capability Detection via Regex Patterns

The _detect_capabilities(content) function scans executable files once using regular expressions to identify six capability categories:

  • shell: subprocess, Popen, curl, chmod
  • network: httpx, requests, urllib, socket\.connect
  • file_read: open(..., "r"), .read_text(), os\.listdir
  • file_write: open(..., "w"), .write_text(), shutil\.copy
  • env: os\.environ, os\.getenv, process\.env
  • mcp: create_session, MCPClient, mcp\.client

Test files (matching test_*.py or *_test.py) are tracked separately. Capabilities appearing only in test files receive a confidence score of 0.55 versus 0.75 for production code.

Permission-to-Category Mapping

Declared permissions are normalized to lowercase and mapped to capability categories via _map_permissions_to_categories(permissions). The internal _PERM_TO_CAPABILITY table maps keywords like bash, shell, or terminal to the shell category, while network, http, or fetch map to network.

Wildcard entries ("*", "all", "full", "any") are detected by _has_wildcard() in src/skillspector/nodes/analyzers/mcp_least_privilege.py and trigger the LP2 rule.

The Four Detection Rules (LP1-LP4)

The node(state) function implements four distinct findings through systematic evaluation:

  1. LP1 – Underdeclared Capability: Emitted when code contains capabilities not covered by declared permissions. Confidence is 0.75 for production code or 0.55 if the capability appears only in test files.

  2. LP2 – Wildcard Permission: Triggered when any declared permission is a wildcard entry. This indicates potential over-privilege.

  3. LP3 – Missing Permission Declaration: Fired when no permissions are declared in the manifest but capabilities are detected in the code.

  4. LP4 – Overdeclared Permission: Emitted when a declared permission maps to a capability completely absent from the source code. Fixed confidence of 0.65.

Rule Interaction and Suppression Logic

The analyzer implements suppression logic to prevent redundant findings according to the NVIDIA SkillSpector design specification:

  • Wildcard + under-declared capabilities: LP2 is emitted; LP1 is suppressed
  • No permissions declared + capabilities detected: LP3 is emitted; LP1 and LP4 are suppressed
  • Normal permission list with gaps: LP1 and/or LP4 are emitted individually
  • Documentation-only skills: No analysis performed; no findings emitted

This interaction matrix is documented in docs/B.3.1-mcp-least-privilege.md.

Implementation Examples in SkillSpector

To invoke the analyzer directly on a minimal state:

from skillspector.nodes.analyzers import mcp_least_privilege

state = {
    "manifest": {"permissions": ["read"], "triggers": []},
    "file_cache": {"script.py": "import httpx\nhttpx.get('https://example.com')"},
    "component_metadata": [
        {"path": "script.py", "type": "python", "executable": True, "lines": 2, "size_bytes": 50}
    ],
    "has_executable_scripts": True,
    "components": ["script.py"],
}

result = mcp_least_privilege.node(state)
for finding in result["findings"]:
    print(f"{finding.rule_id}: {finding.message}")

To detect wildcard permissions (LP2):

state = {
    "manifest": {"permissions": ["*"]},
    "file_cache": {"agent.py": "print('hello')"},
    "component_metadata": [{"path": "agent.py", "executable": True}],
    "has_executable_scripts": True,
    "components": ["agent.py"],
}

findings = mcp_least_privilege.node(state)["findings"]
lp2 = next(f for f in findings if f.rule_id == "LP2")
print(lp2.message)  # Output: Permission list contains a wildcard entry...

Summary

  • MCP least privilege detection compares SKILL.md manifests against actual code capabilities using static regex analysis in src/skillspector/nodes/analyzers/mcp_least_privilege.py
  • The analyzer categorizes code into six capability types: shell, network, file_read, file_write, env, and mcp
  • Four rules (LP1-LP4) identify under-declared, wildcard, missing, and over-declared permissions with specific confidence scoring
  • Test-only capabilities receive reduced confidence scores (0.55 vs 0.75) to reflect lower risk profiles
  • Rule suppression logic prevents redundant findings when wildcards or missing declarations are present

Frequently Asked Questions

What triggers an LP1 finding in MCP least privilege detection?

An LP1 (Underdeclared Capability) finding is triggered when _detect_capabilities() identifies a capability category in executable code that is not covered by the corresponding permission declaration in the manifest. The finding receives a confidence score of 0.75 for capabilities found in production code, or 0.55 if the capability appears only in test files.

How does the analyzer handle test files differently?

The analyzer tracks capabilities found in files matching test_*.py or *_test.py patterns separately from production code. When a capability appears exclusively in test files, the confidence score for LP1 findings is reduced to 0.55 compared to 0.75 for normal files, reflecting the lower risk of test-only code paths.

What is the difference between LP2 and LP3 findings?

LP2 (Wildcard Permission) is emitted when the manifest contains wildcard entries like "*", "all", "full", or "any", indicating potentially over-privileged declarations. LP3 (Missing Permission Declaration) is emitted when the manifest contains no permissions at all but capabilities are detected in the code. When LP3 is triggered, LP1 and LP4 findings are suppressed to avoid redundancy.

Where is the confidence scoring logic defined?

The confidence scoring logic is implemented in src/skillspector/nodes/analyzers/mcp_least_privilege.py within the node() function. The analyzer assigns 0.75 confidence to LP1 findings in normal files, 0.55 to LP1 findings for test-only capabilities, and 0.65 to LP4 (Overdeclared Permission) findings. These values are hardcoded constants in the rule evaluation logic.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →