tracker-mcp
Experiment Tracker MCP
An MCP (Model Context Protocol) server that lets you query your machine-learning experiments in natural language. Train models, log every run to a local store, then ask an AI assistant — Claude Desktop, VS Code Copilot, or any MCP client — questions like "which run had the best recall?" and have it call your tools to answer.
The project is a small, self-contained demonstration of three things: a clean experiment-tracking data model, a backend-agnostic storage layer, and an intent-oriented MCP server on top of it.
What it does
train.pytrains scikit-learn models on a dataset and logs each run — hyperparameters, metrics, and the saved model file — through a tracker.The store persists runs in SQLite behind a swappable interface.
server.pyexposes the store as MCP tools, so an LLM can list, inspect, rank, and compare your experiments on request.
train models ──▶ log to store (SQLite) ──▶ MCP server ──▶ "which run is best?" (any MCP client)Architecture
The codebase is layered by responsibility, and every dependency points inward toward an interface rather than toward a concrete implementation:
server.py ─┐
├─▶ store/base.py ◀── store/sqlite_store.py ──▶ db/connection.py ──▶ schema.sql
train.py ──┘ (Tracker: the (SQLiteTracker: (connection + (the DDL)
interface) an implementation) initialization)server.py and train.py — the application edges — depend only on the abstract Tracker interface, never on SQLite. SQLite is an adapter plugged in behind the interface. This is the Dependency Inversion Principle in practice: swapping the backend (e.g. for MLflow) means adding one class in store/, and nothing above the interface changes.
Project structure
tracker-mcp/
├── pyproject.toml # dependencies + package metadata
├── .gitignore # keeps tracker.db, models, __pycache__ out of git
├── README.md
├── .vscode/
│ └── mcp.json # VS Code MCP server config
├── tracker_mcp/ # the package
│ ├── __init__.py # re-exports Tracker, SQLiteTracker
│ ├── config.py # single source of truth for filesystem paths
│ ├── db/ # ── infrastructure layer ──
│ │ ├── schema.sql # the database structure, as real SQL
│ │ └── connection.py # connection config + schema initialization
│ ├── store/ # ── repository layer ──
│ │ ├── base.py # the Tracker interface (abstract)
│ │ └── sqlite_store.py # the SQLite implementation
│ ├── train.py # ── application: trains models, logs runs ──
│ └── server.py # ── application: the FastMCP server ──
├── models/ # saved .joblib model artifacts (gitignored)
└── tracker.db # the SQLite store (gitignored)Setup
Requires Python 3.12+.
# from the project root
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install fastmcp scikit-learn joblib
pip install -e . # editable install: makes `python -m tracker_mcp.*` work anywhereThe editable install is what lets the MCP server be launched by name (-m tracker_mcp.server) from any working directory — important because MCP clients spawn the server from an unpredictable location.
Usage
1. Train some models
python -m tracker_mcp.trainThis trains four models on the scikit-learn breast-cancer dataset (a shallow decision tree as a weak baseline, a default and a tuned random forest, and gradient boosting), logs each run to tracker.db, and saves each model to models/.
2. Run the MCP server
python -m tracker_mcp.server # stdio transport — what MCP clients expectOr debug it interactively with the MCP Inspector:
fastmcp dev tracker_mcp/server.py3. Connect it to a client
Claude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"experiment-tracker": {
"command": "/absolute/path/to/tracker-mcp/.venv/bin/python",
"args": ["-m", "tracker_mcp.server"]
}
}
}VS Code (GitHub Copilot, agent mode) — .vscode/mcp.json. Note VS Code uses the servers key, not mcpServers:
{
"servers": {
"experiment-tracker": {
"type": "stdio",
"command": "/absolute/path/to/tracker-mcp/.venv/bin/python",
"args": ["-m", "tracker_mcp.server"]
}
}
}Then ask, in natural language:
"List my experiment runs."
"I'm building a cancer screening model — which run should I use?"
"Compare the tuned random forest and gradient boosting."
"Show me the details of run
910ced952bb6."
The tools
The server exposes tools designed around what a user asks, not a 1:1 mirror of the store's methods:
Tool | Purpose |
| Recent runs, newest first (summary). |
| Full detail of one run, including all metrics. |
| The run with the best value for a metric ( |
| Several runs side by side, by params and metrics. |
It also exposes each run as a read-only resource at experiment://{run_id}, and ships a server-level instructions block — domain context (what a run is, which metric fits which goal) delivered to the model during the MCP handshake, so it reasons about experiments correctly without the user re-explaining each turn.
Design decisions
These are the choices worth understanding, and the reasoning behind them.
Two tables, split by access pattern. Runs get one table; metrics get their own. Hyperparameters are heterogeneous across model types and rarely queried individually, so they're stored as a single JSON column. Metrics are the opposite — you constantly rank and compare across them, and they can form time series over training steps — so they live in a normalized table where MAX(value) WHERE key = 'accuracy' is trivial. This params-as-JSON, metrics-as-rows asymmetry is the core of the data model.
Backend-agnostic storage. The server and training code depend on the abstract Tracker interface, so the SQLite backend is a detail, not an assumption. A different backend (MLflow, Postgres) would be a new class behind the same interface.
Intent-oriented tools. MCP tools are shaped around user questions (find_best_model, compare_experiments) built on top of the store's primitives, rather than mechanically exposing get_run/list_runs. Clear, distinct tool descriptions are what let the model pick the right tool.
Single source of truth for paths. config.py anchors the database and model directory to the project root via __file__, so the training script (writer) and the server (reader) always resolve to the same files regardless of the working directory a command is launched from. Env vars (TRACKER_DB, TRACKER_MODELS) override when needed.
Reproducibility as a logged value. The random seed lives inside each run's logged params, so a stored run can be recreated exactly — the same dict both builds the model and gets recorded.
Failures are recorded. A run is created before training starts and marked failed if training raises, so the experiment history reflects reality rather than only successes.
Tech stack
FastMCP (3.x) — the MCP server framework
scikit-learn — model training
SQLite (stdlib
sqlite3) — the tracking storejoblib — model serialization
Possible extensions
An
MLflowTrackerimplementation behind the sameTrackerinterface.A test suite under
tests/(the in-memoryfastmcp.Clientmakes end-to-end tool tests straightforward).Per-step metric logging to support training curves, and a
best_runthat ranks by each run's final step.A
delete_run/ archival tool, and filteringlist_experimentsby model type or dataset.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/Lesvitadsouza/tracker-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server