-
Notifications
You must be signed in to change notification settings - Fork 0
FAQ
# FAQ
## General / Overview
### What is the MATLAB MCP Server?
The MATLAB MCP Server connects AI agents (like Claude, Cursor, or GitHub Copilot) to MATLAB via the Model Context Protocol (MCP). It lets agents execute MATLAB code, discover toolboxes and functions, generate plots, manage files, and track long-running jobs—all through a standardized protocol.
### What MATLAB versions are supported?
**MATLAB 2020b and later**. The MATLAB Engine API for Python must be installed separately from your MATLAB installation. See [[Installation]] for setup instructions.
### What programming languages does the server support?
The server itself is written in **Python**. It controls MATLAB engines and exposes tools via MCP. MATLAB code executed through the server can use any MATLAB syntax, including calls to toolboxes like Signal Processing, Statistics, Deep Learning, and more.
### Does it work without MATLAB installed?
**No.** The server requires a local MATLAB installation with the Engine API. It connects to real MATLAB engines, not a simulator. For testing without MATLAB, the project includes a mock engine for CI (see `tests/mocks/matlab_engine_mock.py`).
### What license do I need?
You must have a valid MATLAB license (individual, team, or concurrent). The server does not provide licensing — it uses your existing MATLAB installation.
### What AI agents work with this?
Any agent that supports the **Model Context Protocol (MCP)**:
- **Claude Desktop** (Mac/Windows with MCP support)
- **Cursor** (with MCP integration)
- **GitHub Copilot** (when MCP support is available)
- **Custom agents** built with MCP SDKs (Python, JavaScript, etc.)
See [[Installation]] for integration steps.
### Can I install it from PyPI?
**Yes.** The package is available on PyPI as `matlab-mcp-python`:
```bash
pip install matlab-mcp-python
matlab-mcp # Start the serverYou still need the MATLAB Engine API installed separately. See Installation.
The MATLAB MCP Server is licensed under the MIT License. MATLAB and its toolboxes are proprietary and subject to MathWorks licensing.
Open an issue or pull request on GitHub. See the README for contribution guidelines.
macOS:
cd /Applications/MATLAB_R2024a.app/extern/engines/python
pip install .Windows (Command Prompt):
cd "C:\Program Files\MATLAB\R2024a\extern\engines\python"
pip install .Adjust the path for your MATLAB version. If installation fails, ensure Python 3.10+ is in your PATH and matches your MATLAB Engine API requirements.
See Installation for detailed step-by-step instructions.
Yes. The Engine API must be installed into the same virtual environment where matlab-mcp-python is installed:
python -m venv venv
source venv/bin/activate # macOS/Linux
# or
venv\Scripts\activate # Windows
pip install matlab-mcp-python
cd /path/to/matlab/extern/engines/python
pip install .| Transport | Use Case | Users |
|---|---|---|
| stdio | Single user, agent launches server directly | 1 |
| SSE (HTTP) | Multi-user, shared server, persistent connection | Many |
stdio is simpler for personal use. SSE is better for team deployments where multiple agents connect to a single server instance.
See Configuration for how to switch transports.
- Start the server (it will print stdio port info):
matlab-mcp
- On macOS, edit
~/Library/Application Support/Claude/claude_desktop_config.json:{ "mcpServers": { "matlab": { "command": "matlab-mcp", "args": [] } } } - Restart Claude Desktop.
See Installation for Cursor, Code, and Docker setups.
Use SSE transport on a remote machine:
# On remote server
matlab-mcp --config config.yaml
# (config has server.transport: "sse", server.port: 8765)Important: Always put the server behind an authenticating reverse proxy (nginx, Caddy, etc.) for production. See Security.
Use the provided install.bat (Windows) or manually download wheels via:
pip download -r requirements.txt -d vendor/Then install from the local directory:
pip install --no-index --find-links vendor/ matlab-mcp-pythonSee the project's install.bat script for a complete offline setup example.
Common issues and fixes:
| Error | Cause | Fix |
|---|---|---|
ModuleNotFoundError: No module named 'matlab' |
Engine API not installed | Install Engine API from MATLAB directory |
python --version shows wrong Python |
Multiple Python versions installed | Use full path to correct Python (e.g., /usr/bin/python3.11) |
macOS: Library not in rpath
|
MATLAB dylib path issue | See Troubleshooting |
Windows: 'matlab-mcp' is not recognized
|
Scripts not in PATH | Add Python\Scripts\ to PATH or use python -m matlab_mcp.cli
|
See Troubleshooting for more detailed debugging steps.
| Scenario | Recommended | Notes |
|---|---|---|
| Personal use | 1–2 | One per agent or workload type |
| Small team (2–5) | 2–4 | One per concurrent user, with queuing |
| Larger team | 4–16 | Scale based on concurrent usage & license limit |
| macOS | ≤ 4 | OS/MATLAB limitation on concurrent engines |
Each engine is an independent MATLAB process consuming ~500MB–2GB memory.
- Per engine: ~500MB–2GB (depending on loaded toolboxes)
- Server process: ~50–100MB
- Monitoring (optional): ~10–50MB for metrics database
Example: 4 engines + server ≈ 2–10GB memory.
The EnginePoolManager maintains between min_engines and max_engines:
-
Startup: Starts
min_enginesin parallel -
Under load: If all engines are busy and
total < max_engines, a new engine is started -
Idle: Engines idle longer than
scale_down_idle_timeout(default 900s) are stopped -
Proactive warmup: When utilization exceeds
proactive_warmup_threshold(default 80%), a new engine is pre-warmed
See Configuration for tuning these parameters.
Requests queue up (default queue_max_size: 50). The pool attempts to scale up. If the queue fills, new requests are rejected with a 503 error.
To reduce queueing:
- Increase
max_engines - Reduce
sync_timeoutto promote long jobs to async faster - Split large computations across multiple jobs
execution:
sync_timeout: 10 # Fast timeout promotes to async
max_execution_time: 3600 # 1 hour max per job
pool:
min_engines: 2
max_engines: 8 # Scale to handle concurrency
output:
large_result_threshold: 50000 # Save large results to temp files
max_inline_text_length: 100000Use async execution for long computations and read results back via read_data.
Enable the monitoring dashboard:
monitoring:
enabled: true
sample_interval: 10 # Seconds between samplesAccess /dashboard to see:
- Average execution latency
- p95 execution latency (95th percentile)
- Pool utilization over time
- Job throughput (jobs/minute)
See Configuration for more monitoring options.
Option 1: Custom Tools (recommended)
Define functions in custom_tools.yaml:
custom_tools:
- name: "analyze_signal"
description: "FFT analysis of a signal"
function: "signal_processing.analyze" # my_package.m_function
parameters:
- name: "signal"
type: "list"
description: "Signal array"
- name: "fs"
type: "float"
description: "Sampling frequency"
default: 1000.0
return_type: "dict"
return_description: "FFT magnitude and frequency"The function becomes a first-class MCP tool. See Custom Tools.
Option 2: Path Configuration
Add function directories to config:
execution:
workspace:
default_paths:
- "/path/to/my_functions"
- "/path/to/my_package"Then call functions via execute_code:
result = my_analyze_signal(signal, fs);The server can be called from CI/CD via the stdio transport:
# GitHub Actions example
- name: Run MATLAB job
env:
MATLAB_CODE: |
result = run_optimization();
save('result.mat', 'result');
run: |
matlab-mcp << 'EOF'
${{ env.MATLAB_CODE }}
EOFOr via SSE if the server is always running:
curl -X POST http://localhost:8765/execute \
-d '{"code": "result = compute();"}'Use the stdio transport to send MATLAB commands from Python:
import subprocess
import json
code = "x = linspace(0,2*pi,100); y = sin(x); disp(max(y));"
result = subprocess.run(["matlab-mcp"], input=code, capture_output=True, text=True)
print(result.stdout)Or set up a persistent SSE connection for repeated calls.
GitHub Copilot Chat (with MCP support): Yes, once GitHub enables MCP in Copilot. Follow the same setup as Claude Desktop.
GitHub Copilot for Individuals: Currently uses Codex, not MCP. This may change in future releases.
Check GitHub's MCP roadmap for updates.
Use the MCP SDKs:
Python:
import asyncio
from mcp import ClientSession, StdioTransport
async def main():
async with StdioTransport("matlab-mcp") as transport:
async with ClientSession(transport) as session:
tools = await session.list_tools()
for tool in tools:
print(tool.name)
asyncio.run(main())JavaScript:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({ command: "matlab-mcp" });
const client = new Client({ name: "my-agent", version: "1.0.0" }, {
capabilities: {}
});
await client.connect(transport);
const tools = await client.listTools();See MCP documentation for more.
The server uses hybrid sync/async execution:
-
Sync path (fast): Code finishes within
sync_timeout(default 30s) → result returned immediately -
Async path (slow): Code exceeds timeout → job promoted to background, returns
job_idimmediately
graph LR
A["execute_code(code)"] --> B{"Finishes<br/>within timeout?"}
B -->|Yes| C["Return result<br/>status: completed"]
B -->|No| D["Promote to async<br/>Return job_id"]
D --> E["Poll get_job_status<br/>or get_job_result"]
E --> F["Job completes<br/>in background"]
Use the mcp_progress() MATLAB helper in your code:
for trial = 1:1000000
% ... computation ...
if mod(trial, 100000) == 0
% Report 30% progress with message
mcp_progress(__mcp_job_id__, 30, sprintf('Trial %d/1000000', trial));
end
endThe agent can poll get_job_status to see progress:
{
"job_id": "abc123",
"status": "running",
"progress": 30.0,
"message": "Trial 300000/1000000",
"elapsed_seconds": 45.2
}See Async Jobs for detailed examples.
Configured by execution.max_execution_time (default 86400s = 24 hours). Jobs exceeding this are cancelled.
Yes, use cancel_job:
await client.call_tool("cancel_job", {"job_id": "abc123"})The job transitions to CANCELLED status. If it's running in MATLAB, the engine is interrupted (best-effort).
Jobs are kept for sessions.job_retention_seconds (default 86400 = 24 hours) after completion. Older jobs are pruned. To keep results longer, read them and save to a file.
Define it in custom_tools.yaml:
custom_tools:
- name: "fit_model"
description: "Fit a linear regression model"
function: "my_package.fit_linear" # my_package/fit_linear.m
parameters:
- name: "X"
type: "list"
description: "Feature matrix"
- name: "y"
type: "list"
description: "Target vector"
return_type: "dict"
return_description: "Model coefficients and R²"The function becomes a tool. When called, X and y are passed as arguments, and the return value is serialized to JSON.
See Custom Tools for full schema and examples.
Yes, as long as they're on the MATLAB path. Add their directory to workspace.default_paths:
execution:
workspace:
default_paths:
- "/path/to/mex_compiled_bin"Then call them via execute_code or define a custom tool wrapper.
Use the list_toolboxes tool:
toolboxes = await client.call_tool("list_toolboxes", {})
# Returns: ["Signal Processing", "Statistics and Machine Learning", ...]Filtered by security.allowed_toolboxes (if configured).
Yes. In config:
security:
allowed_toolboxes:
- "Signal Processing Toolbox"
- "Statistics and Machine Learning Toolbox"Only these toolboxes appear in list_toolboxes. Functions from restricted toolboxes cannot be called.
Yes, via the description and return_description fields in custom_tools.yaml. The agent sees this documentation when inspecting the tool schema.
Files are written to the session's temp directory (temp_dir in config, default /tmp/matlab-mcp-<session-id>/). You can:
-
List files:
list_filestool -
Read files:
read_data(for data files),read_script(for.mfiles),read_image(for images) - Download results: Read the file and save it locally
When the session ends, temp files are deleted (configurable via execution.temp_cleanup_on_disconnect).
Yes! MATLAB figures are automatically converted to interactive Plotly JSON. Agents and web-based clients see fully zoomable, pannable, interactive charts.
A static PNG fallback is also generated (configurable DPI via output.static_image_dpi).
- Line plots
- Scatter plots
- Bar charts
- Histograms
- Surface (3D)
- Heatmaps
- Images
Complex custom graphics or specialty plots may fall back to static PNG.
The server auto-converts MATLAB styling (colors, line styles, markers, legends, grid) to Plotly equivalents. Some MATLAB-specific styles may not have Plotly equivalents and are approximated.
To customize, either:
- Style in MATLAB: Use standard MATLAB plot properties
- Post-process in Python: Extract Plotly JSON and modify
Via the Plotly UI in web clients (download as PNG, SVG, etc.). Programmatically, request the Plotly JSON and process it.
Use read_data:
# Summary mode (default) — shows variable info
result = await client.call_tool("read_data", {"filename": "data.mat"})
# Returns: variables list, sizes, types
# Raw mode — returns base64-encoded file content
result = await client.call_tool("read_data", {
"filename": "data.mat",
"format": "raw"
})
# Decode and process locallyFor .csv, .txt, .json: returns text content directly.
The upload_data tool accepts base64-encoded files up to security.max_upload_size_mb (default 100MB). For very large files:
- Split into chunks and upload separately
- Use shared network drives (if available) and reference paths
- Stream data via repeated MATLAB calls
For SSE transport:
- Always put behind an authenticating reverse proxy (nginx, Caddy, etc.)
- Set
require_proxy_auth: truein config - Bind to
127.0.0.1(localhost) if proxy is on same machine - Use HTTPS/TLS for remote connections
Without these precautions, users can execute arbitrary MATLAB code on your server.
For stdio transport:
Stdio is inherently single-user and process-spawned. Not exposed over network.
See Security for detailed SSE setup.
By default:
| Blocked | Reason |
|---|---|
system(), unix(), dos()
|
Execute OS commands |
! (shell escape) |
Execute OS commands |
eval(), feval(), evalc(), evalin()
|
Execute arbitrary code strings |
assignin() |
Modify caller's workspace |
perl(), python()
|
Execute external scripts |
The validator uses smart scanning—it strips string literals and comments to avoid false positives:
disp("The system is down") % "system" in string — SAFE ✓
system("ls") % Actual system() — BLOCKED ✗Yes:
security:
blocked_functions_enabled: true
blocked_functions:
- "system"
- "eval"
- "web" # Add as needed
# Remove entries to allow themOr disable entirely (not recommended):
security:
blocked_functions_enabled: false # Dangerous!Yes. When execution.workspace_isolation: true (default), the workspace is fully cleared between sessions:
clear all
clear global
clear functions
fclose all
restoredefaultpathVariables, functions, and file handles from one user don't leak to another.
For stdio: The agent launcher handles auth (Claude Desktop, Cursor, etc.).
For SSE:
- Put behind reverse proxy with auth (OAuth, LDAP, Basic Auth, etc.)
- Set
require_proxy_auth: truein config - Server logs warning if SSE enabled without this flag
Example (nginx):
location / {
auth_basic "MATLAB MCP";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://localhost:8765;
}server:
log_level: "debug" # "info", "debug", "warning", "error"Logs appear in console or configured log file.
pip install -e ".[dev]"
pytest tests/ -vTests use a mock MATLAB engine—no MATLAB installation required. To run only integration tests (requires MATLAB):
pytest tests/ -v -m matlab-
Implement in
src/matlab_mcp/tools/ -
Register in
src/matlab_mcp/server.pywith@mcp.tooldecorator -
Test in
tests/
Example:
@mcp.tool
async def my_tool(param1: str, param2: int = 10) -> dict:
"""Tool description."""
# Implementation
return {"result": value}Yes! Open an issue or PR on GitHub. See the README for guidelines.
The project uses GitHub Actions:
-
.github/workflows/ci.yml: Runs on every push/PR — linting, pytest, code coverage, Windows/macOS tests -
.github/workflows/docs.yml: Auto-generates wiki on successful CI -
.github/workflows/release.yml: Creates draft release with changelog on git version tag -
.github/workflows/publish.yml: Publishes to PyPI on GitHub release
See the workflow files for details.
See Architecture. It includes diagrams showing:
- Top-level system flow (agent → MCP server → engines → MATLAB)
- Engine pool manager lifecycle
- Job executor sync/async behavior
- Result formatting and Plotly conversion
Quick links to common config options:
| Use Case | Config |
|---|---|
| Single user | examples/config_minimal.yaml |
| Multi-user, team server | examples/config_multiuser.yaml |
| Max performance | Increase pool.max_engines, reduce execution.sync_timeout
|
| Monitoring/metrics | Enable monitoring.enabled: true
|
| Custom tools | Define in custom_tools.yaml
|
| Remote deployment | Use server.transport: "sse" + reverse proxy |
See Configuration for full reference.
| Error | Cause | Solution |
|---|---|---|
ModuleNotFoundError: No module named 'matlab' |
Engine API not installed | Install Engine API from MATLAB directory |
MATLAB Engine is not alive |
Engine crashed or timed out | Check logs, restart server |
Blocked function detected: system |
Security violation | Remove function or customize blocklist |
Job timeout after 30s |
Code too slow | Increase execution.sync_timeout or optimize code |
All engines busy, queue full |
High concurrency | Increase pool.max_engines or add server instances |
FileNotFoundError: data.mat |
File not found in session temp dir | Use upload_data to upload file first |
See Troubleshooting for more.
graph TB
A["Agent<br/>(Claude, Cursor)"] -->|MCP Protocol| B["MATLAB MCP Server"]
B -->|Create Job| C["Job Tracker"]
B -->|Acquire Engine| D["Engine Pool Manager"]
D -->|Execute Code| E["MATLAB Engine"]
E -->|Workspace I/O| F["Session Temp Dir"]
B -->|Format Results| G["Result Formatter"]
G -->|Convert Figures| H["Plotly Converter"]
B -->|Track Metrics| I["Metrics Collector"]
I -->|Store| J["Metrics DB"]
B -->|Validate Code| K["Security Validator"]
For more details, see:
- Installation — Step-by-step setup
- Configuration — Complete YAML reference
- Custom Tools — Define your own tools
- Async Jobs — Long-running computations
- Architecture — System design
- Security — Protecting MATLAB
- Troubleshooting — Common issues