Skip to content

MCP Permission Inference Specification

Version: 1.0.0 Status: Standard Last Updated: 2025-01-30


Table of Contents

  1. Introduction
  2. Terminology
  3. Permission Categories
  4. Data Model
  5. Inference Algorithm
  6. Permission Comparison
  7. Risk Assessment
  8. JSON Schema
  9. Examples
  10. Integration Guidelines

1. Introduction

1.1 Purpose

This specification defines the MCP Permission Inference System, a mechanism for automatically detecting and cataloging the permissions required by an MCP (Model Context Protocol) server based on static code analysis.

The system analyzes security findings from mcp-scan and maps them to a structured permission model, enabling:

  • Transparency: Users understand what capabilities an MCP server requires
  • Comparison: Declared permissions can be validated against actual code behavior
  • Risk Assessment: Undeclared or excessive permissions are flagged
  • Policy Enforcement: Organizations can enforce permission policies

1.2 Scope

This specification covers:

  • Permission category definitions
  • Data structures for representing permissions
  • The inference algorithm that maps findings to permissions
  • Comparison logic between declared and inferred permissions
  • Risk level classification
  • JSON output format

1.3 Conformance

An implementation conforms to this specification if it:

  1. Implements all eight permission categories as defined
  2. Produces JSON output matching the schema in Section 8
  3. Calculates risk levels according to Section 7
  4. Follows the inference algorithm described in Section 5

2. Terminology

Term Definition
MCP Server A server implementing the Model Context Protocol
Permission A capability that an MCP server requires to function
Category A classification grouping related permissions
Finding A security-relevant code pattern detected by mcp-scan
Inferred Permission A permission detected through code analysis
Declared Permission A permission explicitly stated in a manifest
Confidence The certainty level of a permission detection
Sink A code location where data flows to a sensitive operation

3. Permission Categories

The system defines eight permission categories, each representing a distinct capability domain.

3.1 Exec (exec)

Description: Ability to execute external commands or spawn processes.

Risk Level: Critical

Examples: - subprocess.run() in Python - child_process.exec() in Node.js - os.system() in Python - Shell command execution

Sub-permissions: - Specific command allowlists - Shell access (restricted vs unrestricted)

3.2 Eval (eval)

Description: Ability to dynamically evaluate or compile code at runtime.

Risk Level: Critical

Examples: - eval() in Python/JavaScript - exec() in Python - Function() constructor in JavaScript - compile() in Python

Note: This is a boolean permission (present or absent).

3.3 Filesystem (filesystem)

Description: Ability to read, write, or delete files on the local filesystem.

Risk Level: High to Critical (depending on scope)

Operations: - Read: Access to read file contents - Write: Ability to create or modify files - Delete: Ability to remove files or directories

Examples: - open(), Path.read_text() in Python - fs.readFileSync(), fs.writeFileSync() in Node.js - shutil.rmtree() in Python

3.4 Network (network)

Description: Ability to make outbound network connections.

Risk Level: High

Attributes: - Target hosts/domains - Protocols (HTTP, HTTPS, WebSocket, raw TCP) - Ports

Examples: - requests.get(), httpx.post() in Python - fetch(), axios in JavaScript - Socket operations

3.5 Database (database)

Description: Ability to connect to and query databases.

Risk Level: High

Attributes: - Database type (SQLite, PostgreSQL, MySQL, MongoDB, Redis) - Write access flag

Examples: - sqlite3.connect() in Python - psycopg2, pymongo connections - ORM operations (SQLAlchemy, Prisma)

3.6 Secrets (secrets)

Description: Access to sensitive credentials, tokens, or API keys.

Risk Level: Critical

Attributes: - Secret name/identifier - Secret type (api_key, token, password, certificate) - Exposure risk (logged, transmitted, stored)

Examples: - Reading from .env files - Accessing credential stores - Environment variables with sensitive names

3.7 LLM (llm)

Description: Integration with Large Language Model APIs.

Risk Level: Medium to High

Providers: - OpenAI - Anthropic - Google (Gemini) - Ollama (local) - Cohere - Hugging Face - LangChain (framework) - LlamaIndex (framework)

Examples: - openai.ChatCompletion.create() - anthropic.messages.create() - LangChain chains and agents

3.8 Env (env)

Description: Access to environment variables.

Risk Level: Low to High (depending on variable)

Attributes: - Variable name - Sensitivity flag (sensitive variables like API keys) - Write access flag

Examples: - os.environ.get() in Python - process.env in Node.js - os.Getenv() in Go


4. Data Model

4.1 InferredPermissions

The root structure containing all detected permissions.

InferredPermissions {
    version: string           // Specification version (e.g., "1.0.0")
    exec: ExecPermission?     // Command execution permissions
    eval: boolean             // Dynamic code evaluation
    filesystem: FilesystemPermission?
    network: NetworkPermission?
    database: DatabasePermission?
    secrets: SecretsPermission?
    llm: LLMPermission?
    env: EnvPermission?
    summary: PermissionSummary
}

4.2 ExecPermission

ExecPermission {
    commands: CommandPermission[]  // Specific commands detected
    shell: boolean                 // Unrestricted shell access
}

CommandPermission {
    command: string           // Command name or pattern
    dangerous: boolean        // High-risk command flag
    confidence: Confidence
    location: string          // Source location (file:line)
}

Dangerous commands include: rm, chmod, chown, kill, mkfs, dd, curl|sh, wget|sh

4.3 FilesystemPermission

FilesystemPermission {
    read: PathPattern[]       // Read access patterns
    write: PathPattern[]      // Write access patterns
    delete: PathPattern[]     // Delete access patterns
}

PathPattern {
    pattern: string           // Path or glob pattern
    confidence: Confidence
    location: string
}

4.4 NetworkPermission

NetworkPermission {
    outbound: HostPermission[]  // Outbound connection targets
    inbound: HostPermission[]   // Inbound listeners (rare)
}

HostPermission {
    host: string              // Hostname, IP, or "*" for any
    protocol: Protocol        // http, https, ws, wss, tcp, udp
    port: integer?            // Specific port or null for any
    confidence: Confidence
    location: string
}

Protocol: "http" | "https" | "ws" | "wss" | "tcp" | "udp" | "unknown"

4.5 DatabasePermission

DatabasePermission {
    connections: DatabaseConnection[]
}

DatabaseConnection {
    database_type: DatabaseType
    write_access: boolean     // Can modify data
    confidence: Confidence
    location: string
}

DatabaseType: "sqlite" | "postgresql" | "mysql" | "mongodb" |
              "redis" | "elasticsearch" | "unknown"

4.6 SecretsPermission

SecretsPermission {
    accessed: SecretAccess[]
}

SecretAccess {
    name: string              // Secret identifier
    secret_type: SecretType   // Classification
    exposed: boolean          // Potentially logged/transmitted
    confidence: Confidence
    location: string
}

SecretType: "api_key" | "token" | "password" | "certificate" |
            "connection_string" | "unknown"

4.7 LLMPermission

LLMPermission {
    providers: LLMProviderAccess[]
}

LLMProviderAccess {
    provider: LLMProvider
    confidence: Confidence
    location: string
}

LLMProvider: "openai" | "anthropic" | "google" | "ollama" |
             "cohere" | "huggingface" | "langchain" | "llamaindex" | "unknown"

4.8 EnvPermission

EnvPermission {
    accessed: EnvVarAccess[]
}

EnvVarAccess {
    name: string              // Variable name
    sensitive: boolean        // Contains sensitive data
    write: boolean            // Modifies the variable
    confidence: Confidence
    location: string
}

4.9 PermissionSummary

PermissionSummary {
    total_permissions: integer      // Total count
    by_category: map<Category, int> // Count per category
    high_risk_count: integer        // Critical/high risk items
    findings_analyzed: integer      // Source findings count
}

4.10 Confidence Levels

Confidence: "high" | "medium" | "low"
Level Description Typical Source
high Direct API call detected Pattern match on known function
medium Indirect or partial match Inferred from imports or context
low Heuristic detection String analysis or naming patterns

5. Inference Algorithm

5.1 Overview

The inference process transforms security findings into structured permissions:

Findings[] → CategoryClassifier → Extractor → InferredPermissions

5.2 Category Classification

Each finding is classified by analyzing its rule ID and sink category:

Rule ID Pattern Category
*subprocess*, *os.system*, *child_process*, *spawn*, *popen*, MCP-A* exec
*eval*, *exec* (without subprocess), *compile*, *Function* eval
*file*, *path*, *open* (not openai), *write*, *read*, *unlink*, *shutil*, MCP-B* filesystem
*http*, *fetch*, *request*, *axios*, *urllib*, *socket*, *ssrf*, MCP-C* network
*sql*, *database*, *query*, *cursor*, *sqlite*, *postgres*, *mongo*, MCP-D* database
*secret*, *token*, *credential*, *password*, *api_key*, MCP-E* secrets
*llm*, *openai*, *anthropic*, *claude*, *gpt*, *langchain* llm
*env*, *environ*, *getenv*, *process.env* env

5.3 Detail Extraction

After classification, specific details are extracted from the finding:

5.3.1 Command Extraction (exec)

Input: "subprocess.run(['ls', '-la', path])"
Output: command="ls", dangerous=false

Extraction patterns: 1. First argument of exec functions 2. String literals in command arrays 3. Shell command strings

5.3.2 Path Extraction (filesystem)

Input: "open('/etc/passwd', 'r')"
Output: pattern="/etc/*", operation=read

Operation detection: - Read: 'r', 'rb', read, readFile - Write: 'w', 'a', 'wb', write, writeFile - Delete: unlink, remove, rmtree, rmdir

5.3.3 Host Extraction (network)

Input: "requests.get('https://api.example.com/data')"
Output: host="api.example.com", protocol="https"

Extraction regex: https?://([a-zA-Z0-9][-a-zA-Z0-9.]*)

5.3.4 LLM Provider Detection

Input: Rule ID "py-openai-llm-chat", snippet "openai.ChatCompletion"
Output: provider="openai"

Provider patterns: | Pattern | Provider | |---------|----------| | openai, gpt | openai | | anthropic, claude | anthropic | | ollama | ollama | | langchain | langchain | | llamaindex, llama_index | llamaindex |

5.4 Deduplication

Permissions are deduplicated by: 1. Same category 2. Same target (command, path, host, etc.) 3. Higher confidence replaces lower confidence


6. Permission Comparison

6.1 Purpose

Comparison validates that declared permissions match actual code behavior.

6.2 Comparison Result

ComparisonResult {
    matches: PermissionMatch[]        // Declared and found in code
    undeclared: UndeclaredPermission[] // Found but not declared
    overdeclared: OverdeclaredPermission[] // Declared but not found
    risk_level: RiskLevel
    summary: string
}

6.3 Match Types

6.3.1 Matched Permission

A declared permission that was confirmed by code analysis.

PermissionMatch {
    category: Category
    declared: string    // From manifest
    inferred: string    // From code
    confidence: Confidence
}

6.3.2 Undeclared Permission

A permission detected in code but not declared in the manifest.

Security implication: The MCP server uses capabilities it didn't disclose.

UndeclaredPermission {
    category: Category
    permission: string
    risk_level: RiskLevel
    location: string
    recommendation: string
}

6.3.3 Overdeclared Permission

A permission declared but not detected in code.

Possible causes: - Dead code - Dynamic/runtime behavior not detected - Overly broad declaration

OverdeclaredPermission {
    category: Category
    declared: string
    possible_reasons: string[]
}

7. Risk Assessment

7.1 Risk Levels

RiskLevel: "low" | "medium" | "high" | "critical"

7.2 Category Risk Classification

Category Base Risk Critical Conditions
exec Critical Always critical
eval Critical Always critical
filesystem High Write/delete to system paths
network High Wildcard hosts
database High Write access
secrets Critical Exposed secrets
llm Medium Multiple providers
env Low-Medium Sensitive variables

7.3 Risk Escalation Rules

The overall risk level is the maximum of:

  1. Any undeclared exec or eval → Critical
  2. Any undeclared secrets with exposed=true → Critical
  3. Any undeclared filesystem delete → Critical
  4. Any undeclared network with host="*" → High
  5. Multiple undeclared permissions → Escalate one level
  6. Only overdeclared permissions → Low

7.4 Risk Score Calculation

risk_score = sum(category_weight * permission_count * confidence_multiplier)

category_weights = {
    exec: 10,
    eval: 10,
    secrets: 8,
    filesystem: 6,
    network: 5,
    database: 5,
    llm: 3,
    env: 2
}

confidence_multiplier = {
    high: 1.0,
    medium: 0.7,
    low: 0.4
}

8. JSON Schema

8.1 Complete Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "MCP Inferred Permissions",
  "type": "object",
  "properties": {
    "version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$"
    },
    "exec": {
      "$ref": "#/$defs/ExecPermission"
    },
    "eval": {
      "type": "boolean"
    },
    "filesystem": {
      "$ref": "#/$defs/FilesystemPermission"
    },
    "network": {
      "$ref": "#/$defs/NetworkPermission"
    },
    "database": {
      "$ref": "#/$defs/DatabasePermission"
    },
    "secrets": {
      "$ref": "#/$defs/SecretsPermission"
    },
    "llm": {
      "$ref": "#/$defs/LLMPermission"
    },
    "env": {
      "$ref": "#/$defs/EnvPermission"
    },
    "summary": {
      "$ref": "#/$defs/PermissionSummary"
    }
  },
  "required": ["version", "summary"],
  "$defs": {
    "Confidence": {
      "type": "string",
      "enum": ["high", "medium", "low"]
    },
    "ExecPermission": {
      "type": "object",
      "properties": {
        "commands": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "command": { "type": "string" },
              "dangerous": { "type": "boolean" },
              "confidence": { "$ref": "#/$defs/Confidence" },
              "location": { "type": "string" }
            },
            "required": ["command", "dangerous", "confidence", "location"]
          }
        },
        "shell": { "type": "boolean" }
      }
    },
    "FilesystemPermission": {
      "type": "object",
      "properties": {
        "read": { "type": "array", "items": { "$ref": "#/$defs/PathPattern" } },
        "write": { "type": "array", "items": { "$ref": "#/$defs/PathPattern" } },
        "delete": { "type": "array", "items": { "$ref": "#/$defs/PathPattern" } }
      }
    },
    "PathPattern": {
      "type": "object",
      "properties": {
        "pattern": { "type": "string" },
        "confidence": { "$ref": "#/$defs/Confidence" },
        "location": { "type": "string" }
      },
      "required": ["pattern", "confidence", "location"]
    },
    "NetworkPermission": {
      "type": "object",
      "properties": {
        "outbound": { "type": "array", "items": { "$ref": "#/$defs/HostPermission" } },
        "inbound": { "type": "array", "items": { "$ref": "#/$defs/HostPermission" } }
      }
    },
    "HostPermission": {
      "type": "object",
      "properties": {
        "host": { "type": "string" },
        "protocol": {
          "type": "string",
          "enum": ["http", "https", "ws", "wss", "tcp", "udp", "unknown"]
        },
        "port": { "type": ["integer", "null"] },
        "confidence": { "$ref": "#/$defs/Confidence" },
        "location": { "type": "string" }
      },
      "required": ["host", "protocol", "confidence", "location"]
    },
    "DatabasePermission": {
      "type": "object",
      "properties": {
        "connections": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "database_type": {
                "type": "string",
                "enum": ["sqlite", "postgresql", "mysql", "mongodb", "redis", "elasticsearch", "unknown"]
              },
              "write_access": { "type": "boolean" },
              "confidence": { "$ref": "#/$defs/Confidence" },
              "location": { "type": "string" }
            },
            "required": ["database_type", "write_access", "confidence", "location"]
          }
        }
      }
    },
    "SecretsPermission": {
      "type": "object",
      "properties": {
        "accessed": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "secret_type": {
                "type": "string",
                "enum": ["api_key", "token", "password", "certificate", "connection_string", "unknown"]
              },
              "exposed": { "type": "boolean" },
              "confidence": { "$ref": "#/$defs/Confidence" },
              "location": { "type": "string" }
            },
            "required": ["name", "secret_type", "exposed", "confidence", "location"]
          }
        }
      }
    },
    "LLMPermission": {
      "type": "object",
      "properties": {
        "providers": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": ["openai", "anthropic", "google", "ollama", "cohere", "huggingface", "langchain", "llamaindex", "unknown"]
              },
              "confidence": { "$ref": "#/$defs/Confidence" },
              "location": { "type": "string" }
            },
            "required": ["provider", "confidence", "location"]
          }
        }
      }
    },
    "EnvPermission": {
      "type": "object",
      "properties": {
        "accessed": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "sensitive": { "type": "boolean" },
              "write": { "type": "boolean" },
              "confidence": { "$ref": "#/$defs/Confidence" },
              "location": { "type": "string" }
            },
            "required": ["name", "sensitive", "write", "confidence", "location"]
          }
        }
      }
    },
    "PermissionSummary": {
      "type": "object",
      "properties": {
        "total_permissions": { "type": "integer", "minimum": 0 },
        "by_category": {
          "type": "object",
          "additionalProperties": { "type": "integer" }
        },
        "high_risk_count": { "type": "integer", "minimum": 0 },
        "findings_analyzed": { "type": "integer", "minimum": 0 }
      },
      "required": ["total_permissions", "by_category", "high_risk_count", "findings_analyzed"]
    }
  }
}

9. Examples

9.1 Basic MCP Server with Multiple Permissions

Source code (Python):

import subprocess
import requests
import openai
import os

def execute_command(cmd):
    return subprocess.run(cmd, shell=True, capture_output=True)

def fetch_data(url):
    return requests.get(url)

def ask_llm(prompt):
    api_key = os.environ.get("OPENAI_API_KEY")
    return openai.ChatCompletion.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}]
    )

Inferred permissions:

{
  "version": "1.0.0",
  "exec": {
    "commands": [
      {
        "command": "*",
        "dangerous": true,
        "confidence": "high",
        "location": "server.py:7"
      }
    ],
    "shell": true
  },
  "network": {
    "outbound": [
      {
        "host": "*",
        "protocol": "https",
        "port": null,
        "confidence": "high",
        "location": "server.py:10"
      }
    ]
  },
  "llm": {
    "providers": [
      {
        "provider": "openai",
        "confidence": "high",
        "location": "server.py:14"
      }
    ]
  },
  "env": {
    "accessed": [
      {
        "name": "OPENAI_API_KEY",
        "sensitive": true,
        "write": false,
        "confidence": "high",
        "location": "server.py:13"
      }
    ]
  },
  "summary": {
    "total_permissions": 4,
    "by_category": {
      "exec": 1,
      "network": 1,
      "llm": 1,
      "env": 1
    },
    "high_risk_count": 2,
    "findings_analyzed": 4
  }
}

9.2 Filesystem Operations

Source code (TypeScript):

import * as fs from 'fs';
import * as path from 'path';

export function readConfig(configPath: string): string {
    return fs.readFileSync(configPath, 'utf-8');
}

export function writeOutput(data: string): void {
    fs.writeFileSync('/tmp/output.txt', data);
}

export function cleanup(dir: string): void {
    fs.rmSync(dir, { recursive: true });
}

Inferred permissions:

{
  "version": "1.0.0",
  "filesystem": {
    "read": [
      {
        "pattern": "*",
        "confidence": "high",
        "location": "files.ts:5"
      }
    ],
    "write": [
      {
        "pattern": "/tmp/*",
        "confidence": "high",
        "location": "files.ts:9"
      }
    ],
    "delete": [
      {
        "pattern": "*",
        "confidence": "high",
        "location": "files.ts:13"
      }
    ]
  },
  "summary": {
    "total_permissions": 3,
    "by_category": {
      "filesystem": 3
    },
    "high_risk_count": 1,
    "findings_analyzed": 3
  }
}

9.3 Permission Comparison Result

Declared permissions (manifest):

{
  "permissions": {
    "exec": ["ls", "cat"],
    "llm": ["openai"]
  }
}

Comparison result:

{
  "matches": [
    {
      "category": "exec",
      "declared": "ls",
      "inferred": "ls",
      "confidence": "high"
    },
    {
      "category": "llm",
      "declared": "openai",
      "inferred": "openai",
      "confidence": "high"
    }
  ],
  "undeclared": [
    {
      "category": "exec",
      "permission": "rm",
      "risk_level": "critical",
      "location": "cleanup.py:15",
      "recommendation": "Add 'rm' to declared exec permissions or remove the code"
    },
    {
      "category": "llm",
      "permission": "anthropic",
      "risk_level": "medium",
      "location": "llm.py:28",
      "recommendation": "Add 'anthropic' to declared llm permissions"
    }
  ],
  "overdeclared": [
    {
      "category": "exec",
      "declared": "cat",
      "possible_reasons": [
        "Command is never called in analyzed code",
        "Command may be called dynamically at runtime",
        "Dead code path"
      ]
    }
  ],
  "risk_level": "critical",
  "summary": "2 matches, 2 undeclared (1 critical), 1 overdeclared"
}


10. Integration Guidelines

10.1 Scan Output Integration

The inferred_permissions field appears in the mcp_surface section of scan results:

{
  "mcp_surface": {
    "tools": [...],
    "transport": "stdio",
    "inferred_permissions": {
      "version": "1.0.0",
      ...
    }
  },
  "findings": [...]
}

10.2 CI/CD Integration

Recommended checks for CI/CD pipelines:

# Example GitHub Actions workflow
- name: Scan MCP Server
  run: mcp-scan scan . --output json > scan-results.json

- name: Check Critical Permissions
  run: |
    jq -e '.mcp_surface.inferred_permissions.exec.shell != true' scan-results.json
    jq -e '.mcp_surface.inferred_permissions.summary.high_risk_count < 3' scan-results.json

10.3 Policy Enforcement

Organizations can define permission policies:

# Example policy
permission_policy:
  allowed_categories:
    - filesystem
    - network
    - llm
  denied_categories:
    - exec
    - eval
  max_risk_level: medium
  require_declaration: true

10.4 Manifest Declaration Format

Recommended manifest format for declaring permissions:

{
  "mcp": {
    "name": "my-mcp-server",
    "version": "1.0.0",
    "permissions": {
      "filesystem": {
        "read": ["/config/*"],
        "write": ["/tmp/*"]
      },
      "network": {
        "outbound": ["api.example.com"]
      },
      "llm": ["openai"],
      "env": ["OPENAI_API_KEY", "CONFIG_PATH"]
    }
  }
}

Appendix A: Sensitive Environment Variables

The following patterns are considered sensitive:

Pattern Type
*_API_KEY, *_APIKEY API Key
*_SECRET* Secret
*_TOKEN Token
*_PASSWORD, *_PASSWD Password
*_CREDENTIAL* Credential
DATABASE_URL, *_DB_* Connection String
AWS_*, AZURE_*, GCP_* Cloud Credentials
PRIVATE_KEY, *_PRIVATE_* Private Key

Appendix B: Dangerous Commands

Commands classified as dangerous:

Command Risk Reason
rm Critical File deletion
chmod High Permission modification
chown High Ownership modification
kill High Process termination
mkfs Critical Filesystem formatting
dd Critical Raw disk access
curl \| sh Critical Remote code execution
wget \| sh Critical Remote code execution
eval Critical Code injection
sudo Critical Privilege escalation

Appendix C: Version History

Version Date Changes
1.0.0 2025-01-30 Initial specification

Copyright (c) 2025 MCP Hub Platform. All rights reserved.