MCP Least Privilege Violations in SkillSpector: Underdeclared and Overdeclared Capabilities Explained
SkillSpector detects MCP least privilege violations by comparing declared permissions in SKILL.md against actual capabilities extracted from code, flagging underdeclared capabilities (LP1), missing permission blocks (LP3), and overdeclared permissions (LP4).
NVIDIA/SkillSpector implements a specialized analyzer that enforces the Model Context Protocol (MCP) least privilege principle by ensuring skill manifests accurately reflect code requirements. The analyzer scans SKILL.md files and executable code to identify discrepancies between declared permissions and actual capabilities used. This article examines how SkillSpector detects MCP least privilege violations, specifically underdeclared and overdeclared capabilities, using the rules defined in src/skillspector/nodes/analyzers/mcp_least_privilege.py.
Understanding the Four MCP Least Privilege Rules
The analyzer implements four distinct rules (LP1-LP4) to verify that skills follow the principle of least privilege. Each rule targets a specific type of permission mismatch between the manifest and the implementation.
LP1 – Underdeclared Capability (HIGH Severity)
LP1 triggers when a capability is present in the code but absent from the manifest permissions. The analyzer extracts capabilities using regex patterns defined in _CAPABILITY_PATTERNS and maps declared permission strings to categories via _PERM_TO_CAPABILITY. When a detected category exists in the code but not in the mapped set of declared permissions, SkillSpector emits an LP1 finding.
This violation carries HIGH severity with confidence ≥ 0.75, though confidence drops to 0.55 when the capability appears only in a test file (detected by _is_test_file). To resolve this, developers must either add the missing permission to the permissions list in SKILL.md or remove the code requiring the capability.
LP3 – No Permissions Declared (MEDIUM Severity)
LP3 identifies skills that lack a permissions field entirely or contain an empty permissions list while the code exhibits one or more capabilities. The analyzer first gathers all capabilities through _detect_capabilities and then checks permissions_absent. If capabilities exist without corresponding declarations, the tool generates an LP3 finding with MEDIUM severity and approximately 0.70 confidence.
LP4 – Overdeclared Permission (LOW Severity)
LP4 detects when a permission is declared in the manifest but the corresponding capability is absent from the code. After mapping declared permissions to categories, the analyzer checks each mapped category against the set of detected capabilities (all_caps). If a declared category has no matching capability in the code, SkillSpector flags an LP4 violation.
This receives LOW severity with confidence ≈ 0.65, as excess permissions represent a less immediate security risk than under-declaration. The fix involves removing unnecessary permissions from SKILL.md.
LP2 – Wildcard Permission (MEDIUM Severity)
While not the primary focus of capability declaration accuracy, LP2 catches wildcard entries (*, all, full, any) in the permission list. The _has_wildcard function detects these patterns, triggering a MEDIUM severity finding because they grant unrestricted access contrary to least privilege principles.
How the Analyzer Detects Capability Violations
The detection logic in src/skillspector/nodes/analyzers/mcp_least_privilege.py follows a systematic pipeline to identify MCP least privilege violations.
-
Manifest Extraction: The analyzer retrieves the manifest via
state.get("manifest"), pulling YAML front-matter fromSKILL.md. -
Executable Detection: The tool skips skills containing only documentation files by checking
has_executable. -
Capability Detection: For each executable file,
_detect_capabilitiesexecutes regex patterns (such asr"\bhttpx\b"for network access orr"subprocess"for shell execution) against file contents. -
Permission Mapping: Each declared permission string normalizes and matches against
_PERM_TO_CAPABILITY, producingdeclared_categories. -
Finding Generation: The analyzer evaluates:
- LP2: Wildcard check via
_has_wildcard - LP3: Presence of capabilities when
permissionsis missing or empty - LP1: Detected capabilities absent from
declared_categories(with confidence adjustment for test files) - LP4: Declared categories with no matching detected capabilities
- LP2: Wildcard check via
The unit test suite in tests/test_mcp_least_privilege.py validates each detection scenario using fixture skills.
Code Examples of MCP Least Privilege Violations
The following examples demonstrate how SkillSpector identifies specific violations using the rules above.
Underdeclared Capability (LP1)
When a skill uses network and shell capabilities but only declares file-read permissions:
# SKILL.md
---
name: example-skill
permissions:
- read # declares only file-read
---
# scripts/agent.py
import httpx # ← network capability
import subprocess # ← shell capability
Running the analyzer yields LP1 findings for network and shell because they are not listed in permissions, as shown in the test fixture at tests/fixtures/mcp_underdeclared_skill/scripts/agent.py.
No Permissions Declared (LP3)
If the manifest lacks a permissions field entirely:
# SKILL.md
---
name: missing-perms-skill
# note: no `permissions` key at all
---
When the code contains patterns from _CAPABILITY_PATTERNS (such as os.system), SkillSpector emits an LP3 finding indicating the manifest is opaque.
Overdeclared Permission (LP4)
When the manifest claims capabilities that the code does not use:
# SKILL.md
---
name: over-privileged-skill
permissions:
- bash # shell
- network
- write # file-write
---
# scripts/format.py
with open("data.txt", "r") as f: # only reads, no write
print(f.read())
The analyzer detects only file_read capability, generating an LP4 finding for the over-declared write permission, as demonstrated in tests/fixtures/mcp_overprivileged_skill/scripts/helper.py.
Wildcard Permission (LP2)
For completeness, a wildcard entry triggers LP2:
# SKILL.md
---
name: wildcard-skill
permissions:
- "*"
---
The presence of * causes the analyzer to flag unrestricted access via the _has_wildcard check.
Summary
- LP1 (Underdeclared): Code uses capabilities not listed in
SKILL.mdpermissions; HIGH severity with confidence ≥ 0.75 (reduced to 0.55 in test files). - LP3 (No Permissions): Manifest lacks
permissionsfield while code exhibits capabilities; MEDIUM severity with confidence ≈ 0.70. - LP4 (Overdeclared): Permissions declared but unused in code; LOW severity with confidence ≈ 0.65.
- Detection Pipeline: Uses
_detect_capabilities,_CAPABILITY_PATTERNS, and_PERM_TO_CAPABILITYinsrc/skillspector/nodes/analyzers/mcp_least_privilege.pyto compare manifest declarations against actual code usage.
Frequently Asked Questions
What triggers an LP1 underdeclared capability violation in SkillSpector?
An LP1 violation triggers when the analyzer detects a capability in the code (such as httpx for network or subprocess for shell) that is not present in the skill's SKILL.md permissions list. The detection occurs in src/skillspector/nodes/analyzers/mcp_least_privilege.py when the _PERM_TO_CAPABILITY mapping reveals that a detected capability category is missing from the declared permissions.
How does SkillSpector differentiate between LP1 and LP3 violations?
LP1 applies when a permissions field exists but is missing specific capabilities, while LP3 triggers when the permissions field is entirely absent or empty despite the code containing capabilities. LP1 carries HIGH severity (0.75 confidence) whereas LP3 carries MEDIUM severity (0.70 confidence).
Why does SkillSpector reduce confidence for capabilities found only in test files?
The analyzer lowers LP1 confidence from 0.75 to 0.55 when _is_test_file identifies that a capability appears only in test files, because test dependencies often require capabilities that production code does not, and these may legitimately not need declaration in the main skill permissions.
What is the recommended fix for LP4 overdeclared permissions?
Remove the unnecessary permission from the permissions list in SKILL.md. Since LP4 indicates that a declared permission has no corresponding capability in the actual code (detected by comparing declared_categories against all_caps), deleting the unused permission aligns the manifest with the principle of least privilege.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →