Skip to content

Security

github-actions[bot] edited this page Apr 3, 2026 · 20 revisions

Security

The MATLAB MCP Server includes multiple security layers to authenticate users, prevent code injection, isolate workspaces, and protect system resources while keeping MATLAB accessible to AI agents.

graph TB
    Agent["AI Agent<br/>(Claude Code, Cursor, etc)"]
    
    subgraph Transport["HTTP Transport Layer"]
        Auth["Bearer Token<br/>Authentication"]
        CORS["CORS Headers<br/>(Origins, Methods)"]
    end
    
    subgraph FastMCP["FastMCP Server"]
        MCP["MCP Protocol<br/>Message Handler"]
    end
    
    subgraph Security["Security Validation"]
        Validator["Code Validator<br/>(Blocked Functions,<br/>String/Comment Stripping)"]
        Sanitizer["Filename<br/>Sanitizer<br/>(Path Traversal<br/>Prevention)"]
        HITL["Human-in-the-Loop<br/>Approval Gates<br/>(Optional)"]
    end
    
    subgraph Isolation["Workspace & Session Isolation"]
        Sessions["Session Manager<br/>(Per-User Workspaces)"]
        WorkspaceReset["Workspace Reset<br/>(clear all, fclose, etc)"]
    end
    
    MATLAB["MATLAB Engine<br/>(Execution)"]
    
    Agent -->|Bearer Token| Auth
    Auth -->|Valid Request| CORS
    CORS -->|MCP Message| MCP
    MCP -->|Tool Invocation| Validator
    Validator -->|Code OK| HITL
    HITL -->|Approved| Sessions
    Sessions -->|Session ID| WorkspaceReset
    WorkspaceReset -->|Clean Env| MATLAB
    
    MCP -->|File Operation| Sanitizer
    Sanitizer -->|Path Safe| Sessions
    
    Auth -->|Invalid Token| Reject["401 Unauthorized<br/>WWW-Authenticate: Bearer"]
    Validator -->|Blocked Function| Reject
    Sanitizer -->|Path Traversal| Reject
    HITL -->|Denied| Reject
    
    style Auth fill:#2563eb,color:#fff
    style Reject fill:#dc2626,color:#fff
    style Validator fill:#7c3aed,color:#fff
    style HITL fill:#f59e0b,color:#000
Loading

Authentication & Authorization

Bearer Token Authentication

All HTTP requests (SSE and streamable HTTP transports) require a valid bearer token in the Authorization header:

GET /mcp HTTP/1.1
Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Configuration:

# Generate a 64-character hex token
matlab-mcp --generate-token
# Output:
#   6a7f9e2c5d1b8a3f4e6c9d2b5f8a1e3d7c2b5a9e1f6d3c8b4a7f2e5c9d1b3a
#   
#   Add to your shell environment:
#   export MATLAB_MCP_AUTH_TOKEN="6a7f9e2c5d1b8a3f4e6c9d2b5f8a1e3d7c2b5a9e1f6d3c8b4a7f2e5c9d1b3a"
#   
#   (Windows PowerShell)
#   $env:MATLAB_MCP_AUTH_TOKEN = "6a7f9e2c5d1b8a3f4e6c9d2b5f8a1e3d7c2b5a9e1f6d3c8b4a7f2e5c9d1b3a"

Token source: Environment variable MATLAB_MCP_AUTH_TOKEN only. Tokens are read at server startup and compared using constant-time comparison (hmac.compare_digest) to prevent timing attacks.

When no token is set: Authentication is disabled — all requests pass through. A startup warning is logged for HTTP transports without a token configured:

WARNING: MATLAB_MCP_AUTH_TOKEN not set; HTTP transport is running without authentication!

Invalid token response:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
Content-Type: application/json

{
  "error": "unauthorized",
  "message": "Valid Bearer token required"
}

Exemptions:

  • /health endpoint always bypasses authentication (for load balancer health checks)
  • OPTIONS requests bypass authentication (CORS pre-flight)
  • stdio transport has no authentication (single-user, inherits shell security)

CORS Configuration

When HTTP transport is active, CORS headers are automatically configured:

Header Value Purpose
Access-Control-Allow-Origin * Allow all origins (agents may be on different hosts)
Access-Control-Allow-Methods GET, POST, OPTIONS Standard HTTP methods
Access-Control-Allow-Headers Authorization, Content-Type, Accept Require auth header support

This enables browser-based agents (Claude Code) to make cross-origin requests to the server.


Input Validation & Sanitization

Code Security Validation

The SecurityValidator prevents dangerous MATLAB operations by checking code against a blocklist before execution.

Default Blocklist

security:
  blocked_functions_enabled: true
  blocked_functions:
    - "system"      # Execute OS commands
    - "unix"        # Execute Unix commands
    - "dos"         # Execute DOS/Windows commands
    - "!"           # Shell escape operator
    - "eval"        # Execute string as MATLAB code
    - "feval"       # Call function by dynamic name
    - "evalc"       # Evaluate and capture output
    - "evalin"      # Evaluate in caller/base workspace
    - "assignin"    # Assign variable to caller workspace
    - "perl"        # Execute Perl scripts
    - "python"      # Execute Python scripts

Smart Scanning

The validator strips string literals and comments before checking, preventing false positives:

% These are SAFE and will NOT trigger the blocklist:
disp('The operating system is great')    % "system" inside string literal
% system('ls')                            % "system" inside comment
msg = "unix-based systems";              % "unix" inside string literal
help('system')                           % "system" as help argument (inside string)

% This WILL be blocked:
system('rm -rf /')                       % Actual system() function call

Customizing the blocklist:

security:
  blocked_functions_enabled: true
  blocked_functions:
    - "system"
    - "eval"
    - "assignin"
    - "web"
    - "url"
    # Add your own blocked patterns

Error response when blocked:

{
  "status": "failed",
  "error": "ValidationError",
  "message": "Code contains blocked function: 'system'",
  "code": "BLOCKED_FUNCTION"
}

Filename Sanitization

File operations (upload_data, delete_file, read_*) validate filenames to prevent path traversal attacks:

Rejected patterns:

  • ../../etc/passwd — parent directory traversal
  • /etc/passwd — absolute paths
  • C:\Windows\System32 — Windows absolute paths
  • file;rm -rf — command separators (illegal chars)

Allowed characters: [a-zA-Z0-9._-] only

Sanitized example:

sanitize_filename("my-data_v2.csv")      # ✓ ALLOWED
sanitize_filename("../data/file.csv")    # ✗ REJECTED
sanitize_filename("file; rm -rf /")      # ✗ REJECTED

Upload Size Limits

security:
  max_upload_size_mb: 100  # Default: 100 MB

Uploads exceeding this limit are rejected:

{
  "status": "failed",
  "error": "ValidationError",
  "message": "Upload size 250 MB exceeds limit of 100 MB"
}

Human-in-the-Loop Approval Gates

For sensitive operations, the server can require human approval before execution:

hitl:
  enabled: false                      # Enable human approval gates
  protected_functions:                # Specific functions requiring approval
    - "eval"
    - "system"
    - "assignin"
  protect_file_ops: true              # Require approval for upload/delete
  all_execute: false                  # Require approval for ALL code execution

Approval flow:

  1. Agent invokes a tool (e.g., execute_code)
  2. If HITL is enabled and the operation is protected, server calls ctx.elicit()
  3. Human reviews the request in their MCP client UI
  4. Human approves or denies
  5. If approved, execution proceeds; if denied, the operation is blocked

Protected operations:

  • Code containing protected functions (protected_functions list)
  • File uploads and deletes (when protect_file_ops: true)
  • All code execution (when all_execute: true)

Example response when denied:

{
  "status": "failed",
  "error": "HumanApprovalDenied",
  "message": "Human approval was declined for this operation"
}

Configuration Options & Recommended Values

Server Configuration

server:
  transport: "streamablehttp"        # Use streamable HTTP for remote agents
  host: "127.0.0.1"                  # Default: loopback (safer, avoids firewall)
  port: 8765
  log_level: "info"

Why loopback default? Windows 10 Firewall blocks inbound connections to 0.0.0.0. Using 127.0.0.1 (loopback) prevents UAC prompts and firewall dialogs. If you need remote access, use a reverse proxy or set MATLAB_MCP_SERVER_HOST=0.0.0.0.

Execution Configuration

execution:
  workspace_isolation: true           # ALWAYS: Reset workspace between sessions
  temp_cleanup_on_disconnect: true    # ALWAYS: Delete temp files on session end
  sync_timeout: 30                    # Sync execution timeout (seconds)
  max_execution_time: 86400           # Max job duration (24 hours)

Security Configuration

security:
  blocked_functions_enabled: true     # ALWAYS: Enable in production
  blocked_functions:
    - "system"
    - "eval"
    - "assignin"
    # ... (see default blocklist above)
  max_upload_size_mb: 100

Session Configuration

sessions:
  max_sessions: 50                    # Limit concurrent sessions
  session_timeout: 3600               # Idle timeout (1 hour)
  job_retention_seconds: 86400        # Keep job metadata for 24 hours

Best Practices for Secure Deployment

Personal Use (Single Agent, Single Machine)

server:
  transport: "stdio"                  # No network exposure
  
# No authentication needed — shell session is the security boundary

Recommendation: Use stdio. Your terminal login is the security boundary; MATLAB runs with your user permissions.


Team Server (Multiple Agents, Shared MATLAB)

server:
  transport: "streamablehttp"
  host: "127.0.0.1"
  port: 8765

security:
  blocked_functions_enabled: true
  require_proxy_auth: true            # MUST: Acknowledge reverse proxy auth
  max_upload_size_mb: 50
  
sessions:
  max_sessions: 10
  session_timeout: 1800               # Aggressive idle timeout

hitl:
  enabled: false                      # Optional: enable for high-risk operations

Deployment steps:

  1. Run server behind a reverse proxy (nginx, Caddy, Traefik) on 127.0.0.1:8765
  2. Configure proxy to require authentication (OAuth, LDAP, HTTP Basic)
  3. Set MATLAB_MCP_AUTH_TOKEN for agent-to-server communication
  4. Configure DNS to point agents to the proxy URL (not direct server IP)

Nginx example:

upstream matlab_mcp {
    server 127.0.0.1:8765;
}

server {
    listen 443 ssl http2;
    server_name matlab-server.mycompany.com;

    ssl_certificate /etc/letsencrypt/live/matlab-server.mycompany.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/matlab-server.mycompany.com/privkey.pem;

    auth_basic "MATLAB MCP Server";
    auth_basic_user_file /etc/nginx/htpasswd;

    location /mcp {
        proxy_pass http://matlab_mcp;
        proxy_set_header Authorization $http_authorization;
        proxy_pass_header Authorization;
        
        # For SSE/streamable HTTP
        proxy_http_version 1.1;
        proxy_buffering off;
        proxy_request_buffering off;
    }

    location /health {
        proxy_pass http://matlab_mcp/health;
        access_log off;
    }
}

Production Deployment (Enterprise)

server:
  transport: "streamablehttp"
  host: "127.0.0.1"                  # Never expose to public internet
  port: 8765

security:
  blocked_functions_enabled: true
  blocked_functions:                 # Review for your use case
    - "system"
    - "unix"
    - "dos"
    - "!"
    - "eval"
    - "feval"
    - "evalc"
    - "evalin"
    - "assignin"
    - "perl"
    - "python"
    - "web"                          # Add any dangerous functions
  require_proxy_auth: true
  max_upload_size_mb: 50

sessions:
  max_sessions: 5
  session_timeout: 900                # 15-minute idle timeout

hitl:
  enabled: true                       # Require approval for protected ops
  protected_functions:
    - "eval"
    - "system"
    - "assignin"
  protect_file_ops: true
  all_execute: false

monitoring:
  enabled: true
  metrics_db: "/var/lib/matlab-mcp/metrics.db"  # Persistent metrics

Deployment checklist:

  • Run behind TLS reverse proxy (nginx, Traefik, etc.)
  • Enable authentication on the reverse proxy (OAuth 2.0, LDAP, mTLS)
  • Set MATLAB_MCP_AUTH_TOKEN for server-agent communication
  • Enable monitoring and review metrics dashboard regularly
  • Enable HITL gates for sensitive operations
  • Review and customize the blocklist for your use case
  • Set aggressive session/execution timeouts
  • Monitor disk space for temp files and metrics database
  • Rotate logs regularly (configure your log manager)
  • Restrict MATLAB toolbox access if needed (via startup script)

Reporting Security Vulnerabilities

If you discover a security vulnerability in the MATLAB MCP Server, please do not open a public GitHub issue. Instead:

  1. Email the maintainer: [security@your-domain.com] with:

    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact
    • Suggested fix (if you have one)
  2. Include in the subject line: [SECURITY] MATLAB MCP Server vulnerability

  3. Allow time for a fix: Expect a response within 48 hours and a patch release within 7 days for critical issues.

  4. Responsible disclosure: Avoid sharing vulnerability details publicly until a patch is released.

Public vulnerability tracking: Once patched, we'll acknowledge the issue in the CHANGELOG and GitHub release notes, crediting the reporter (if desired).


Data Flow Diagram: Request to Execution

sequenceDiagram
    participant Agent as AI Agent
    participant Auth as BearerAuth Middleware
    participant MCP as FastMCP Server
    participant Validator as SecurityValidator
    participant Executor as JobExecutor
    participant Engine as MATLAB Engine

    Agent->>Auth: GET /mcp + Bearer token
    Auth->>Auth: Verify token (constant-time)
    
    alt Token invalid
        Auth-->>Agent: 401 Unauthorized
    else Token valid
        Auth->>MCP: Forward request
        MCP->>MCP: Parse MCP message
        MCP->>Validator: Check code safety
        
        alt Code contains blocked function
            Validator-->>MCP: ValidationError
            MCP-->>Agent: {status: "failed", error: "ValidationError"}
        else Code is safe
            Validator->>Executor: Execute code
            Executor->>Executor: Inject job context
            Executor->>Engine: eval(code)
            Engine->>Engine: Run in isolated workspace
            Engine-->>Executor: {output, variables, figures}
            Executor->>Agent: {status: "completed", output, ...}
        end
    end
Loading

Testing Security

The test suite includes comprehensive security checks:

# Run all security-related tests
python -m pytest tests/test_security.py -v
python -m pytest tests/test_auth_middleware.py -v
python -m pytest tests/test_tools.py::TestExecuteCodeSecurity -v
python -m pytest tests/test_tools_files.py -v

# Check for code quality issues
ruff check src/

# Audit dependencies for known vulnerabilities
pip-audit

Key test coverage:

  • Blocked function detection (with string/comment stripping)
  • Filename sanitization (path traversal prevention)
  • Bearer token validation (constant-time comparison)
  • HITL approval gate logic
  • Workspace isolation between sessions
  • Upload size enforcement

Security Audit Checklist

Before deploying to production, verify:

  • Authentication enabled: MATLAB_MCP_AUTH_TOKEN is set
  • Reverse proxy configured: Server is not exposed directly to internet
  • Blocklist reviewed: Customize for your use case
  • Session limits enforced: max_sessions and session_timeout configured
  • Upload limits set: max_upload_size_mb is reasonable
  • Monitoring enabled: Access metrics at /health and /metrics endpoints
  • Logs reviewed: Check for errors or suspicious patterns regularly
  • Firewall configured: Only reverse proxy port (443) is exposed to network
  • HITL gates enabled: For high-risk operations (if needed)
  • Dependencies audited: Run pip-audit for known CVEs

Clone this wiki locally