Skip to content

Architecture

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

Architecture

System Overview

AI Agent (Claude Code, Codex CLI, Cursor, etc.)
       │
       │ MCP Protocol
       │ (stdio, SSE, or streamable HTTP)
       ▼
┌─────────────────────────────────────────┐
│   FastMCP 3.2.0 Server                  │
│   ├─ 20 built-in MCP tools              │
│   ├─ Custom tools (from YAML)           │
│   ├─ Human-in-the-loop approval gates   │
│   ├─ Bearer token authentication        │
│   └─ Session & context management       │
└──────────┬──────────────────────────────┘
           │
┌──────────▼──────────────────────────────┐
│   Job Executor                           │
│   ├─ Hybrid sync/async execution         │
│   ├─ Timeout-based async promotion       │
│   ├─ Progress tracking (mcp_progress)    │
│   └─ Result formatting & Plotly convert  │
└──────────┬──────────────────────────────┘
           │
┌──────────▼──────────────────────────────┐
│   Engine Pool Manager                    │
│   ├─ Elastic scaling (min → max engines) │
│   ├─ Health checks & proactive warmup    │
│   ├─ Idle scale-down                     │
│   └─ Queue-based acquisition             │
└──────────┬──────────────────────────────┘
           │
┌──────────▼──────────────────────────────┐
│   MATLAB Engine Pool                     │
│   (R2022b+ with Engine API)              │
│   Engine 1 │ Engine 2 │ ... │ Engine N   │
└────────────────────────────────────────┘

Major Components & Responsibilities

1. Server & MCP Protocol (src/matlab_mcp/server.py, FastMCP 3.2.0)

Responsibilities:

  • Register and dispatch all 20 built-in MCP tools
  • Load and register custom tools from custom_tools.yaml
  • Manage server lifecycle (startup, shutdown, graceful drain)
  • Run background tasks (health checks, session cleanup, job pruning)
  • Support three transport modes: stdio (single-user), SSE (deprecated), streamable HTTP (multi-agent)
  • Wire authentication middleware for HTTP/SSE transports

Key Methods:

  • create_server(config) — Factory that assembles all subsystems
  • run() — Main entry point; selects transport and starts server
  • main() — CLI handler with --transport, --config, --generate-token flags
  • Tool implementations organized by category: core, files, discovery, jobs, admin, monitoring, custom

2. Authentication & Authorization (src/matlab_mcp/auth/, Phase 2)

Responsibilities:

  • Enforce bearer token authentication on HTTP/SSE transports via ASGI middleware
  • Read token from MATLAB_MCP_AUTH_TOKEN environment variable at startup
  • Return HTTP 401 with WWW-Authenticate: Bearer header on auth failure
  • Bypass auth for /health and OPTIONS pre-flight requests
  • Support CORS for browser-based agents
  • Provide --generate-token CLI helper to generate 64-character hex tokens

Components:

  • BearerAuthMiddleware — Pure ASGI middleware (not BaseHTTPMiddleware) using hmac.compare_digest() for constant-time token comparison
  • Token generation via secrets.token_hex(32) with shell snippets for POSIX, Windows cmd, and PowerShell
  • Startup warning when HTTP transport is selected without MATLAB_MCP_AUTH_TOKEN env var

3. Configuration System (src/matlab_mcp/config.py)

Responsibilities:

  • Load YAML configuration with sensible defaults
  • Apply environment variable overrides (MATLAB_MCP_* prefix)
  • Validate all settings via Pydantic models
  • Warn users if sensitive keys appear in config files

Configuration Domains:

  • ServerConfig: transport (stdio | sse | streamablehttp), host, port, logging
  • PoolConfig: min/max engines, health checks, warmup thresholds
  • ExecutionConfig: sync timeout, workspace isolation, temp directory
  • SecurityConfig: blocked function list, upload size limits
  • SessionConfig: max sessions, idle timeout, job retention
  • OutputConfig: Plotly conversion, image formats, truncation thresholds
  • MonitoringConfig: metrics collection, dashboard settings
  • HITLConfig: Human-in-the-loop approval gates (execute, file ops)

4. Engine Pool Manager (src/matlab_mcp/pool/)

Responsibilities:

  • Manage a pool of MATLAB engine instances with elastic scaling
  • Acquire/release engines from the pool
  • Health check engines periodically; replace unhealthy ones
  • Proactively start engines when utilization exceeds threshold (80%)
  • Scale down idle engines beyond minimum count

Components:

  • EnginePoolManager — Orchestrates pool lifecycle
  • MatlabEngineWrapper — Wraps individual MATLAB engine with state (STOPPED → STARTING → IDLE ↔ BUSY)
  • matlab.engine integration (lazy-imported, R2022b+)

5. Job Execution (src/matlab_mcp/jobs/)

Responsibilities:

  • Execute MATLAB code with optional security checks
  • Implement hybrid sync/async execution model
  • Promote code to async when sync_timeout exceeded
  • Track job metadata (status, timestamps, results)
  • Implement progress tracking via mcp_progress.m helper

Components:

  • JobExecutor — Orchestrates code execution lifecycle
  • JobTracker — In-memory registry of jobs by session
  • Job & JobStatus — Data models for job state
  • MATLAB helpers: mcp_progress.m (progress reporting), mcp_checkcode.m (linting)

Execution Flow:

  1. Security validation (blocked functions scan)
  2. Job context injection (__mcp_job_id__, __mcp_temp_dir__)
  3. Synchronous execution up to sync_timeout (default 30s)
  4. Auto-promotion to async if timeout exceeded
  5. Background task monitors async completion
  6. Result formatting (text, variables, figures, files)

6. Session Management (src/matlab_mcp/session/)

Responsibilities:

  • Create and manage per-user sessions (SSE and streamable HTTP only)
  • Isolate temporary working directories by session
  • Track session idle time and enforce cleanup timeouts
  • Enforce maximum session count limit

Components:

  • SessionManager — CRUD operations on sessions
  • Session — Per-user workspace directory and metadata
  • Automatic cleanup of expired sessions (default 1 hour idle)

Transport-Specific Behavior:

  • stdio: Single default session (no multi-user isolation)
  • SSE/streamable HTTP: Each connected client gets a unique session with isolated temp directory

7. Security Validation (src/matlab_mcp/security/)

Responsibilities:

  • Scan code for blocked MATLAB functions before execution
  • Prevent shell injection via ! escape and system() family
  • Sanitize filenames to prevent path traversal attacks
  • Enforce upload size limits

Blocked Functions: system, unix, dos, !, eval, feval, evalc, evalin, assignin, perl, python

Implementation Details:

  • Smart detection: strips string literals and comments before scanning
  • Regex-based pattern matching (best-effort heuristic)
  • Metrics integration: logs violations for monitoring

8. Result Formatting & Visualization (src/matlab_mcp/output/)

Responsibilities:

  • Format MATLAB execution results (text, variables, figures)
  • Convert MATLAB figures to interactive Plotly JSON
  • Generate PNG thumbnails for figures
  • Truncate large results with optional file storage

Components:

  • ResultFormatter — Structures success/error responses
  • plotly_style_mapper.py — MATLAB → Plotly style conversion (line styles, markers, colormaps, fonts)
  • plotly_convert.py — Loads and validates Plotly JSON from MATLAB helper output
  • thumbnail.py — Pillow-based thumbnail generation
  • MATLAB helper: mcp_extract_props.m — Extracts figure properties from MATLAB figures

Plotly Conversion Pipeline:

  1. MATLAB side: mcp_extract_props.m extracts raw figure data (axes, lines, surfaces, etc.)
  2. Python side: plotly_style_mapper.py converts MATLAB properties to Plotly equivalents
  3. JSON validation: load_plotly_json() reads and validates schema
  4. WebGL support: Large datasets (10,000+ points) use WebGL traces for performance

9. Monitoring & Health Checks (src/matlab_mcp/monitoring/)

Responsibilities:

  • Collect real-time metrics (pool utilization, job counts, error rates)
  • Evaluate overall server health (healthy, degraded, unhealthy)
  • Expose metrics and health status via HTTP endpoints
  • Provide interactive web dashboard

Components:

  • MetricsCollector — Accumulates counters and timing statistics
  • MetricsStore — Async SQLite backend for time-series data
  • evaluate_health() — Classification logic (healthy/degraded/unhealthy)
  • Dashboard: HTML/JS/CSS at /dashboard with real-time Plotly charts
  • Routes: /health, /metrics, /dashboard, /dashboard/api/*

10. Human-in-the-Loop Approval (src/matlab_mcp/hitl/, Phase 4)

Responsibilities:

  • Intercept sensitive operations (code execution, file operations)
  • Request human approval via FastMCP's elicitation protocol
  • Allow/deny/cancel operations based on response
  • Audit log all approvals

Configuration:

  • HITLConfig in config.yaml: enabled, protected_functions, protect_file_ops, all_execute
  • Approval gates wired into execute_code_impl, upload_data_impl, delete_file_impl
  • Disabled by default (zero operational cost)

Connection & Data Flow

graph LR
    Agent["AI Agent<br/>(Claude Code, Codex CLI, Cursor)"]
    Auth["BearerAuthMiddleware<br/>(Phase 2)"]
    Server["FastMCP 3.2.0<br/>Tool Router"]
    SecurityValidator["SecurityValidator<br/>(blocked functions)"]
    HITL["HITL Approval Gates<br/>(Phase 4)"]
    JobExecutor["JobExecutor<br/>(sync/async)"]
    EnginePool["EnginePoolManager<br/>(elastic scaling)"]
    MatlabEngine["MATLAB Engine<br/>Instance"]
    ResultFormatter["ResultFormatter<br/>(Plotly convert)"]
    SessionMgr["SessionManager<br/>(workspace isolation)"]
    
    Agent -->|stdio/SSE/HTTP + Bearer token| Auth
    Auth -->|Authenticated request| Server
    Server -->|Route to tool| SecurityValidator
    SecurityValidator -->|Scan for blocked functions| HITL
    HITL -->|Request approval if enabled| JobExecutor
    JobExecutor -->|Acquire engine| EnginePool
    EnginePool -->|Get/create workspace| SessionMgr
    EnginePool -->|Allocate engine| MatlabEngine
    MatlabEngine -->|Execute code| MatlabEngine
    MatlabEngine -->|Return output| ResultFormatter
    ResultFormatter -->|Format + convert figures| Server
    Server -->|MCP response| Agent
Loading

Data Flow: Execute Code (Sync)

sequenceDiagram
    participant Agent
    participant Server as MCP Server
    participant Validator as SecurityValidator
    participant Executor as JobExecutor
    participant Pool as EnginePool
    participant Engine as MATLAB Engine
    participant Formatter as ResultFormatter
    
    Agent->>Server: execute_code("x = magic(3)")
    Server->>Validator: validate_code()
    alt Blocked Function Detected
        Validator-->>Server: SecurityError
        Server-->>Agent: {"error": "..."}
    else Code OK
        Validator-->>Server: OK
        Server->>Executor: execute(code)
        Executor->>Pool: acquire_engine()
        Pool-->>Executor: engine_handle
        Executor->>Engine: eval(code + job_context)
        Engine-->>Executor: stdout, workspace
        Executor->>Formatter: build_response()
        Formatter-->>Executor: MCP response
        Executor->>Pool: release_engine()
        Server-->>Agent: {"text": "ans = 8 6 4...", ...}
    end
Loading

Data Flow: Execute Code (Async Promotion)

sequenceDiagram
    participant Agent
    participant Server as MCP Server
    participant Executor as JobExecutor
    participant Engine as MATLAB Engine
    participant Tracker as JobTracker
    participant Pool as EnginePool
    
    Agent->>Server: execute_code("long_simulation()")
    Note over Executor: sync_timeout = 30s
    Server->>Executor: execute(code)
    Executor->>Pool: acquire_engine()
    Executor->>Engine: eval(code, background=True)
    Note over Executor: Waiting...
    Executor-->>Executor: 30s timeout!
    Executor->>Tracker: create_job(status=RUNNING)
    Executor-->>Server: {job_id: "abc123", status: "RUNNING"}
    Server-->>Agent: {"job_id": "abc123", "status": "RUNNING"}
    
    par Background Monitoring
        Executor->>Engine: poll future
        Engine-->>Executor: 45% complete
        Executor->>Tracker: update progress
    and Agent Polls
        Agent->>Server: get_job_status("abc123")
        Server->>Tracker: get_job("abc123")
        Tracker-->>Server: {status: "RUNNING", progress: 45%}
        Server-->>Agent: {status: "RUNNING", progress: 45%}
    end
    
    Engine-->>Executor: Completed!
    Executor->>Tracker: update_job(status=COMPLETED, result=...)
    Agent->>Server: get_job_result("abc123")
    Tracker-->>Server: Full result
    Server-->>Agent: MCP response with output
Loading

Transport Modes

1. stdio (Default, Single-User)

  • Direct stdin/stdout communication
  • No network stack needed
  • Simplest setup, safest for local development
  • No authentication (by MCP design)
  • Single session per server instance

Example:

matlab-mcp --transport stdio

2. SSE (Server-Sent Events, Multi-User, Deprecated)

  • HTTP-based, supports multiple concurrent agents
  • Session isolation via mcp-session-id header
  • Deprecated: Codex CLI does not support SSE; use streamable HTTP instead
  • Requires bearer token auth via BearerAuthMiddleware
  • Suitable for teams behind reverse proxy

Example:

MATLAB_MCP_AUTH_TOKEN="$(matlab-mcp --generate-token)" \
  matlab-mcp --transport sse --host 127.0.0.1 --port 8765

3. Streamable HTTP (Multi-User, Recommended)

  • Single /mcp endpoint for all MCP requests
  • HTTP POST for client requests, GET for streaming responses
  • Per-session workspace isolation via mcp-session-id header
  • Stateless mode available (stateless_http: true) for load-balanced deployments
  • Requires bearer token auth via BearerAuthMiddleware
  • Recommended for Codex CLI, Claude Code, and Cursor

Example:

MATLAB_MCP_AUTH_TOKEN="$(matlab-mcp --generate-token)" \
  matlab-mcp --transport streamablehttp --host 127.0.0.1 --port 8765

Key Design Decisions

1. Lazy MATLAB Engine Import

The matlab.engine module is imported lazily inside pool/engine.py only when needed. This allows the server to start and respond to /health checks even if MATLAB is not installed or not available in PATH.

# In pool/engine.py
def start(self):
    import matlab.engine
    self._engine = matlab.engine.start_matlab()

2. Pure ASGI Middleware for Auth

BearerAuthMiddleware is a pure ASGI middleware (not Starlette's BaseHTTPMiddleware) to avoid the known streaming double-send bug in Starlette versions < 0.50. This ensures auth works correctly with SSE and streamable HTTP transports.

3. Hybrid Sync/Async Execution Model

The executor runs code synchronously up to sync_timeout, then auto-promotes to async if the timeout is exceeded. This provides:

  • Fast path: Short scripts complete and return immediately
  • Long path: Long-running simulations return a job_id for polling
  • Transparency: Agents don't need to know execution mode in advance

4. Session Isolation via Temp Directories

Each session gets its own temporary directory (/tmp/matlab_mcp/<session_id>/ on Unix, %TEMP%\matlab_mcp\<session_id>\ on Windows). This provides:

  • Workspace isolation: Files and data don't leak between agents
  • Cleanup: Expired sessions' temp directories are removed automatically
  • Multi-tenant safety: Concurrent agents can't interfere with each other

5. Environment-Variable-First Configuration

All configuration can be overridden via environment variables (MATLAB_MCP_*), which:

  • Eliminates config file security debt (no secrets in YAML)
  • Enables CI/CD and containerized deployments
  • Allows per-deployment customization without file editing

6. Monitoring Disabled by Default

Metrics collection (MetricsCollector, MetricsStore) is optional and disabled by default to reduce memory overhead in resource-constrained environments. Enable via monitoring.enabled: true in config.


Integration Points with External Systems

MATLAB Engine API (matlab.engine, R2022b+)

  • What: MATLAB's Python API for evaluating code in MATLAB engines
  • Where: src/matlab_mcp/pool/engine.py
  • How: engine.eval(code) for sync, engine.eval(..., background=True) for async
  • Lazy import: Only imported when first engine starts

FastMCP 3.2.0 Framework

  • What: MCP server framework and protocol handler
  • Where: src/matlab_mcp/server.py
  • How: @mcp.tool() decorators, ctx parameter for context
  • Features used: Tool registration, elicitation API, custom routes, middleware integration

Starlette ASGI Framework

  • What: ASGI middleware and HTTP routing
  • Where: src/matlab_mcp/auth/middleware.py, monitoring dashboard
  • How: Middleware class, custom ASGI callables, cors.CORSMiddleware
  • Why: Pure ASGI for auth; CORS support for browser agents

SQLite3 (Monitoring Backend)

  • What: Time-series metrics storage
  • Where: src/matlab_mcp/monitoring/store.py
  • How: aiosqlite async wrapper for WAL-mode SQLite
  • Optional: Disabled by default; enable with monitoring.enabled: true

Performance Characteristics

Metric Target Notes
Tool registration <100ms On startup; cached after
Code security scan <10ms Regex-based; depends on code size
Sync execution Agent timeout (default 30s) Fast MATLAB code completes quickly
Async promotion <1s overhead Minimal; mostly engine overhead
Health check ~500ms per engine 1+1 eval per engine; runs periodically
Session cleanup <100ms Runs on configurable interval
Plotly conversion 100-500ms Depends on figure complexity
Dashboard load <1s Cached static HTML + API calls

Clone this wiki locally