MCP Permission Inference Specification¶
Version: 1.0.0 Status: Standard Last Updated: 2025-01-30
Table of Contents¶
- Introduction
- Terminology
- Permission Categories
- Data Model
- Inference Algorithm
- Permission Comparison
- Risk Assessment
- JSON Schema
- Examples
- 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:
- Implements all eight permission categories as defined
- Produces JSON output matching the schema in Section 8
- Calculates risk levels according to Section 7
- 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¶
| 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:
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)¶
Extraction patterns: 1. First argument of exec functions 2. String literals in command arrays 3. Shell command strings
5.3.2 Path Extraction (filesystem)¶
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¶
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
7. Risk Assessment¶
7.1 Risk Levels¶
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:
- Any undeclared
execoreval→ Critical - Any undeclared
secretswithexposed=true→ Critical - Any undeclared filesystem
delete→ Critical - Any undeclared
networkwithhost="*"→ High - Multiple undeclared permissions → Escalate one level
- 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):
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.