Architecture
sktime-mcp is a specialized implementation of the Model Context Protocol (MCP) server designed to expose the sktime library’s capabilities to Large Language Models.
High-Level Overview
The server operates as a bridge between the stateless nature of LLM interactions and the stateful requirements of machine learning workflows. It manages object lifecycles, provides semantic discovery of estimators, and handles complex data ingestion.
A request enters at the MCP protocol layer (server.py), is dispatched to a tool
(tools/), which delegates the actual work to the executor (runtime/executor.py).
Data access is resolved by the data source layer (data/registry.py) against one of
the adapters — pandas, SQL, file, or URL.
Core Components
1. Server Layer (server.py)
This layer implements the MCP specification. It defines the tool schemas and dispatches incoming requests to the appropriate internal modules. It also handles JSON serialization/deserialization, ensuring that complex Python types are safely converted for the LLM.
2. Registry Module (registry/)
The registry module dynamically inspects sktime. It uses sktime.registry.all_estimators to discover available models and their capabilities (tags). This ensures the server is always in sync with the installed version of sktime.
3. Runtime and Handle Registry (runtime/)
The runtime is the heart of the server’s statefulness. It maintains two primary registries:
Handle Registry: A mapping of string identifiers to active Python objects (estimators and datasets).
Job Registry: Tracks the status and results of background operations.
4. Tool Implementation (tools/)
Tools are modularized into functional groups:
Discovery: Querying the registry.
Data: Ingesting, inspecting, splitting, formatting, and plotting time series.
Execution: Instantiating, fitting, predicting, and evaluating.
Persistence: Saving models and exporting code.
Jobs: Tracking and cancelling background work.
Composition is not a separate layer. Pipelines are expressed inside the
instantiate spec string and validated by sktime’s own craft(), so the server
carries no pipeline-validation code of its own.
5. Data Layer (data/)
Loading is adapter-based. DataSourceRegistry selects an adapter from the
type key of the incoming config:
Four adapters ship today: PandasAdapter, SQLAdapter, FileAdapter, and
URLAdapter (the diagram above predates the URL adapter).
Data Flow: The “Handle” Pattern
Instantiation: The client requests a model (e.g.,
ARIMA) viainstantiate. The server crafts the object, generates a UUID-based handle, and stores it in theHandle Registry.Action: The client calls
fit, thenpredict, passing the handle. The server retrieves the object from the registry and executes the requested method.Response: The server serializes the result (e.g., forecast values) into JSON and returns it to the client.
The two flows below show this end to end — loading data to obtain a handle, and then forecasting against it:
Concurrency Model
Sync tools: Run on the main event loop. These should be fast, metadata-only operations.
Async tools:
fit,predict,evaluate, andload_data_sourceaccept arun_asyncflag. When set, the work is scheduled withasyncio.create_task, the task reference is retained by theJobManager, and ajob_idis returned immediately. This keeps a long training run from blocking the server, so the client can keep querying status or metadata.Blocking sktime calls that would otherwise stall the loop are offloaded with
run_in_executor.Because task references are retained,
cancel_jobcan actually cancel in-flight work. Cancellation is cooperative: a step already inside a blocking sktime call completes before the task stops.
Security Considerations
The trust boundary is the user account running the server. There is no authentication layer and no sandbox.
Strict typing: All tool inputs are validated against JSON schemas.
Registry-bounded modelling: The modelling tools (
instantiate,fit,predict, …) only construct classes resolvable through thesktimeregistry viacraft(). They do not evaluate caller-supplied Python.run_commandis an explicit exception. The server also exposes arun_commandtool that executes an arbitrary shell command with the server process’s full privileges, so that an assistant can install a missing optional dependency mid-session. It is not sandboxed. Any client permitted to call it can run anything the user can. Treat it as opt-in: most MCP clients can allow or deny individual tools, andrun_commandshould stay on ask-every-time — or be disabled — unless you want that capability. Running the server in a container confines it to the container.Local access: The server inherits the permissions of the user running it and can read/write any file that user can.
See the MCP Tool Reference for the per-tool detail.