Skip to main content
Glama
README.md
# TapeDeck

> An MCP server engineered like production infrastructure: context-budgeted responses,
> an adversarially-tested SQL sandbox, measured query benchmarks, and zero hallucinated
> numbers.

TapeDeck gives any Model Context Protocol client a compact market-data and quant-research
surface over a local Parquet lake. It combines DuckDB SQL analytics, causal indicators,
an event-driven next-open backtester, and a hostile-input SQL guard. It never routes an
order and needs no API key.

The committed offline dataset contains **203,460 labelled sample rows**: 120,000 synthetic
Poisson/regime-switching aggregate trades for BTCUSDT and ETHUSDT, plus 83,460 synthetic
volatility-clustered daily bars for 20 equities from 2010-01-04 through 2025-12-31. Every
tool returns `source_label`, so an agent cannot quietly present sample observations as live
market facts.

```mermaid
flowchart LR
  A["Binance archive"] --> I["Idempotent ingest"]
  B["Stooq daily CSV"] --> I
  C["Deterministic offline samples"] --> I
  I --> P["Hive-partitioned Parquet"]
  P --> D["DuckDB views and macros"]
  D --> G["SQL AST guard"]
  D --> Q["Indicators, scans, microstructure"]
  D --> E["Next-open event backtester"]
  G --> M["Budgeted FastMCP tools"]
  Q --> M
  E --> M
  M --> Z["Codex, Claude, and MCP clients"]
```

## 60-second offline demo

Python 3.11 or newer is required. After the one-time dependency installation, every demo,
test, benchmark, and server operation is local; only the two explicitly named ingest scripts
can make network requests.

```bash
python3.11 -m venv .venv
.venv/bin/pip install -e '.[dev]'
make demo
```

The demo connects in memory through a real FastMCP client, calls all nine tools, reads all
three resources, renders all three prompts, and writes the complete result to
`data/demo_output.json`. Re-running `make samples` is idempotent.

The verified build uses the external `fastmcp` package (version 2.14.7 in the recorded
environment), rather than the fallback FastMCP bundled with the official MCP Python SDK.

## Tool surface

| Tool | What it returns |
|---|---|
| `list_symbols` | Asset class, coverage, row count, sources, and live/sample label |
| `get_bars` | Equity daily bars or crypto 1s/1m/5m/1h/1d OHLCV, VWAP, and count |
| `compute_indicators` | Causal SMA, EMA, RSI, returns, realized vol, z-score, and drawdown |
| `scan_market` | Ranked momentum, z-score, volatility, and 52-week-break snapshots |
| `volume_profile` | Price-bucketed volume and point of control |
| `order_flow` | Crypto aggressor-side buy/sell volume and normalized imbalance |
| `run_backtest` | CAGR, Sharpe plus bootstrap CI, drawdown, turnover, trades, and curve |
| `sql_query` | One AST-validated, read-only, timed and row-capped DuckDB SELECT |
| `describe_schema` | Agent-readable view, macro, column, and semantic documentation |

Detailed signatures and real request/response fragments are in
[`docs/TOOLS.md`](docs/TOOLS.md). The `tapedeck://manifest`, `tapedeck://schema`, and
`tapedeck://strategies` resources support agent planning; `analyze_symbol`,
`daily_market_review`, and `strategy_evaluation` provide evidence-disciplined prompt flows.

## Real demo fragment

This is trimmed from `data/demo_output.json`, produced on the committed sample lake:

```json
{
  "scan_market": {
    "rank": 1,
    "symbol": "MSFT",
    "zscore_20d": 2.108462,
    "momentum_252d": 0.16218052,
    "source_label": "sample"
  },
  "sma_crossover_SPY": {
    "cagr": 0.03190133,
    "sharpe": 0.27361,
    "sharpe_ci_95": [-0.21129888, 0.75640762],
    "max_drawdown": -0.43555141,
    "trade_count": 59
  }
}
```

These values describe deterministic synthetic data. They are software evidence, not market
evidence or an investment result.

## Measured benchmark headlines

<!-- BENCHMARK_HEADLINES_START -->
- Resampled 120,000 ticks to one-minute bars in **7.423 ms warm** (19.275 ms fresh-connection median).
- Ran the 16-year SPY SMA backtest in **91.130 ms warm** (146.534 ms including data reload).
- Wrote Parquet at **2,038,130 rows/second** over 5 measured runs.
<!-- BENCHMARK_HEADLINES_END -->

Definitions, machine details, 20-run medians, and raw JSON are in
[`docs/BENCHMARKS.md`](docs/BENCHMARKS.md) and `data/bench.json`. `make bench` rewrites the
two artifacts and synchronizes the block above, preventing hand-edited performance claims.

## Run and connect

```bash
make serve                 # stdio
make serve-http PORT=8765  # streamable HTTP at http://127.0.0.1:8765/mcp
```

Ready-to-paste files and commands for Codex, Claude Code, Claude Desktop, generic stdio,
streamable HTTP, and MCP Inspector are in [`clients/README.md`](clients/README.md). The
Codex instructions use the current shared host configuration:

```bash
codex mcp add tapedeck -- /Users/sanjeevkumar/Documents/TapeDeck/.venv/bin/python -m tapedeck.server
```

## Security model

`sql_query` parses with `sqlglot` before DuckDB sees text. It admits one SELECT-family AST,
rejects mutation/configuration/extensions/external readers, confines permitted Parquet/CSV
paths beneath `data/`, wraps a row limit, and interrupts a timed query. Execution uses a
read-only DuckDB connection with a 256 MB session memory limit. The test suite contains 30
explicit injection and escape attempts plus row-cap and hard-interruption cases. See
[`docs/SECURITY.md`](docs/SECURITY.md) for the threat model and residual risks.

## Data ingestion

The only network-capable commands are explicit:

```bash
.venv/bin/python scripts/ingest_binance.py --symbols BTCUSDT ETHUSDT --days 2026-07-29
.venv/bin/python scripts/ingest_stooq.py --symbols SPY AAPL MSFT
```

Both skip existing partitions, use finite timeouts, write atomically, refresh
`data/manifest.json`, and materialize the committed deterministic samples after any fetch
failure. `make ingest-offline` exercises that fallback without opening a network connection.
Tick data is partitioned by symbol and UTC day. Daily data uses symbol/year partitions to
avoid tens of thousands of one-row Parquet files while retaining a session-level date column.

## Limitations

- Equities are end-of-day, split-unadjusted OHLCV; equity volume profiles allocate each
  session's volume to its close, and equity order flow is unavailable.
- The committed lake is deliberately synthetic and labelled `sample`; run the ingest scripts
  to add source data where network access permits.
- The deployment is single-node and unauthenticated. Bind HTTP to localhost unless an
  authenticating reverse proxy and tenant isolation are added.
- Aggregate trades are not a limit-order book and cannot reconstruct queue position or L2
  depth.
- Backtests are research simulations with a simplified cost model, not executable returns.

## Verification

```bash
make test
make lint
make demo
make bench
make ingest-offline
```

Design rationale is in [`docs/DESIGN.md`](docs/DESIGN.md), and worked agent flows are in
[`docs/COOKBOOK.md`](docs/COOKBOOK.md).

**Research and education only. TapeDeck does not place trades and is not financial advice.**