Skip to content

Architecture

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

Architecture

System Overview

The MATLAB MCP Server is a Python-based bridge that exposes MATLAB capabilities to AI agents (Claude, Cursor, Codex CLI, etc.) via the Model Context Protocol. It handles authentication, session isolation, engine pooling, job orchestration, security validation, and result formatting across multiple transport modes.

graph TB
    Agent["AI Agent<br/>(Claude, Cursor, Codex CLI)"]
    
    subgraph Transport["Transport Layer"]
        Stdio["stdio<br/>(stdio)"]
        SSE["SSE<br/>(deprecated)"]
        StreamableHTTP["Streamable HTTP<br/>(recommended)"]
    end
    
    subgraph Auth["Auth Middleware"]
        BearerAuth["BearerAuthMiddleware<br/>(validates Authorization header)"]
    end
    
    subgraph MCPServer["MCP Server<br/>(FastMCP 3.2.0)"]
        Tools["20 Built-in Tools<br/>+ Custom Tools"]
        SessionMgr["Session Manager<br/>(workspace isolation)"]
    end
    
    subgraph Execution["Job Execution Layer"]
        JobExec["Job Executor<br/>(sync/async hybrid)"]
        JobTracker["Job Tracker<br/>(lifecycle tracking)"]
        SecurityVal["Security Validator<br/>(code safety)"]
    end
    
    subgraph Pool["Engine Pool Layer"]
        PoolMgr["EnginePoolManager<br/>(elastic scaling)"]
        Engine1["MATLAB Engine 1"]
        Engine2["MATLAB Engine 2"]
        EngineN["... Engine N"]
    end
    
    subgraph Output["Output & Monitoring"]
        Formatter["Result Formatter<br/>(text, vars, figures)"]
        PlotlyConv["Plotly Converter<br/>(MATLAB → interactive viz)"]
        Metrics["Metrics Collector<br/>(SQLite store)"]
    end
    
    Agent -->|MCP protocol| Transport
    Transport -->|HTTP/SSE| Auth
    Auth -->|ASGI| MCPServer
    Transport -->|stdio| MCPServer
    
    MCPServer --> JobExec
    MCPServer --> SessionMgr
    
    JobExec --> SecurityVal
    SecurityVal --> JobTracker
    JobTracker --> PoolMgr
    
    PoolMgr --> Engine1
    PoolMgr --> Engine2
    PoolMgr --> EngineN
    
    Engine1 -->|code execution| MATLAB["MATLAB Runtime<br/>(R2022b+)"]
    Engine2 --> MATLAB
    EngineN --> MATLAB
    
    JobExec --> Formatter
    Formatter --> PlotlyConv
    PlotlyConv --> Metrics
    Metrics -->|result| Agent
Loading

Major Components

1. Transport Layer

Responsibility: Manage protocol-specific communication channels

  • stdio (single-user, default): Direct stdin/stdout. No auth layer. Used for local development.
  • SSE (multi-user, deprecated): Server-Sent Events over HTTP. Marked deprecated as of v2.0; kept for backward compatibility.
  • Streamable HTTP (multi-user, recommended): FastMCP 3.2+ native transport at /mcp endpoint. Supports multiple concurrent sessions with bearer token auth.

Key Files:

  • src/matlab_mcp/config.py: ServerConfig.transport field (Literal["stdio", "sse", "streamablehttp"])
  • src/matlab_mcp/server.py: main() function with transport branching logic

2. Authentication Middleware

Responsibility: Enforce bearer token auth on HTTP transports (SSE, streamable HTTP)

  • Validates Authorization: Bearer <token> header on every request
  • Returns HTTP 401 with WWW-Authenticate header on invalid/missing token
  • Bypasses auth for /health endpoint (load balancer health checks)
  • Disabled for stdio transport (no HTTP layer)
  • Token read exclusively from MATLAB_MCP_AUTH_TOKEN environment variable at startup
  • Constant-time comparison (hmac.compare_digest) prevents timing attacks

Key Files:

  • src/matlab_mcp/auth/middleware.py: BearerAuthMiddleware (pure ASGI class)
  • src/matlab_mcp/config.py: SecurityConfig (auth configuration)
  • src/matlab_mcp/server.py: Middleware wiring and token generation CLI flag

3. MCP Server

Responsibility: Register and route tool calls to implementations

  • FastMCP 3.2.0 instance with 20 built-in tools + dynamic custom tools
  • @mcp.tool decorators for tool registration
  • Lifespan management (startup, shutdown hooks)
  • Request context includes session ID, client ID, and MCP protocol state

Key Files:

  • src/matlab_mcp/server.py: MatlabMCPServer state container, tool registration
  • src/matlab_mcp/tools/*.py: Tool implementations (core, discovery, files, jobs, admin, monitoring)
  • src/matlab_mcp/tools/custom.py: Dynamic tool loading from custom_tools.yaml

4. Session Manager

Responsibility: Isolate workspace per user session

  • Creates unique session ID for each connected agent (HTTP) or uses default (stdio)
  • Per-session temporary directory for file uploads/downloads
  • Workspace variable clearing between sessions (optional, via config)
  • Idle session cleanup after configurable timeout

Session Routing:

  • stdio: Single "default" session, shared workspace
  • HTTP (SSE/streamablehttp): Session ID from header (mcp-session-id) with fallback to client_id (FastMCP 3.2+)

Key Files:

  • src/matlab_mcp/session/manager.py: SessionManager class
  • src/matlab_mcp/server.py: _get_session_id(), _get_temp_dir() methods

5. Job Executor

Responsibility: Orchestrate MATLAB code execution with hybrid sync/async behavior

  1. Security check: Scan code for blocked functions (custom, configurable blocklist)
  2. Context injection: Add __mcp_job_id__ and __mcp_temp_dir__ to workspace
  3. Execution:
    • Synchronous: Return result immediately if execution completes within sync_timeout (default 30s)
    • Asynchronous: Auto-promote to background job if timeout exceeded; return job_id immediately; agent polls for completion
  4. Progress reporting: Optional mcp_progress() MATLAB helper updates progress files
  5. Result formatting: Structure output (stdout, variables, figures, errors) into response
  6. Cleanup: Release engine back to pool; delete job metadata after retention period

Key Files:

  • src/matlab_mcp/jobs/executor.py: JobExecutor class
  • src/matlab_mcp/jobs/models.py: Job dataclass, JobStatus enum
  • src/matlab_mcp/jobs/tracker.py: JobTracker in-memory registry
  • src/matlab_mcp/matlab_helpers/mcp_progress.m: Progress file writer (called from MATLAB)

6. Engine Pool Manager

Responsibility: Manage lifecycle and availability of MATLAB engine instances

  • Elastic scaling: Start with min_engines (default 1), scale up to max_engines (default 4) under load
  • Proactive warmup: When utilization exceeds threshold (default 80%), start new engine before demand spike
  • Scale-down: Engines idle >15 min are stopped (down to min_engines)
  • Health checks: Periodic 1+1 evaluation to detect dead engines; auto-replace
  • Queue: Requests wait in async queue when all engines busy (configurable max wait)

Engine States:

STOPPED ──start()──> STARTING ──ready──> IDLE ←──release()──┐
                         │                 │                  │
                       error          acquire()              busy
                         │                 ↓                  │
                         └─────> ERROR   (working)  ──done()──┘

Key Files:

  • src/matlab_mcp/pool/manager.py: EnginePoolManager class
  • src/matlab_mcp/pool/engine.py: MatlabEngineWrapper class, EngineState enum

7. Security Validator

Responsibility: Prevent execution of dangerous MATLAB code

  • Function blocklist: Detects and rejects code containing blocked functions
    • Default blocklist: system, unix, dos, ! (shell escape), eval, evalc, evalin, feval, assignin, perl, python
    • Smart parsing: Strips string literals and comments to avoid false positives
    • Configurable via security.blocked_functions in config.yaml
  • Filename sanitization: Prevents path traversal in file operations (upload/delete/read)
  • Upload limits: Enforces max file size and dataset limits

Key Files:

  • src/matlab_mcp/security/validator.py: SecurityValidator class, BlockedFunctionError exception

8. Output Formatting & Visualization

Responsibility: Structure and visualize execution results

Text Output:

  • Truncate large stdout (default 10,000 chars)
  • Save overflow to file and include file path in response

Variables:

  • Display type, shape, value summary of workspace variables
  • Truncate large collections (arrays >1000 elements)

Figures:

  • Auto-detect MATLAB figures via gcf()
  • Extract properties via mcp_extract_props.m (MATLAB-side)
  • Convert to interactive Plotly JSON (Python-side)
  • Support line, scatter, bar, histogram, 3D surface, heatmap, image
  • Preserve style: colors, line styles, markers, legends, labels, fonts
  • WebGL rendering for large point clouds (10k+ points)

Key Files:

  • src/matlab_mcp/output/formatter.py: ResultFormatter class
  • src/matlab_mcp/output/plotly_convert.py: Load Plotly JSON
  • src/matlab_mcp/output/plotly_style_mapper.py: Convert MATLAB styles to Plotly
  • src/matlab_mcp/matlab_helpers/mcp_extract_props.m: Extract figure properties

9. Monitoring & Metrics

Responsibility: Collect system metrics, detect anomalies, provide observability

  • In-memory metrics: Engine pool status, job counts, error rates, execution times
  • Persistent storage: SQLite3 backend for time-series data (24-hour retention default)
  • Health evaluation: Classify server status (healthy/degraded/unhealthy) based on thresholds
  • Dashboard: Web UI at /dashboard with live gauges, time-series charts, event log
  • Monitoring tools: MCP tools expose health, metrics, error logs to agents

Key Files:

  • src/matlab_mcp/monitoring/collector.py: MetricsCollector class
  • src/matlab_mcp/monitoring/store.py: MetricsStore class (SQLite async)
  • src/matlab_mcp/monitoring/health.py: evaluate_health() function
  • src/matlab_mcp/monitoring/dashboard.py: Starlette sub-app with routes
  • src/matlab_mcp/monitoring/static/: Dashboard HTML/CSS/JS frontend

Data Flow: Sync Execution

sequenceDiagram
    agent->>mcp: execute_code("x = magic(3)")
    mcp->>security: validate code (OK)
    mcp->>pool: acquire_engine()
    pool->>engine: is_busy? (no)
    pool-->>mcp: engine granted
    mcp->>executor: execute(code, engine, job_id, temp_dir)
    executor->>executor: inject context (job_id, temp_dir)
    executor->>engine: eval("x = magic(3)")
    engine->>matlab: x = magic(3)
    matlab-->>engine: result (x = 3x3 matrix)
    engine-->>executor: stdout, vars
    executor->>formatter: format result
    formatter-->>executor: response dict
    executor->>pool: release_engine()
    pool-->>mcp: engine released
    mcp-->>agent: {status: "completed", output: "...", variables: {...}}
Loading

Data Flow: Async Promotion

sequenceDiagram
    agent->>mcp: execute_code("long_sim = monte_carlo(1e6)")
    mcp->>security: validate code (OK)
    mcp->>pool: acquire_engine()
    pool-->>mcp: engine granted
    mcp->>executor: execute(code, engine, job_id, temp_dir)
    executor->>engine: eval(code, background=True)
    engine->>matlab: long_sim = monte_carlo(1e6) [bg]
    matlab-->>engine: job_id (continue in background)
    executor->>tracker: mark job RUNNING
    executor-->>mcp: {job_id: "j-123", status: "running"}
    mcp-->>agent: {job_id: "j-123", status: "running"}
    
    par Background Execution
        engine->>matlab: [job running...]
        matlab->>matlab: [progress via mcp_progress.m]
    and Agent Polling
        agent->>mcp: get_job_status("j-123")
        mcp->>tracker: query job
        tracker-->>mcp: {status: "running", progress: 45%}
        mcp-->>agent: {status: "running", progress: 45%}
        agent->>mcp: get_job_status("j-123")
        mcp->>tracker: query job
        tracker-->>mcp: {status: "running", progress: 90%}
        mcp-->>agent: {status: "running", progress: 90%}
    end
    
    engine->>tracker: mark job COMPLETED
    agent->>mcp: get_job_result("j-123")
    mcp->>tracker: query job
    tracker-->>mcp: {status: "completed", result: {...}}
    mcp->>pool: release_engine()
    pool-->>mcp: engine released
    mcp-->>agent: {status: "completed", result: {...}}
Loading

Key Design Decisions

1. Authentication: Bearer Tokens, Not OAuth

Decision: Static bearer tokens in environment variable, constant-time comparison, 401 on invalid

Rationale:

  • Agents (Claude Code, Codex CLI, Cursor) natively support Authorization: Bearer <token> headers
  • OAuth requires browser redirect → incompatible with CLI agents
  • Corporate firewalls often block callback URLs
  • For internal team tools, static tokens are the MCP spec recommendation
  • Can be rotated by restarting with new MATLAB_MCP_AUTH_TOKEN

Trade-off: No per-user audit trail (all agents use same token). Mitigated by reverse proxy auth for production.

2. Hybrid Sync/Async Execution

Decision: Synchronous by default (fast path), auto-promote to async if timeout exceeded

Rationale:

  • Agent UX: Immediate results for quick commands (<30s) are better than round-trip latency
  • Resource efficiency: Long jobs don't block the engine pool while waiting
  • Compatibility: Agents can choose sync or async based on expected duration

Trade-off: Requires job tracking and polling. Mitigated by simple job tracker and progress reporting.

3. Per-Session Temp Directories (HTTP)

Decision: Each HTTP session gets isolated temp dir; stdio uses single shared dir

Rationale:

  • Prevents accidental file leaks between users
  • Automatic cleanup on session expiration
  • Simplifies file permission model (no per-user MATLAB workspaces)

Trade-off: Large files uploaded to one session not visible to another. By design.

4. Elastic Engine Pool

Decision: Start small (1 engine), scale up to max on demand, scale down when idle

Rationale:

  • Reduces resource overhead for light workloads
  • Handles traffic spikes without request loss
  • Proactive warmup avoids cold-start delays

Trade-off: Pool startup may delay first request. Mitigated by pre-warming in lifespan hook.

5. Streamable HTTP as Primary Remote Transport

Decision: Replace SSE with FastMCP 3.2's /mcp endpoint; keep SSE deprecated for compat

Rationale:

  • SSE is officially deprecated (as of v2.0)
  • Codex CLI only supports streamable HTTP
  • Cleaner protocol (single POST/GET, bidirectional framing via SSE)
  • Built into FastMCP 3.2+ with native session routing

Trade-off: Existing SSE integrations need migration guidance. Documented in wiki.

Component Communication

graph LR
    Server["MCP Server<br/>(FastMCP)"]
    SessionMgr["Session Manager"]
    JobExec["Job Executor"]
    SecurityVal["Security Validator"]
    JobTracker["Job Tracker"]
    PoolMgr["Engine Pool<br/>Manager"]
    Engine["MATLAB<br/>Engine"]
    Formatter["Result<br/>Formatter"]
    Metrics["Metrics<br/>Collector"]
    
    Server -->|create/get session| SessionMgr
    Server -->|validate code| SecurityVal
    Server -->|execute job| JobExec
    
    JobExec -->|create job| JobTracker
    JobExec -->|acquire/release engine| PoolMgr
    JobExec -->|format result| Formatter
    JobExec -->|record metrics| Metrics
    
    PoolMgr -->|eval code| Engine
    JobTracker -->|query status| JobTracker
    
    SessionMgr -.->|temp dir path| JobExec
    SecurityVal -.->|OK/BLOCKED| JobExec
    Formatter -.->|formatted response| Server
    Metrics -.->|health data| Server
Loading

Configuration Flow

config.yaml (default values)
    ↓
MATLAB_MCP_* environment variables (overrides)
    ↓
Pydantic models in config.py
    ↓
AppConfig instance in MatlabMCPServer
    ↓
Sub-components (pool, executor, sessions, security, monitoring)

Key config sections:

  • server: transport, host, port, logging
  • pool: min/max engines, health check interval
  • execution: sync timeout, workspace isolation
  • security: blocked functions, upload limits, code quality
  • output: text truncation, Plotly styling
  • monitoring: metrics retention, dashboard
  • session: idle timeout, max concurrent sessions
  • hitl (optional): human-in-the-loop approval gates

Testing Strategy

  • Unit tests (733 tests): Mock MATLAB engine, test each component in isolation
  • Integration tests (end-to-end): Start server subprocess, connect real MCP client via HTTP, verify tools
  • CI/CD: Linux (Python 3.10, 3.12), Windows (x64), macOS (x86_64, arm64)
  • Markers: @pytest.mark.matlab (requires MATLAB), @pytest.mark.integration (requires live server)

Clone this wiki locally