# MCP Least Privilege Violations in SkillSpector: Underdeclared and Overdeclared Capabilities Explained

> Understand MCP least privilege violations in NVIDIA SkillSpector. Learn how underdeclared and overdeclared capabilities are detected and how to ensure proper permission management for enhanced security.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: deep-dive
- Published: 2026-06-22

---

**SkillSpector detects MCP least privilege violations by comparing declared permissions in [`SKILL.md`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/mcp_least_privilege.py) follows a systematic pipeline to identify MCP least privilege violations.

1. **Manifest Extraction**: The analyzer retrieves the manifest via `state.get("manifest")`, pulling YAML front-matter from [`SKILL.md`](https://github.com/NVIDIA/SkillSpector/blob/main/SKILL.md).

2. **Executable Detection**: The tool skips skills containing only documentation files by checking `has_executable`.

3. **Capability Detection**: For each executable file, `_detect_capabilities` executes regex patterns (such as `r"\bhttpx\b"` for network access or `r"subprocess"` for shell execution) against file contents.

4. **Permission Mapping**: Each declared permission string normalizes and matches against `_PERM_TO_CAPABILITY`, producing `declared_categories`.

5. **Finding Generation**: The analyzer evaluates:
   - **LP2**: Wildcard check via `_has_wildcard`
   - **LP3**: Presence of capabilities when `permissions` is missing or empty
   - **LP1**: Detected capabilities absent from `declared_categories` (with confidence adjustment for test files)
   - **LP4**: Declared categories with no matching detected capabilities

The unit test suite in [`tests/test_mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/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:

```yaml

# SKILL.md

---
name: example-skill
permissions:
  - read          # declares only file-read

---

```

```python

# 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`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/fixtures/mcp_underdeclared_skill/scripts/agent.py).

### No Permissions Declared (LP3)

If the manifest lacks a permissions field entirely:

```yaml

# 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:

```yaml

# SKILL.md

---
name: over-privileged-skill
permissions:
  - bash        # shell

  - network
  - write       # file-write

---

```

```python

# 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`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/fixtures/mcp_overprivileged_skill/scripts/helper.py).

### Wildcard Permission (LP2)

For completeness, a wildcard entry triggers LP2:

```yaml

# 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.md`](https://github.com/NVIDIA/SkillSpector/blob/main/SKILL.md) permissions; HIGH severity with confidence ≥ 0.75 (reduced to 0.55 in test files).
- **LP3 (No Permissions)**: Manifest lacks `permissions` field 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_CAPABILITY` in [`src/skillspector/nodes/analyzers/mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/mcp_least_privilege.py) to 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`](https://github.com/NVIDIA/SkillSpector/blob/main/SKILL.md) permissions list. The detection occurs in [`src/skillspector/nodes/analyzers/mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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.