Continental-MCP-Server
by neuronomid
README.md
# Continental-MCP-Server
A 24/7 data-collection, analytics, and intelligence service for **Polymarket BTC
5-minute Up/Down** markets, exposed to LLM agents over the **Model Context
Protocol (MCP)**. It runs on an Ubuntu VPS and is strictly **research-only**: no
API keys, no order placement, no bot-status decisions. Every response is framed
as *evidence* (data with sample sizes, confidence intervals, and freshness), not
trading advice.
Consumers are (a) LLM agents via **MCP**, (b) a trading bot via a read-only REST
data plane + ingest, and (c) humans via reports and Telegram alerts. The Python
package is named `pmre` (pm-research-engine); this repository is the MCP server
that fronts it.
---
## For agents: connect to the MCP server
The server speaks **Streamable HTTP** with **bearer-token auth** and is
**read-only**. Any MCP-capable client (Claude, the OpenAI Agents SDK, OpenAI's
hosted `{"type": "mcp"}` tool, custom clients) can auto-discover every tool via
`tools/list` and call it — you do not hand-wire the tools.
- **Endpoint:** `http://<host>:8090/mcp` (default port `8090`)
- **Transport:** Streamable HTTP
- **Auth:** `Authorization: Bearer <PMRE_MCP_BEARER_TOKEN>` on every request
(a missing/invalid token returns `401`)
- **Network:** bind to a private WireGuard/Tailscale overlay IP — never expose
it publicly
### Claude Code / `mcp.json`
```jsonc
{
"mcpServers": {
"continental": {
"type": "http",
"url": "http://127.0.0.1:8090/mcp",
"headers": { "Authorization": "Bearer <PMRE_MCP_BEARER_TOKEN>" }
}
}
}
```
### OpenAI Agents SDK (Python)
A complete, runnable example lives at [`examples/openai_agent_mcp.py`](examples/openai_agent_mcp.py):
```python
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async with MCPServerStreamableHttp(
name="continental",
params={
"url": "http://127.0.0.1:8090/mcp",
"headers": {"Authorization": "Bearer <PMRE_MCP_BEARER_TOKEN>"},
},
cache_tools_list=True,
) as server:
agent = Agent(name="analyst", model="gpt-4o-mini", mcp_servers=[server])
result = await Runner.run(agent, "Is the engine healthy? What is the "
"strongest candidate by CI-lower net EV?")
print(result.final_output)
```
### Response envelope
Every tool response is wrapped in a **freshness envelope** carrying
`data_last_updated_at`, `current_session`, `session_integrity`, and any
`warnings`. Decisions should be made on **CI lower bounds, never point
estimates**.
### Tools (16, all read-only)
| Tool | Returns |
| --- | --- |
| `get_system_health` | Which collectors are alive/silent + recent incidents |
| `get_current_session` | Primary/overlap session, integrity, seconds to next boundary |
| `get_current_btc5m_market` | Active + next market: token ids, price-to-beat, fee params, tick size |
| `get_latest_market_snapshot` | Latest order book: mids, spreads, depth, fee estimate |
| `get_timestamp_performance` | Per-timestamp win rate, net EV (taker/maker), CI-lower, Brier |
| `get_calibration_curve` | Reliability curve (win rate vs price) per 2¢ bin, Wilson CIs, FDR flags |
| `get_session_performance` | Performance per session/overlap (each with its own n and CI) |
| `get_regime_performance` | Performance per volatility regime |
| `get_fee_parameters` | Fee params for a market (feesEnabled, rate, model version) + history |
| `get_fair_value_snapshot` | Fair-value output: `p_fair`, `z_score`, `sigma_1s`, `model_edge` |
| `get_maker_fill_estimates` | P(fill) and time-to-fill for hypothetical maker posts |
| `get_strategy_candidates` | Strategy candidates with status + full evidence |
| `get_champion_strategy` | Current champion strategy and its CI-lower net EV (or null) |
| `get_paper_trade_performance` | Aggregated paper-trade telemetry ingested from the bot |
| `get_analysis_run_summary` | Summary of an analysis run: counts, versions, deterministic hash |
| `get_data_quality_report` | Snapshot counts, stale/crossed/close-call rates over a window |
### Resources
| URI | Contents |
| --- | --- |
| `pmre://methodology` | Calibration-first, CI-lower decisions, BH-FDR, dual-scope sessions |
| `pmre://data-dictionary` | Schema / field dictionary for the served tables |
| `pmre://fee-model` | Dynamic taker fee curve notes |
| `pmre://daily-report` | Latest daily research report (markdown) |
---
## Run the server
```bash
uv sync
# Set the MCP bearer token (and other secrets) — copy the template first.
cp .env.example .env # then edit PMRE_MCP_BEARER_TOKEN, PMRE_DATABASE_URL, ...
uv run python -m pmre migrate # create/upgrade the schema
uv run python -m pmre serve-mcp # MCP server on PMRE_SERVING_HOST:PMRE_MCP_PORT (default :8090)
```
`./run.sh` launches the whole system locally (migrate + calendar + collectors +
REST + MCP + hourly analytics). Modes: `all` (default), `serve` (no collectors),
`demo` (synthetic offline dataset), `collectors`. See the header of `run.sh`.
Relevant environment variables (all `PMRE_`-prefixed; see `.env.example`):
| Variable | Purpose |
| --- | --- |
| `PMRE_MCP_PORT` | MCP server port (default `8090`) |
| `PMRE_MCP_BEARER_TOKEN` | Bearer token agents must present |
| `PMRE_SERVING_HOST` | Bind address — set to the overlay IP |
| `PMRE_DATABASE_URL` | Postgres 16 + TimescaleDB in prod; SQLite for dev/tests |
| `PMRE_ENV` | `dev` or `production` (prod fails fast on any unset secret) |
---
## What the engine does
- **Fees are first-class** — dynamic taker-fee curve `rate·p·(1−p)`; every EV in
gross / net-taker / net-maker variants; maker entries are a first-class family.
- **Calibration-first analytics** — reliability curves (win rate vs price) with
Wilson CIs; decisions on **CI-lower net EV**, never point estimates.
- **Multiple-testing control** — Benjamini-Hochberg FDR (q=0.10) on every bucket
claim; a null dataset produces ~zero "edges" (the project's most important test).
- **Fair-value benchmark** — `p_fair = Φ(z)`, `z = ln(S/S_ptb)/(σ_1s·√τ)`,
independent of the empirical tables.
- **Session & holiday model** — tz-native Tokyo/London/New York + overlaps;
holidays are *integrity* labels (regular/holiday/half_day/weekend), not
closures. Every metric ships at `total` AND per-session scope.
- **Two facades, one service layer** — FastAPI REST (bot) + MCP (agents), both
read-only, both stamped with freshness + evidence + current session.
- **Storage triad** — PostgreSQL 16 + TimescaleDB (hypertables + compression),
daily Parquet exports, zstd raw JSONL archive (replayable).
## Layout
```
src/pm_sessions/ shared, versioned session/calendar model (the bot reuses it)
src/pmre/
config.py pydantic-settings (fails fast on missing prod secrets)
db/ SQLAlchemy models (all v1+v2 tables) + engine + timescale
collectors/ slugs, discovery, clob_ws (book engine), snapshotter,
btc_feed, resolution, calendar_job, supervisor
features/ fair_value, btc_state
analytics/ stats (Wilson/BH), ev, calibration, regime, maker_fill,
walkforward, runner, reports
registry/ candidates, gates, extractor (research_only -> ... -> disabled)
serving/ service (shared), envelope, auth, ingest,
rest/ (FastAPI), mcp/ (FastMCP) <-- MCP server lives here
ops/ health, alerts, clock, watchdog, backup, systemd_notify
parquet_export.py daily exporter + duckdb + replay
cli.py `python -m pmre <command>`
tests/ unit + integration + golden fixtures
deploy/ systemd units/timers, compose, Caddy, wireguard, runbooks
```
## REST facade (for the bot, not agents)
`serve-rest` exposes read-only `GET /v1/health|session/current|markets/current|
snapshots/latest|performance/*|candidates*|fills/maker-estimates|
fairvalue/params|fees/parameters|quality/report|analysis/summary` plus ingest
`POST /v1/ingest/paper-trades|bot-decisions|bot-heartbeat`. Bearer auth (read vs
ingest scopes), freshness envelope on every read.
## Development
```bash
uv sync
uv run pytest # full suite (SQLite; no Docker needed)
uv run ruff check src tests
# End-to-end on a throwaway DB: seed synthetic data with a planted, persistent
# NY-session edge, run analytics -> walk-forward -> candidate extraction.
export PMRE_DATABASE_URL="sqlite+pysqlite:///./pmre.db"
uv run python -m pmre migrate
uv run python -m pmre pipeline-demo --days 25 # discovers the planted edge
uv run python -m pmre daily-report
```
### Testing on real PostgreSQL + TimescaleDB
```bash
docker run -d --name pg -e POSTGRES_USER=pmre -e POSTGRES_PASSWORD=pmre \
-e POSTGRES_DB=pmre -p 55432:5432 timescale/timescaledb:latest-pg16
PMRE_TEST_POSTGRES_URL=postgresql+psycopg2://pmre:pmre@127.0.0.1:55432/pmre \
uv run pytest tests/test_postgres_integration.py
```
## Deployment
`deploy/` ships systemd units/timers (`pmre-mcp.service`, `pmre-rest.service`,
analytics/backup timers), a Docker Compose stack, a `Caddyfile`, and WireGuard
notes. Bind both facades to the overlay IP; nothing is public. See
`deploy/README.md` for install, runbooks, and chaos drills.
## Design docs
`mcp_plan.md` (v2.1 spec) and `mcp_phases.md` (the 10 build phases) document the
full design and status. All phases are implemented and tested.
---
*Research only. Every response is evidence — data with n, confidence intervals,
and freshness — not trading advice.*
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues