-
Notifications
You must be signed in to change notification settings - Fork 0
Security
The MATLAB MCP Server implements a multi-layered security architecture to prevent misuse while keeping MATLAB accessible to AI agents. This document covers authentication, authorization, input validation, secure configuration, deployment best practices, and vulnerability reporting.
The server supports two transport modes with different security implications:
- Use case: Local development, single-user deployments
- Authentication: Inherited from the client application (Claude Desktop, Cursor, etc.)
- Authorization: No multi-user isolation; all code runs in the same MATLAB process
- Recommendation: Suitable for personal/developer use only
- Use case: Multi-user team deployments, shared server scenarios
- Authentication: Must be deployed behind a reverse proxy (nginx, Caddy, Traefik) with authentication layer
- Authorization: Session-based; each user gets isolated temporary directories and workspace separation
-
Configuration flag: Set
require_proxy_auth: trueto acknowledge auth is configured
security:
require_proxy_auth: true # Required for SSE transport
server:
transport: "sse"
host: "127.0.0.1" # Bind to localhost only; expose via proxy
port: 8765Warning: The server logs a startup warning if SSE is enabled without require_proxy_auth: true.
Each user session receives:
- Unique session ID (auto-generated UUID)
-
Isolated temp directory (
./temp/{session_id}/) for file uploads/downloads -
Separate workspace context via
workspace_isolationsetting -
Automatic expiration after
session_timeoutseconds of inactivity (default 3600s) - Cleanup of temp files and job metadata on session end
sessions:
max_sessions: 50 # Limit concurrent sessions
session_timeout: 3600 # 1 hour idle timeout
job_retention_seconds: 86400 # Keep completed job metadata for 24 hoursgraph LR
A["AI Agent<br/>(Claude, Cursor)"] -->|stdio or SSE| B["MCP Server"]
B -->|Creates| C["Session ID"]
C -->|Isolates| D["Temp Directory<br/>/temp/{session_id}/"]
C -->|Isolates| E["MATLAB Workspace<br/>clear all between sessions"]
D --> F["User Files<br/>uploads/downloads"]
E --> G["User Variables<br/>functions, state"]
The server enforces a configurable blocklist of dangerous MATLAB functions. By default, these are blocked:
| Function | Risk | Alternative |
|---|---|---|
system() |
Execute arbitrary OS commands | Use MATLAB System Control Toolbox, or request administrator approval |
unix() |
Execute Unix commands | Same as above |
dos() |
Execute DOS/Windows commands | Same as above |
! |
Shell escape operator | Same as above |
eval() |
Execute arbitrary string as code | Use evalc() (if allowed) or structured APIs |
feval() |
Call function by name string | Use function handles or structured APIs |
evalc() |
Evaluate and capture output | Use direct function calls where possible |
evalin() |
Evaluate in caller/base workspace | Use proper scoping/module structure |
assignin() |
Assign variable in caller/base workspace | Use return values or output arguments |
perl() |
Execute Perl scripts | Blocked for same reasons as system/unix/dos |
python() |
Execute Python scripts | Blocked for same reasons as system/unix/dos |
The security validator strips string literals and comments before checking for blocked functions, preventing false positives:
% These are SAFE and will NOT trigger the blocklist:
disp('The operating system is great') % "system" inside a string
% system('ls') % "system" inside a comment
msg = "unix-based systems are wonderful"; % "unix" inside a string
help system % "system" as help topic
% These WILL be blocked:
system('rm -rf /') % Actual system() call
x = eval('1 + 2') % Actual eval() call
feval('disp', 'hello') % Actual feval() callThe validator:
- Removes all single/double-quoted string literals
- Removes all MATLAB comments (from
%to end of line) - Uses precompiled regex patterns to match function calls:
func(),func arg, or shell escape!cmd - Raises
BlockedFunctionErrorif any match is found - Can be disabled entirely with
blocked_functions_enabled: false
When uploading files, the server sanitizes filenames to prevent path traversal and injection attacks:
# Allowed characters: [a-zA-Z0-9._-]
# Rejected patterns:
"../../etc/passwd" # Path traversal
"/etc/shadow" # Absolute path
"file;rm -rf /" # Shell injection
"file\x00.txt" # Null byte injectionValid filenames:
"data.csv"
"experiment-2024-01-15.mat"
"results_final_v2.xlsx"
"plot_sinwave.png"
Configuration:
security:
max_upload_size_mb: 100 # Size limit (default 100MB)Files are always uploaded to the session's isolated temp directory, not to arbitrary locations.
When calling discovery tools (list_functions, get_help, list_toolboxes), the server validates MATLAB names to prevent command injection:
% Valid names (accepted):
fft, plot, Signal Processing, pkg.subpkg.func
% Invalid names (rejected):
;drop table, func;, !ls, @decorator, (expr)security:
blocked_functions_enabled: true
# Default blocklist (example; customize as needed):
blocked_functions:
- "system"
- "unix"
- "dos"
- "!"
- "eval"
- "feval"
- "evalc"
- "evalin"
- "assignin"
- "perl"
- "python"To allow a blocked function (e.g., for a trusted use case):
- Remove it from the
blocked_functionslist, OR - Disable blocking entirely:
blocked_functions_enabled: false
execution:
workspace_isolation: trueWhen enabled (default), the server executes these commands at the start of each new session:
clear all; % Remove all variables
clear global; % Remove global variables
clear functions; % Clear function cache
fclose all; % Close all file handles
restoredefaultpath; % Restore default MATLAB pathThis ensures one user's variables, functions, and file handles don't leak to another user.
security:
max_upload_size_mb: 100 # Maximum file size (default 100MB)Attempting to upload a file larger than this limit returns:
{
"status": "failed",
"error": {
"type": "UploadError",
"message": "File size (150 MB) exceeds maximum allowed (100 MB)"
}
}sessions:
max_sessions: 50 # Maximum concurrent sessionsWhen the limit is reached, new session creation fails until idle sessions expire or are manually closed.
server:
transport: "stdio" # Single-user, no network exposure
security:
blocked_functions_enabled: true # Keep defaults
require_proxy_auth: false # N/A for stdio
execution:
workspace_isolation: true # Still recommendedSetup:
matlab-mcp --config config.yamlserver:
transport: "sse"
host: "127.0.0.1" # Bind to localhost only
port: 8765
security:
blocked_functions_enabled: true
require_proxy_auth: true # Acknowledge auth is configured
max_upload_size_mb: 500 # Adjust for team use
sessions:
max_sessions: 20
session_timeout: 7200 # 2 hours
execution:
workspace_isolation: trueSetup (with nginx reverse proxy):
server {
listen 8080 ssl;
server_name matlab.internal.company.com;
ssl_certificate /etc/ssl/certs/cert.pem;
ssl_certificate_key /etc/ssl/private/key.pem;
auth_basic "MATLAB Server";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:8765;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
proxy_http_version 1.1;
proxy_set_header Connection "upgrade";
proxy_set_header Upgrade $http_upgrade;
}
}Then connect clients to https://matlab.internal.company.com:8080 instead of the raw MCP server.
In addition to team best practices:
server:
transport: "sse"
host: "127.0.0.1"
port: 8765
log_level: "warning" # Reduce log verbosity
security:
blocked_functions_enabled: true
require_proxy_auth: true
max_upload_size_mb: 500 # Enforce reasonable limits
sessions:
max_sessions: 100
session_timeout: 3600 # 1 hour, enforced
job_retention_seconds: 86400 # Purge old jobs
monitoring:
enabled: true # Enable metrics
db_path: "/var/log/matlab-mcp/metrics.db"
pool:
min_engines: 4
max_engines: 16
health_check_interval: 60 # Monitor engine healthAdditional infrastructure:
- TLS/SSL termination: Use a reverse proxy with valid certificates (Let's Encrypt)
- Authentication: OAuth2, LDAP, or static htpasswd
- Authorization: Role-based access control in the proxy (if needed)
- Network: Isolate to internal network; do NOT expose to the internet without auth
- Logging: Send server logs to a centralized log aggregation system
-
Monitoring: Use
/healthand/metricsendpoints for alerting -
Backup: Regularly backup
monitoring/metrics.dband config files - Updates: Subscribe to security advisories and update promptly
For highly sensitive environments, extend the blocklist:
security:
blocked_functions_enabled: true
blocked_functions:
- "system"
- "unix"
- "dos"
- "!"
- "eval"
- "feval"
- "evalc"
- "evalin"
- "assignin"
- "perl"
- "python"
- "web" # Prevent browser access
- "delete" # Prevent file deletion
- "rmdir" # Prevent directory deletion
- "copyfile" # Prevent file manipulation
- "movefile" # Prevent file manipulation
- "feature" # Prevent feature toggling
- "ver" # Prevent version/toolbox discovery (optional)graph TB
Client["AI Agent<br/>(Claude, etc.)"]
Client -->|MCP Protocol| Transport{Transport Mode}
Transport -->|stdio| Local["Local Execution<br/>Single user"]
Transport -->|SSE| Proxy["Reverse Proxy<br/>Auth + TLS"]
Proxy -->|Authenticated<br/>Request| Server["MCP Server<br/>FastMCP"]
Local --> Server
Server -->|Extracts| SID["Session ID<br/>from context"]
SID -->|Creates| TempDir["Temp Directory<br/>/temp/{sid}/"]
Server -->|Validates| Code["Code"]
Code -->|Check Blocklist| SVG["Security Validator"]
Code -->|Sanitize Names| Names["Names"]
SVG -->|Pass| Executor["Job Executor"]
SVG -->|Fail| Error["BlockedFunctionError"]
Executor -->|Acquire| Pool["Engine Pool<br/>Manager"]
Pool -->|Engine +<br/>Workspace| Execute["Execute Code<br/>in MATLAB"]
Execute -->|Capture| Output["stdout/stderr"]
Execute -->|Return| Result["Job Result"]
Result -->|Format| Formatter["Result Formatter"]
Formatter -->|Truncate/Save| Response["MCP Response"]
Response --> Client
TempDir -->|Upload| Files["Session Files<br/>Isolated"]
Files -->|Read| Read["read_script,<br/>read_image,<br/>read_data"]
Read --> Response
Error --> Response
If you discover a security vulnerability in the MATLAB MCP Server:
- Do NOT open a public GitHub issue — this exposes the vulnerability to potential attackers
-
Report privately by:
- Emailing the maintainer at the address listed in
glama.jsonor repository profile - Using GitHub's private vulnerability disclosure feature (if available)
- Emailing the maintainer at the address listed in
-
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if you have one)
- Allow time for response — typically 7–14 days for a patch
Example vulnerability report:
Title: Blocklist bypass via string concatenation
Description: The security validator does not strip escaped quotes, allowing code like
eval('x' + y)to bypass theeval()blocklist.Steps to reproduce:
- Enable security validator
- Execute code:
eval(['x = 1'])- Expected: Blocked. Actual: Executed.
Suggested fix: Handle string concatenation patterns in the regex.
| Layer | Mechanism | Configuration |
|---|---|---|
| Transport | stdio (local) or SSE (networked) | server.transport |
| Authentication | Client app (stdio) or reverse proxy (SSE) | security.require_proxy_auth |
| Authorization | Session isolation + workspace clearing | execution.workspace_isolation |
| Code Validation | Function blocklist + smart scanning | security.blocked_functions |
| Input Sanitization | Filename validation, MATLAB name validation | Built-in validators |
| Resource Limits | Upload size, session count, execution timeout |
security.max_upload_size_mb, sessions.max_sessions, execution.sync_timeout
|
| Session Management | Unique IDs, temp directories, automatic cleanup |
sessions.session_timeout, execution.temp_cleanup_on_disconnect
|