-
Notifications
You must be signed in to change notification settings - Fork 0
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
All HTTP requests (SSE and streamable HTTP transports) require a valid bearer token in the Authorization header:
GET /mcp HTTP/1.1
Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxConfiguration:
# 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:
-
/healthendpoint always bypasses authentication (for load balancer health checks) -
OPTIONSrequests bypass authentication (CORS pre-flight) - stdio transport has no authentication (single-user, inherits shell security)
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.
The SecurityValidator prevents dangerous MATLAB operations by checking code against a blocklist before execution.
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 scriptsThe 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 callCustomizing the blocklist:
security:
blocked_functions_enabled: true
blocked_functions:
- "system"
- "eval"
- "assignin"
- "web"
- "url"
# Add your own blocked patternsError response when blocked:
{
"status": "failed",
"error": "ValidationError",
"message": "Code contains blocked function: 'system'",
"code": "BLOCKED_FUNCTION"
}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 /") # ✗ REJECTEDsecurity:
max_upload_size_mb: 100 # Default: 100 MBUploads exceeding this limit are rejected:
{
"status": "failed",
"error": "ValidationError",
"message": "Upload size 250 MB exceeds limit of 100 MB"
}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 executionApproval flow:
- Agent invokes a tool (e.g.,
execute_code) - If HITL is enabled and the operation is protected, server calls
ctx.elicit() - Human reviews the request in their MCP client UI
- Human approves or denies
- If approved, execution proceeds; if denied, the operation is blocked
Protected operations:
- Code containing protected functions (
protected_functionslist) - 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"
}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:
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:
blocked_functions_enabled: true # ALWAYS: Enable in production
blocked_functions:
- "system"
- "eval"
- "assignin"
# ... (see default blocklist above)
max_upload_size_mb: 100sessions:
max_sessions: 50 # Limit concurrent sessions
session_timeout: 3600 # Idle timeout (1 hour)
job_retention_seconds: 86400 # Keep job metadata for 24 hoursserver:
transport: "stdio" # No network exposure
# No authentication needed — shell session is the security boundaryRecommendation: Use stdio. Your terminal login is the security boundary; MATLAB runs with your user permissions.
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 operationsDeployment steps:
- Run server behind a reverse proxy (nginx, Caddy, Traefik) on
127.0.0.1:8765 - Configure proxy to require authentication (OAuth, LDAP, HTTP Basic)
- Set
MATLAB_MCP_AUTH_TOKENfor agent-to-server communication - 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;
}
}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 metricsDeployment checklist:
- Run behind TLS reverse proxy (nginx, Traefik, etc.)
- Enable authentication on the reverse proxy (OAuth 2.0, LDAP, mTLS)
- Set
MATLAB_MCP_AUTH_TOKENfor 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)
If you discover a security vulnerability in the MATLAB MCP Server, please do not open a public GitHub issue. Instead:
-
Email the maintainer: [security@your-domain.com] with:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if you have one)
-
Include in the subject line:
[SECURITY] MATLAB MCP Server vulnerability -
Allow time for a fix: Expect a response within 48 hours and a patch release within 7 days for critical issues.
-
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).
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
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-auditKey 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
Before deploying to production, verify:
- Authentication enabled:
MATLAB_MCP_AUTH_TOKENis set - Reverse proxy configured: Server is not exposed directly to internet
- Blocklist reviewed: Customize for your use case
- Session limits enforced:
max_sessionsandsession_timeoutconfigured - Upload limits set:
max_upload_size_mbis reasonable - Monitoring enabled: Access metrics at
/healthand/metricsendpoints - 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-auditfor known CVEs