TapeDeck
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.**
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues