A personal, single-process AI data science & quantitative research agent server featuring Claude Code / Claude Agent SDK in the browser, BYOK (Bring Your Own Keys), inline plot/dataset artifact rendering, and built-in MCPs for Kaggle, Google Colab, Data Science Execution, Quantitative Research, and Academic Literature.
ds-agent is an autonomous assistant built specifically for end-to-end data science and quantitative research workflows: exploratory data analysis (EDA), statistical & econometric modeling, strategy backtesting, machine learning benchmarking, scientific literature retrieval, and cloud compute execution.
- Official Kaggle MCP: Search, query, and download datasets, competition data, models, and benchmarks directly via Kaggle's official remote MCP gateway.
- Programmatic Google Colab MCP: Provision cloud runtimes (CPU, T4, L4, A100, TPU), execute heavy training/inference scripts, install remote dependencies, and capture plots—no browser automation needed.
- Dedicated Data Science Environment & MCP (
ds_mcp): Fast, isolated data science environment (pandas,polars,numpy,scikit-learn,scipy,statsmodels,seaborn,plotly,pyarrow, etc.) with one-call dataset inspection (ds_preview) and code execution (ds_run). - Quantitative & Macroeconomic Research: Integrated Federal Reserve Economic Data (FRED) API series query, statistical/econometric modeling (
statsmodels,scipy), financial backtesting guidance (vectorbt,backtesting.py), and quantitative finance paper search (q-finon arXiv). - Academic & Bio/Quant MCP (
research_mcp): 12 search tools across arXiv, Semantic Scholar, OpenAlex, PubMed, bioRxiv, Hugging Face (models & datasets), UniProt, PDB, Ensembl, and FRED economic time-series. - Cross-Session Memory & Search (
agent_mcp): the agent can search every other session's messages and workspace files for prior work, pull in a summary, and save durable facts/preferences to a persistent memory shown at the top of every future session's prompt. - Session Export: download any session's messages + artifacts as a zip — a button in the web UI's chat header, or
/exportin Telegram. - Inline Artifact Rendering: Matplotlib figures, Seaborn charts, and Plotly visualizations render inline immediately. CSVs, Parquet files, JSON data, and Markdown reports surface as one-click downloads.
- Token Economy & Prompt Caching: Real-time cost calculation, cache hit tracking (
cache_read_tokens), automated context compaction, and output trimming. - Zero-Build Web UI: Clean, responsive interface featuring streaming chat, live tool inspection, an embedded xterm.js terminal, dataset uploader (up to 2 GB), and workspace file browser.
┌──────────────────────────────┐
│ Web Browser UI │
│ (HTMX + Alpine.js + xterm.js)│
└──────────────┬───────────────┘
│
│ WebSocket / HTTP (:8765)
▼
┌─────────────────────────┐ ┌──────────────────────────────┐
│ BYOK Vault │──────►│ FastAPI Agent Server │
│ (Fernet Encrypted Keys) │ │ (src/ds_agent/app.py) │
└─────────────────────────┘ └──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Claude Agent SDK Client │
│ (Warm Subprocess per Turn) │
└───┬──────────┬───────────┬───┘
│ │ │
┌────────────────────┘ │ └────────────────────┐
▼ ▼ ▼
┌─────────────────────────────┐ ┌───────────────────────────┐ ┌─────────────────────────────┐
│ Kaggle MCP │ │ Google Colab MCP │ │ DS Environment MCP │
│ (Official Remote MCP) │ │ (google-colab-cli runtime)│ │ (Dedicated Python Sandbox) │
│ • Datasets & Competitions │ │ • T4 / L4 / A100 / TPU │ │ • ds_preview & ds_run │
│ • Models & Benchmarks │ │ • Remote Python execute │ │ • EDA, Pandas, Scikit-Learn│
└─────────────────────────────┘ └───────────────────────────┘ └─────────────────────────────┘
Each session receives an isolated .mcp.json composed dynamically from the global registry (mcp.json / ~/.coding-agent/mcp.json).
Connects directly to Kaggle's official remote MCP at https://www.kaggle.com/mcp via mcp-remote, giving the agent access to Kaggle's data catalog, competitions, and models:
- Search and download competition files and test sets.
- Query community datasets and pull metadata or CSVs.
- Explore model weights and benchmark leaderboards.
Setup:
- Generate an API token on Kaggle (Settings → Create New Token, starts with
KGAT…). - Enter the token in the UI (Settings → BYOK keys → provider
kaggle). - The token is securely injected via
${VAULT:kaggle}without plaintext leakage.
Powered by src/colab_mcp/colab_server.py wrapping google-colab-cli, enabling the agent to manage and run code on Google Colab hardware without opening a browser:
| Tool | Action |
|---|---|
colab_auth |
Run or finish the OAuth authentication handshake. |
colab_new |
Spin up a fresh runtime (CPU, T4, L4, A100, TPU). |
colab_status |
Query active compute session state and resource usage. |
colab_execute |
Execute Python code remotely; returns stdout and image outputs. |
colab_upload |
Upload a local file onto the runtime's /content dir for colab_execute code to open. |
colab_install |
Install packages via pip on the remote Colab instance. |
colab_sessions |
List all active runtime instances associated with the account. |
colab_stop |
Terminate and release the remote runtime. |
Auth Flow:
Google Colab uses PKCE OAuth (code_verifier). colab_auth's own two-step flow (call it with no arguments to get a URL, then again with code=...) now correctly reuses the same PKCE verifier across both calls — a bug that used to make that path always fail with invalid_grant regardless of the code being valid, which is why the flow below existed as the workaround. It still remains the recommended default for first-time setup (no back-and-forth through the agent chat needed); use the two-step colab_auth flow instead when you don't have local shell access (e.g. a pure remote/cloud session):
-
Authorize locally once (on your development machine):
bash src/colab_mcp/setup.sh src/colab_mcp/.venv/bin/python src/colab_mcp/auth_once.py
Open the printed URL, log in with your Google account, and paste the code back into your terminal. The token will be saved to
~/.config/colab-cli/token.json. -
Auto-synced to remote: When you run
bash scripts/deploy_remote.sh, your local~/.config/colab-cli/token.jsonis automatically copied to/home/agent/.config/colab-cli/token.jsonon the remote server with secure permissions (600). -
Manual copy to existing remote (if needed):
scp ~/.config/colab-cli/token.json root@<REMOTE_IP>:/home/agent/.config/colab-cli/token.json ssh root@<REMOTE_IP> "chown agent:agent /home/agent/.config/colab-cli/token.json && chmod 600 /home/agent/.config/colab-cli/token.json"
The token auto-refreshes indefinitely.
Isolates analytical workloads in a dedicated Python environment located at ~/.coding-agent/ds-env/ (pre-populated with top scientific computing libraries):
| Tool | Action |
|---|---|
ds_preview |
Automated EDA: Inspects CSV, Parquet, Excel, Feather, or JSON files. Returns row/col dimensions, column types, null counts, sample head rows, and distribution summaries. |
ds_run |
Runs Python code or analytical scripts in the DS environment (with configurable timeouts up to 600s). |
ds_env |
Returns the Python interpreter path and installed library versions. |
ds_install |
Installs additional libraries (xgboost, lightgbm, torch, vectorbt, yfinance, etc.) into the DS environment. |
A unified search server with 12 tools for literature and biological/economic data:
- Macroeconomics & Quantitative Data:
fred_series(Federal Reserve Economic Data for interest rates, inflation, GDP, yield curves, monetary indicators). - Literature:
arxiv_search(supporting CS, stats,q-bio, andq-fin),semantic_scholar_search,openalex_search,pubmed_search,biorxiv_search,crossref_lookup. - Machine Learning & Data:
hf_search_models,hf_search_datasets. - Bioinformatics:
uniprot_search,pdb_search,ensembl_search.
Gives the agent itself — not just the human user — visibility across every session, so it can reuse prior work instead of redoing it, plus a small persistent notebook that survives across sessions:
| Tool | Action |
|---|---|
list_sessions |
List every session (id, title, provider/model, last active). |
search_other_sessions |
Substring search over every other session's messages and workspace filenames (plots, csvs, notebooks, ...) — e.g. when the user says "the model I trained yesterday". |
get_session_summary |
Pull another session's messages as plain text (by id or prefix) for full context after locating it. |
remember |
Save a durable fact/preference/decision (tags optional) — shown automatically near the top of every future session's system prompt. |
recall / forget |
List (optionally filtered) or delete saved memories. |
Backed by the same state.db SQLite file as the web UI/Telegram bot — no
separate index to keep in sync. .mcp.json / .claude/settings.local.json
(which hold resolved provider API keys per session) are never searched or
surfaced by these tools.
Interact directly with the agent from Telegram on mobile or desktop. It runs
as a background long-polling worker (src/ds_agent/telegram.py) alongside
the FastAPI server whenever a bot token is configured — no separate process
or public webhook needed.
Setup:
- Create a bot with @BotFather and grab its token.
- Add to
.env:TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrSTUvwxYZ" # Optional whitelist of Telegram user IDs (comma-separated); if unset, anyone can use the bot TELEGRAM_ALLOWED_USERS="12345678,87654321"
- Start the server (
bash scripts/run_server.sh) and message your bot on Telegram — an unauthorized user gets their numeric ID back in the denial message, handy for populatingTELEGRAM_ALLOWED_USERS.
Commands:
| Command | Action |
|---|---|
/start, /help |
Welcome message and usage instructions |
/models [query] |
List/filter live or curated model IDs as tappable buttons for /new |
/provider [name] |
Show configured BYOK providers, or set the default one /new uses (tappable buttons, or /provider openrouter) |
/new [model] |
Start a fresh session (defaults to claude-sonnet-4.5 on the chat's default provider, initially OpenRouter or the first configured BYOK provider) |
/sessions |
List the 10 most recent sessions, marking the active one |
/switch <id> |
Switch the chat to a different session (prefix match on session ID) |
/compact |
Compact the active session's context |
/stop |
Interrupt the agent mid-turn |
/status |
Show the active session's ID, title, model, provider, and workspace path |
/export |
Zip the active session's messages + artifacts and send it as a Telegram document (skips the send with a message if the zip is over ~45 MB — download it from the web UI instead) |
Behavior:
- Each Telegram chat maps to one ds-agent session at a time (
/newcreates one automatically on first message if none exists), with a per-chat lock so turns don't overlap. - Assistant replies stream as edited messages; thinking blocks and intermediate tool narration are filtered out so only the end result is sent, with Markdown converted to Telegram-safe HTML (code blocks preserved) — the bot no longer posts a separate "Turn finished" / cost summary after every reply (check
/statusor the web UI for cost/usage). - Generated plots/artifacts are automatically attached as photos or documents.
- Send a file (CSV, JSON, Parquet,
.py, etc.) directly in chat to upload it into the active session's workspace — the agent is prompted to inspect it automatically.
ds-agent is optimized for quantitative analysis, econometric modeling, and algorithmic research:
-
Macroeconomic Indicators (FRED API):
- Call
fred_series(series_id="DGS10")(10-Year Treasury Yield),fred_series(series_id="CPIAUCSL")(Consumer Price Index),fred_series(series_id="FEDFUNDS")(Federal Funds Effective Rate), etc. - Set
FRED_API_KEYin environment or UI settings for full access.
- Call
-
Time-Series & Econometrics:
statsmodels&scipypre-installed for ARIMA, GARCH, vector autoregression (VAR), cointegration tests, and OLS factor regressions.- High-performance data manipulation via
pandas,polars, andpyarrow.
-
Strategy Backtesting & Quantitative Modeling:
- Agent prompts are tuned for backtesting workflows (
vectorbt,backtesting.py). - Seamless installation of market data toolkits (
yfinance,pandas-ta,quantstats) viads_installorDS_EXTRA_PACKAGES.
- Agent prompts are tuned for backtesting workflows (
-
Financial Visualizations:
- Generate interactive Plotly or Matplotlib equity curves, drawdown charts, correlation matrices, and candlestick charts, instantly surfaced in the chat UI via
__ARTIFACT__:plot:....
- Generate interactive Plotly or Matplotlib equity curves, drawdown charts, correlation matrices, and candlestick charts, instantly surfaced in the chat UI via
When executing data science scripts, the agent surfaces visual plots, tables, and reports directly in the chat stream using stdout markers:
__ARTIFACT__:<kind>:/absolute/path
- Visualizations (
plot,png,jpg,jpeg,svg): Rendered directly as interactive image elements in the chat. - Datasets & Tables (
csv,json,text,md,html,ipynb): Displayed with instant download links and preview cards. - Documents (
pdf): Rendered with inline preview links.
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import seaborn as sns
df = sns.load_dataset('penguins')
sns.scatterplot(data=df, x='flipper_length_mm', y='body_mass_g', hue='species')
plt.title('Penguin Morphology Analysis')
plt.savefig('/workspace/penguin_morphology.png', bbox_inches='tight')
print('__ARTIFACT__:plot:/workspace/penguin_morphology.png')- Python 3.11+
- uv (fast Python package manager)
- Node.js (npx) (for Kaggle and filesystem MCP bridges)
# Clone the repository
git clone git@github.com:davidmiheev/ds-agent.git
cd ds-agent
# Sync project dependencies
uv sync
# Set up the dedicated data-science environment (pandas, scikit-learn, etc.)
bash scripts/install_ds_env.sh
# Optional: include extra quant / ML libraries
DS_EXTRA_PACKAGES="yfinance vectorbt pandas-ta xgboost torch" bash scripts/install_ds_env.shCreate a .env file in the project root:
# Optional default provider API key
OPENROUTER_API_KEY=sk-or-v1-...
# Optional Web UI login password (leave unset for passwordless local mode).
# Ignored if Telegram OTP login is configured instead — see "Web Login" below.
APP_PASSWORD=your-secure-password
# Optional FRED API key for economic data (free at https://fred.stlouisfed.org)
FRED_API_KEY=your-fred-api-keybash scripts/run_server.shOpen http://localhost:8765 in your browser, create a new session, and start exploring datasets or training models.
Three modes, picked automatically based on what's configured:
- Passwordless (default):
APP_PASSWORDunset and no Telegram OTP configured — fine forlocalhostonly. - Password: set
APP_PASSWORDin.env. - Telegram one-time code (recommended once the Telegram bot — see Telegram Bot Integration below — is set up; replaces the password entirely): configure
TELEGRAM_BOT_TOKENand either a singleTELEGRAM_ALLOWED_USERSentry or an explicitTELEGRAM_OTP_CHAT_ID. The/loginpage then shows a "send code to Telegram" button instead of a password field — click it, a 6-digit code arrives in the chat, and entering it (within 5 minutes, up to 5 attempts, one resend per 30s) signs you in.APP_PASSWORDis ignored while this mode is active.
# .env — Telegram OTP login (no APP_PASSWORD needed)
TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrSTUvwxYZ"
TELEGRAM_ALLOWED_USERS="12345678" # single ID → also used as the OTP recipientds-agent supports direct API keys from multiple LLM providers:
- OpenRouter
- Anthropic
- MiniMax
- Custom Anthropic-compatible endpoints
Keys are stored locally with Fernet encryption in ~/.coding-agent/keys.enc (derived from APP_PASSWORD) or ~/.coding-agent/keys.json.
ds-agent/
├── src/
│ ├── ds_agent/ # Core FastAPI web server & agent controller
│ │ ├── app.py # FastAPI routing, WebSocket streaming bridge
│ │ ├── core.py # Paths, home directory, environment resolution
│ │ ├── db.py # SQLite database for sessions, tokens, and cookies
│ │ ├── crypto.py # Fernet encryption for BYOK key vault
│ │ ├── providers.py # Environment variables & gateway configs per provider
│ │ ├── sessions.py # Claude Agent SDK subprocess lifecycle & message bus
│ │ ├── artifact_parser.py# Parser for __ARTIFACT__: markers -> inline HTML
│ │ ├── trim.py # Intelligent tool-result output trimmer
│ │ ├── agent_prompt.py # Data science & quant system prompt & artifact guidelines
│ │ ├── model_catalog.py # Model registry & live OpenRouter catalog fetcher
│ │ ├── templates/ # Jinja2 HTML templates
│ │ └── static/ # Client-side JavaScript (HTMX, Alpine, xterm, artifacts)
│ │
│ ├── colab_mcp/ # Google Colab MCP server
│ │ ├── colab_server.py # stdio MCP server wrapping google-colab-cli
│ │ └── setup.sh # Colab environment setup script
│ │
│ ├── ds_mcp/ # Data Science execution MCP
│ │ └── server.py # ds_preview, ds_run, ds_env, ds_install tools
│ │
│ └── research_mcp/ # Academic literature & scientific/economic data MCP
│ └── server.py # arXiv (CS/q-fin), PubMed, FRED, OpenAlex, HuggingFace
│
├── deploy/
│ └── Caddyfile # 5-line Caddy reverse proxy configuration with automatic TLS
├── docs/ # Architecture notes (arch.md), roadmap (todo.md), CI/CD docs (ci-cd.md)
├── scripts/
│ ├── run_server.sh # Startup script on port 8765 (with auto port-reclaim)
│ ├── install_ds_env.sh # Installer for dedicated ~/.coding-agent/ds-env
│ └── deploy_remote.sh # One-command remote VPS deploy (app + MCPs + systemd)
├── tests/ # End-to-end WebSocket tests & MCP verification
├── mcp.json # Global MCP registry configuration
└── pyproject.toml # Project dependencies and packaging definition
Run end-to-end integration tests (requires server running on port 8765):
# Start server in background
bash scripts/run_server.sh &
# Create a test session and verify plot generation & WebSocket events
SID=$(curl -s -X POST http://localhost:8765/v1/sessions \
-H 'Content-Type: application/json' \
-d '{"provider":"openrouter","model":"anthropic/claude-sonnet-4-5","title":"test"}' \
| python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')
uv run python tests/test_ds2.py "$SID"Deploy a fully working agent (app + all 6 MCP servers + systemd service) to any Ubuntu/Debian VPS with a single command:
# from the project root (a .env with your keys must exist)
bash scripts/deploy_remote.sh <REMOTE_IP> <PATH_TO_SSH_KEY>
# e.g.
bash scripts/deploy_remote.sh 203.0.113.10 ~/.ssh/id_ed25519_1Both arguments are optional — the script asks for them interactively if omitted.
Optional env overrides: REMOTE_USER (default root), REMOTE_PORT (default 22),
REMOTE_DIR (default /opt/coding-agent), APP_PORT (default 8765).
The script:
- Copies the project (rsync) and
.env(chmod 600) to the host. - Installs Node.js (for the
filesystem+kaggleMCPs) if missing. - Creates a non-root
agentuser — the agent CLI refuses--dangerously-skip-permissionsunder root, so the service must not run as root. - Installs
uv, builds the app venv (uv sync), the Colab MCP venv, and the data-science env (ds-env). - Installs the live MCP config to
~/.coding-agent/mcp.json. - Installs and starts the
coding-agentsystemd service (auto-start on boot, restart on failure). - Health-checks
/healthzand verifies every MCP server with an initialize handshake.
Then open http://<REMOTE_IP>:8765 and log in — with your APP_PASSWORD, or via a Telegram one-time code if you configured that instead (see Web Login above).
Useful on the remote:
systemctl status coding-agent # service state
journalctl -u coding-agent -f # live logs.github/workflows/deploy.yml runs scripts/deploy_remote.sh automatically
from GitHub Actions (push to main, or manually via workflow_dispatch).
See docs/ci-cd.md for the required repository secrets
and how it's configured.
To host ds-agent on a public VPS with HTTPS via Caddy:
# Set public mode and a strong password in .env
echo 'APP_PUBLIC=1' >> .env
echo "APP_PASSWORD=$(openssl rand -base64 32)" >> .env
# Install Caddy reverse proxy
sudo apt update && sudo apt install -y caddy
sudo cp deploy/Caddyfile /etc/caddy/Caddyfile
sudo systemctl reload caddy
# Launch agent server
bash scripts/run_server.sh
⚠️ APP_PUBLIC=1setsSecurecookies, so it only works behind TLS (e.g. Caddy with a real domain). On a raw IP without TLS keepAPP_PUBLIC=0.