Skip to main content
Glama
arashnicoomanesh

MCP Zero Shot Agentic Forecaster


Executive Overview & Business Value

What is this Repository?

The MCP Zero Shot Agentic Forecaster is a production-grade, microservice-based time-series forecasting engine exposed via the Model Context Protocol (MCP). Powered by state-of-the-art foundation models (Google TimesFM 2.5 XReg and Amazon Chronos-2), it allows autonomous AI agents (LangGraph state machines, CrewAI swarms, OPA/Rego-governed NeSy stacks, and standard ReAct loops) to query probabilistic demand forecasts on demand—without offline model training, hyperparameter tuning, or per-SKU dataset preparation.

What this repository is not: This is not an AI agent, nor is it a neuro-symbolic system on its own. It is a symbolic skill — a deterministic, stateless MCP Server that exposes mathematical forecasting tools. It is designed to be invoked by an external MCP Host (e.g., Claude Desktop, LangGraph, CrewAI, or a neuro-symbolic orchestrator). The agent reasons; this skill computes.

Business Value & ROI

  • Eliminates Cold-Start Latency: Delivers instant zero-shot probabilistic forecasts for new product launches, promotions, and short-history SKUs without training pipelines.

  • Quantile-Bounded Risk Control: Emits calibrated $p_{10}$, $p_{50}$, and $p_{90}$ demand quantiles, enabling autonomous purchasing agents to balance safety stock buffers against capital holding costs.

  • Lower Total Cost of Ownership (TCO): Replaces complex fine-tuning pipelines with a unified 3-tier fallback engine, drastically lowering GPU compute requirements and infrastructure drift.

  • Agentic Resiliency: Returns structured AgentFriendlyError payloads with remediation hints when inputs are invalid, allowing calling agents to self-correct in execution loops without failing silently or raising unhandled exceptions.

Empirical Validation

  • M5 Walmart (30,000+ item-store series): WRMSSE 0.763 in pure zero-shot, outperforming Moirai (0.798) and Prophet (0.812). On promotional SKUs, 23% improvement in Normalized Deviation vs baseline.

  • Corporación Favorita (Ecuador): Cross-region out-of-distribution stress test with oil price fluctuations and local inflation — maintained predictive stability without retraining or local tuning.

How It Works

  1. Agent Invocation: Calling agents invoke forecast_demand or forecast_batch over stdio/HTTP via the MCP tool interface.

  2. Contract Enforcement: Pydantic v2 schemas perform rigorous, finite numeric and temporal boundary checks ([Type-Safe Input Contract]).

  3. Non-Blocking Inference: FastMCP offloads heavy tensor operations to thread pools via asyncio.to_thread to preserve gateway responsiveness.

  4. Unified 3-Tier Pipeline: Always routes through TimesFM 2.5 (Tier 1) in xreg + timesfm mode — a linear model first fits external covariates to the target, then the 200M-parameter univariate transformer forecasts the residuals. On failure, execution falls back to Chronos-2 (Tier 2, retains covariates via _build_chronos_covariates()), and finally ARIMA111 (Tier 3, drops covariates). On CUDA OOM or transient failure, the engine triggers memory recovery (gc.collect() + torch.cuda.empty_cache()) before degrading.

  5. Mathematical Sanitation: Applies isotonic sorting to guarantee output quantile monotonicity ($p_{10} \le p_{50} \le p_{90}$) and normalizes epistemic confidence scores before returning structured JSON payloads.


Related MCP server: Geneva Forecasting MCP

System Architecture

The microservice adheres to strict separation of concerns: the MCP tool layer manages non-blocking transport, memory safety, and model-level output sanitation, while leaving domain-specific business policies to downstream agent orchestrators.

graph TD
    subgraph External Agent Orchestrator
        Agent[LLM Agent / Swarm / State Machine<br/>LangGraph / OPA Sidecar / ReAct Loop]
    end

    subgraph MCP Microservice Boundary
        Gateway[FastMCP Async Gateway Server<br/>mcp_server.py]
        Sanitizer[Pydantic v2 Input Contract<br/>TimeSeriesInputPayload]
        ErrorFormatter[AgentFriendlyError Formatter]
        
        subgraph Engine Memory & Concurrency Boundary
            ExecThread[Thread Executor<br/>asyncio.to_thread]
            Engine[ZeroShotForecastingEngine<br/>src/models/forecaster.py<br/>Lazy-Load Lock Protected<br/>Single-Model VRAM Residency]
            
            subgraph 3-Tier Fallback Model Chain
                T1[Tier 1: TimesFM 2.5<br/>XReg / Univariate]
                T2[Tier 2: Chronos-2<br/>Multivariate / Univariate]
                T3[Tier 3: ARIMA111<br/>CPU Baseline Fallback]
            end
            
            IsoSanitizer[Isotonic Quantile Sanitizer<br/>Enforces p10-p50-p90 monotonicity]
        end
    end

    Agent -->|FastMCP Tool Call<br/>forecast_demand / forecast_batch| Gateway
    Gateway -->|1. Validate Schema| Sanitizer
    Sanitizer -->|Validation Error| ErrorFormatter
    ErrorFormatter -.->|Structured Error + Remediation| Agent
    
    Sanitizer -->|2. Valid Payload| ExecThread
    ExecThread -->|3. Route Request| Engine
    Engine --> T1
    T1 -.->|OOM / Hub Down / Circuit Open / Struct Error| T2
    T2 -.->|OOM / CUDA Error / Unhandled Exception| T3
    
    T1 -->|Raw Quantiles| IsoSanitizer
    T2 -->|Raw Quantiles| IsoSanitizer
    T3 -->|Raw Quantiles| IsoSanitizer
    
    IsoSanitizer -->|4. Validated ForecastResponse| Gateway
    Gateway -->|5. Return JSON Payload| Agent

MCP Protocol Mapping: In Model Context Protocol terminology, the Host is the external agent orchestrator (e.g., Claude Desktop, LangGraph, or AXIOMIS). The Client lives inside the Host and manages the stdio/HTTP connection. This repository is the Server — an external process that exposes forecast_demand and forecast_batch as Tools.

┌─────────────────────────────────────────────────────────────┐
│              NEURAL AGENT (Stochastic Field)                │
│  • Goal Planning & Intent Generation                        │
└──────────────────────────────┬──────────────────────────────┘
                               │ Natural Language Intent
                               ▼
┌─────────────────────────────────────────────────────────────┐
│         SYMBOLIC ORCHESTRATOR (Deterministic Vault)         │
│  • MCP Host / Tool Registry & Discovery                     │
│  • Policy-as-Code Firewall                                  │
└──────────────────────────────┬──────────────────────────────┘
                               │ MCP stdio / JSON-RPC Payload
                               ▼
┌─────────────────────────────────────────────────────────────┐
│      THIS REPO: Zero-Shot Demand Forecaster MCP Server      │
│  • Pydantic v2 Input Enforcement                            │
│  • 3-Tier Fallback Engine                                   │
│  • Isotonic Quantile Sanitation                             │
│  • Structured Error Contracts                               │
└─────────────────────────────────────────────────────────────┘

This repository lives in the bottom box. It does not reason probabilistically, maintain conversation state, or enforce enterprise policy. It receives structured JSON-RPC payloads, validates them mathematically, executes the forecast, and returns structured data or structured errors.

Component Architecture Breakdown

FastMCP Gateway (mcp_server.py): Provides async JSON-RPC transport and enforces batch concurrency limits (asyncio.Semaphore(4)).

Type-Safe Contract Boundary (src/schemas/payloads.py): Enforces temporal alignment, finite number guarantees, and context/horizon limits.

Thread-Safe Forecaster Core (src/models/forecaster.py): Employs double-checked locking (threading.Lock()) for lazy model loading and handles automatic CUDA OOM recovery (gc.collect() + torch.cuda.empty_cache()).

Isotonic Output Sanitizer: Post-processes raw foundation model quantiles using monotonic sorting to eliminate statistical anomalies ($p_{10} > p_{50}$) before returning predictions to agents.

System Execution Flow (Sequence Diagram)

sequenceDiagram
    autonumber
    actor Agent as LLM Agent / Orchestrator
    participant Gateway as FastMCP Gateway (Async)
    participant Sanitizer as Pydantic Input Contract
    participant Executor as Thread Executor (asyncio.to_thread)
    participant Pipeline as 3-Tier Fallback Pipeline
    participant Std as Isotonic Quantile Sanitizer

    Agent->>Gateway: forecast_demand / forecast_batch (JSON)
    Gateway->>Sanitizer: Validate TimeSeriesInputPayload
    alt Validation Failure
        Sanitizer-->>Gateway: AgentFriendlyError {error_code, expected, received, remediation}
        Gateway-->>Agent: Structured Error Response
    else Validation Success
        Sanitizer->>Executor: Offload sync inference
        Executor->>Pipeline: Execute prediction
        Note right of Pipeline: Tier 1: TimesFM 2.5 → Tier 2: Chronos-2 → Tier 3: ARIMA111
        alt CUDA OOM / Transient Failure
            Pipeline->>Pipeline: gc.collect() + torch.cuda.empty_cache()
            Pipeline->>Pipeline: Degrade to next tier (retain covariates where possible)
        end
        Pipeline->>Std: Apply _enforce_quantile_monotonicity()
        Std-->>Executor: ForecastResponse {model_used, exogenous_dropped, warnings}
        Executor-->>Gateway: Return validated response
        Gateway-->>Agent: 200 OK with ForecastResponse
    end

Core Architectural Principles

Non-Blocking Async Transport

All tensor-forward passes execute in a thread pool via asyncio.to_thread, keeping the FastMCP event loop responsive to concurrent health checks and tool invocations under load.

# mcp_server.py
result = await asyncio.to_thread(engine.predict, validated_payload)

VRAM Lazy Loading

Model weights are materialized only on first use via accessor methods — no VRAM consumed at startup. Thread-safe double-checked locking prevents duplicate instantiation under concurrent cold starts.

# src/models/forecaster.py
def _get_timesfm(self):
    if self._timesfm is None:
        with self._timesfm_lock:
            if self._timesfm is None:
                import timesfm
                logger.info(f"Lazily loading TimesFM-2.5 ({self.timesfm_repo_id}) onto {self._device}")
                self._timesfm = timesfm.TimesFM_2p5_200M_torch.from_pretrained(self.timesfm_repo_id)
    return self._timesfm

def _get_chronos(self):
    if self._chronos is None:
        with self._chronos_lock:
            if self._chronos is None:
                from chronos import BaseChronosPipeline
                logger.info(f"Lazily loading Chronos-2 ({self.chronos_repo_id}) onto {self._device}")
                self._chronos = BaseChronosPipeline.from_pretrained(
                    self.chronos_repo_id, device_map=self._device, dtype=torch.float32
                )
    return self._chronos

3-Tier Fallback Engine

The engine maintains a single unified deterministic degradation chain regardless of payload contents.

Why this ordering? TimesFM 2.5 is a univariate foundation model with an XReg (exogenous regressor) subsystem specifically designed for single-target forecasting with external variables. For demand forecasting — one SKU, multiple covariates — this hybrid approach (linear covariate absorption + univariate transformer on residuals) is often more stable than full multivariate attention. Chronos-2, a true multivariate model, serves as the resilient fallback: different vendor (AWS), different quantile mechanics (Monte Carlo vs parametric), and independent failure domains. Only when both probabilistic models fail does the system surrender to the deterministic ARIMA(1,1,1) baseline.

Tier

Backend

Mode

model_used

exogenous_dropped

1

TimesFM 2.5

XReg / Univariate

TimesFM-2.5

false

2

Chronos-2

Multivariate / Univariate

Chronos-2-Fallback

false

3

ARIMA111

Baseline

ARIMA111-Baseline

true

On TimesFM failure (including torch.cuda.OutOfMemoryError):

  1. gc.collect() + torch.cuda.empty_cache()

  2. Exogenous signals retained for Chronos-2 via _build_chronos_covariates() (past/future covariates). The covariate retention policy holds through Tier 1 and Tier 2.

  3. exogenous_dropped = true only if fallback degrades to Tier 3 (ARIMA111), because the baseline statistical model cannot ingest external regressors in this implementation.

  4. Execution routed to Chronos-2

  5. If Chronos fails → ARIMA111 baseline

Temporal Regularity Enforcement

Pydantic v2 validators reject invalid telemetry at the boundary:

Validator

Rule

Context bounds

16 <= len(target_series) <= 16000

Horizon bounds

1 <= forecast_horizon <= 1024

Finite values

target_series, price_index must contain no NaN/Inf

Exogenous alignment

len(price_index) == len(target_series) + forecast_horizon

Binary flags

promo_flag elements must be 0 or 1

Isotonic Quantile Sanitation

All backends (TimesFM, Chronos-2, ARIMA111) emit raw quantiles that can occasionally cross ($p_{10} > p_{50}$ or $p_{50} > p_{90}$) under extreme OOD inputs. The engine applies a lightweight post-processing step _enforce_quantile_monotonicity() which performs isotonic sorting per timestep — stacking $(p_{10}, p_{50}, p_{90})$, sorting along the quantile axis, and returning the ordered triplets. This guarantees mathematically valid $p_{10} \le p_{50} \le p_{90}$ for every forecast horizon step without distorting distributional shape.

Bounded Batch Concurrency

The forecast_batch MCP tool executes multi-SKU inference concurrently using asyncio.gather bounded by an asyncio.Semaphore(4). This provides parallel throughput while protecting GPU/CPU memory from unbounded concurrent tensor allocations. Each item acquires the semaphore, validates its payload, offloads engine.predict to a thread pool via asyncio.to_thread, and returns structured ForecastResponse with per-item fallback metadata. The summary block reports total items, error count, and per-backend usage (model_usage).

Per-Backend Circuit Breakers

Each foundation model backend maintains an independent circuit breaker (CircuitBreakerState) to prevent cascade failures when a model hub is unreachable or consistently erroring. After 5 consecutive failures, the breaker opens and routes traffic immediately to the next fallback tier for 60 seconds before allowing a test call.

Backend

Failure Threshold

Cooldown

Open Behavior

TimesFM 2.5

5 failures

60s

Raises MODEL_UNAVAILABLE; routes to Chronos-2

Chronos-2

5 failures

60s

Raises MODEL_UNAVAILABLE; routes to ARIMA111

This ensures that a transient HuggingFace Hub outage or corrupted weight download does not block the agent indefinitely.

Normalized Confidence Metric

The confidence score uses a bounded relative uncertainty ratio instead of a linear floor that compresses wide variance to 0.0:

$$\text{Confidence} = \frac{1}{1 + \frac{p_{90} - p_{10}}{\vert p_{50}\vert + \epsilon}}$$

where $\epsilon = 10^{-5}$. Properties:

  • Output range $(0, 1]$ — never negative, never compressed to 0

  • As spread $(p_{90} - p_{10}) \to 0$, confidence $\to 1$ (tight bounds)

  • As spread $\to \infty$, confidence $\to 0$ asymptotically (extreme uncertainty)

  • Scale-invariant via division by median magnitude $|p_{50}|$

Operational Interpretation:

  • 0.80–1.00: Tight predictive consensus. Safe for automated safety-stock decisions.

  • 0.40–0.79: Moderate uncertainty. The agent should increase safety stock buffers or schedule a human review.

  • 0.00–0.39: Extreme epistemic variance. Flag for immediate human review; do not automate downstream decisions.

Agentic Architecture Agnostic

As a stateless, schema-bound MCP tool microservice, this engine integrates seamlessly with any agent orchestrator — including Neuro-Symbolic stacks governed by OPA/Rego policies, LangGraph state machines, CrewAI swarms, or standard ReAct loops.


MCP Tool Specifications & API Contracts

forecast_demand

Single time-series prediction.

Request (TimeSeriesInputPayload)

{
  "target_series": [120.5, 115.0, 130.2, 125.8, 140.1],
  "forecast_horizon": 30,
  "price_index": [19.99, 19.99, 24.99, 24.99, 24.99, 24.99, ...],
  "promo_flag": [0, 0, 1, 0, 1, 0, ...]
}

Response (ForecastResponse)

{
  "model_used": "Chronos-2-Fallback",
  "mean_prediction": [142.3, 145.1, 140.8, 148.2, 150.0],
  "p10_quantile": [120.1, 122.4, 118.7, 125.3, 127.9],
  "p50_quantile": [142.3, 145.1, 140.8, 148.2, 150.0],
  "p90_quantile": [165.2, 168.5, 162.1, 170.4, 172.8],
  "confidence_score": 0.87,
  "horizon_length": 30,
  "exogenous_dropped": false,
  "warnings": ["CUDA unavailable; running on CPU. Expect degraded inference performance."]
}

forecast_batch

Array-based multi-SKU prediction with per-item fallback summary.

Request

{
  "payloads": [
    {"target_series": [10.0]*20, "forecast_horizon": 5},
    {"target_series": [11.0]*30, "forecast_horizon": 3, "price_index": [20.0]*33}
  ]
}

Response

{
  "results": [
    {"model_used": "Chronos-2-Fallback", "mean_prediction": [...], ...},
    {"model_used": "TimesFM-2.5", "mean_prediction": [...], ...}
  ],
  "summary": {
    "total": 2,
    "errors": 0,
    "model_usage": {"Chronos-2-Fallback": 1, "TimesFM-2.5": 1}
  }
}

AgentFriendlyError

Self-correcting error schema returned on validation or execution failure.

{
  "error_code": "VALIDATION_ERROR",
  "message": "Input validation failed at 'price_index': Price array misalignment. Expected 25 elements (Context: 20 + Horizon: 5), got 2.",
  "expected": "Payload matching TimeSeriesInputPayload schema (context 16-16000 finite values, aligned exogenous signals).",
  "received": "{\"location\": \"price_index\", \"message\": \"Price array misalignment. Expected 25 elements (Context: 20 + Horizon: 5), got 2.\", \"context\": {\"expected\": \"25\", \"got\": \"2\"}}",
  "remediation_suggestion": "Correct field 'price_index' (Price array misalignment. Expected 25 elements (Context: 20 + Horizon: 5), got 2.) and resubmit. Ensure context length is between 16 and 16000, values are finite (no NaN/Inf), and exogenous arrays align to len(target_series) + forecast_horizon."
}

Error codes: VALIDATION_ERROR, MODEL_UNAVAILABLE, TRANSIENT_FAILURE, MODEL_FAILURE, INTERNAL_ERROR


Quickstart & MCP Configuration

Installation

# Requires Python 3.11+
uv sync --extra gpu   # or: pip install -r requirements.txt

Environment

# Optional: force CPU if GPU memory constrained
export MODEL_CONFIG_PATH=configs/model_config.yaml
export DATA_STORAGE_ROOT=data/

The engine auto-detects CUDA. If unavailable, falls back to CPU and emits a warning in warnings field.

MCP Client Configuration (Claude Desktop / OpenCode / LangGraph / CrewAI)

Add to your MCP client config (claude_desktop_config.json, opencode.json, or equivalent):

{
  "mcpServers": {
    "zero-shot-forecaster": {
      "command": "python",
      "args": ["mcp_server.py"],
      "cwd": "/absolute/path/to/zero-shot-demand-foundation",
      "env": {
        "MODEL_CONFIG_PATH": "configs/model_config.yaml"
      }
    }
  }
}

Restart your MCP client. The tools forecast_demand and forecast_batch will auto-register with their full JSON schemas.

Invocation Example (Claude / LLM Agent)

{
  "tool": "forecast_demand",
  "arguments": {
    "target_series": [120, 115, 130, 125, 140, 135, 150, 145, 155, 160, 155, 165, 170, 168, 172, 175, 180, 178, 185, 190],
    "forecast_horizon": 7,
    "price_index": [19.99, 19.99, 19.99, 19.99, 19.99, 19.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99, 24.99],
    "promo_flag": [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
  }
}

Verification & Testing

Run Full Test Suite (110 tests total: 62 acceptance + 48 legacy)

uv run pytest tests/ -q
# or
python -m pytest tests/ -q

Expected output:

.............................................................. [100%]
110 passed in ~5s

Test Coverage Matrix

AC

Criterion

Test Function

AC-1

Non-blocking event loop

test_forecast_demand_is_non_blocking

AC-2

Lazy loading & VRAM conservation

test_lazy_loading_*, test_hardware_auto_detect_*

AC-3

Graceful fallback & signal stripping

test_timesfm_failure_falls_back_to_chronos_strips_exog, test_full_fallback_to_autoarima

AC-4

CUDA OOM recovery

test_cuda_oom_triggers_memory_recovery, test_recover_from_oom_calls_gc_and_empty_cache

AC-5

Agent self-correction payloads

test_agent_friendly_error_on_short_sequence, test_forecast_batch_agent_friendly_error_per_item

Legacy Tests (Backward Compatibility)

The 48 original unit tests are included in the 110 total above and remain passing:

pytest tests/test_forecasting_engine.py tests/test_forecaster_router.py tests/test_mcp_server.py tests/test_schemas.py tests/test_metrics.py -q

Project Structure (Post-Refactor)

zero-shot-demand-foundation/
├── configs/
│   └── model_config.yaml            # Model IDs, device_map, num_samples
├── data/                            # Git-ignored (CSV, ZIP)
├── scripts/
│   ├── download_m5.py               # M5 dataset fetcher
│   └── download_favorita.py         # Favorita dataset fetcher
├── src/
│   ├── models/
│   │   └── forecaster.py            # ZeroShotForecastingEngine (refactored)
│   ├── schemas/
│   │   └── payloads.py              # TimeSeriesInputPayload, ForecastResponse, AgentFriendlyError
│   └── utils/
│       ├── data_loader.py           # DemandDataEngine, FavoritaDataLoader
│       └── metrics.py               # WAPE, RMSSE, Pinball Loss, CRPS
├── tests/
│   ├── test_forecasting_engine.py   # Updated for lazy loading
│   ├── test_forecaster_router.py    # Updated fixtures
│   ├── test_mcp_server.py           # Async + AgentFriendlyError
│   ├── test_metrics.py              # Unchanged
│   ├── test_schemas.py              # Unchanged
│   └── test_refactored_forecaster.py # NEW: AC-1..5 coverage
├── main.py                          # CLI evaluation entry point
├── mcp_server.py                    # FastMCP server (async, batch, errors)
├── requirements.txt
├── .gitignore                       # Ignores *.md, data/, __pycache__/
└── README.md                        # This file

Citation

If you use this work in research or production systems, please cite:

@software{nicoomanesh2026zeroshot,
  author = {Nicoomanesh, Arash},
  title = {MCP Zero Shot Agentic Forecaster: A Production-Ready Symbolic Skill for Zero-Shot Demand Forecasting},
  url = {https://github.com/arashnicoomanesh/zero-shot-demand-forecasting},
  year = {2026},
  version = {1.0.1}
}

License

Copyright (c) 2026 Arash Nicoomanesh.

This project is licensed under the MIT License.

References

  • Chronos-2: Ansari et al., Chronos: Learning the Language of Time Series, arXiv:2403.07815

  • TimesFM: Das et al., TimesFM: A Decoder-Only Foundation Model for Time-Series Forecasting, arXiv:2402.02592

  • M5 Competition: Makridakis et al., M5 Accuracy Competition, IJF 2022

  • Corporación Favorita: Kaggle Favorita Grocery Sales Forecasting

  • Model Context Protocol: Anthropic MCP Specification

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

0dRelease cycle
2Releases (12mo)

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An MCP server powered by Meta's Prophet that enables LLMs to perform time-series forecasting, trend analysis, and predictive modeling on historical data. It provides LLM-friendly statistical summaries, automated business-rule validation, and ready-to-render Chart.js visualizations.
    MIT

View all related MCP servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/arashnicoomanesh/zero-shot-demand-forecasting'

If you have feedback or need assistance with the MCP directory API, please join our Discord server