Time-Travel Benchmark MCP Server
Enables LLMs to perform web search and fetch with a frozen cutoff date, enforcing server-side time-lock and usage budgets to prevent lookahead contamination.
README
Time-Travel Benchmark
You can freeze the internet at any past date and let an LLM do real research inside that frozen world. This repo is a working rig for exactly that: a time-lock proxy over OpenReward's backdated search (built by General Reasoning), sealed model runners, and a scoring harness, demonstrated by locking five frontier models inside June 10, 2026 and making them predict the 2026 FIFA World Cup one day before kickoff.
Why bother? Because evaluating a model's judgment about the future is usually impossible: by the time you know the answer, every model has read it. Backdated search removes the lookahead. The model searches, fetches, and cites a web that ends on a date you choose, so you can backtest forecasting agents against any past event, benchmark reasoning with zero contamination from the outcome, or reconstruct exactly what the world knew the day before anything happened. The World Cup run here is the worked example, not the product.
Reality, for the record: Spain beat Argentina 1-0 in the final on July 19, 2026. Three of the five sealed models called Spain.

The sealed leaderboard, graded against the real result. Every model was locked to the same June 10 snapshot with the same tool budget.
The tricky parts
The cutoff must be unforgeable
Giving a model a "please pretend it's June 10" prompt proves nothing; it has to be physically unable to see past the date. The proxy in src/mcp-server.mjs exposes exactly two tools, web_search and web_fetch, and injects as_of: 2026-06-10 server side on every OpenReward call. The parameter never appears in the tool schema, so the model cannot change it or even see it. On top of OpenReward's own enforcement, the proxy re-checks every returned crawl timestamp and withholds anything that postdates the cutoff (in the five runs, that second filter caught 0 pages, which is what you want to see). Budgets are enforced in the same place: 10 searches, 15 fetches, 25 calls total per model, with the remaining budget echoed after each call so the model can plan.
Sealing a leaky harness
Four of the models ran through the Claude Code CLI harness (the Claude pair directly, MiniMax and Qwen via Anthropic-compatible endpoints), and that harness ships with tools that would break the seal instantly: live web search, shell, file system. src/run-model.mjs disallows every built-in tool and passes a strict MCP config so the time-lock proxy is the only tool surface left. GPT was the awkward one. The plan was Codex CLI in its read-only sandbox, and the proxy's streamable HTTP mode on localhost (WC_HTTP_PORT) exists for that, but codex exec auto-cancels custom MCP tool calls. So the shipped GPT run uses src/run-gpt-api.mjs instead: a direct OpenAI Responses API loop where the only tools the model receives are web_search and web_fetch as function definitions, wired to the same localhost proxy, so the caps, the cutoff, and the event log all stay server side. Each model also gets its own proxy instance, so budgets and event logs never mix.
Models invent citations, so citations are verified
Every tool call is appended to a JSONL event log: queries, returned URLs, titles, crawl dates. After a run, src/schema.mjs validates the model's prediction against that log. A citation only counts if its URL actually appeared in that model's own tool results and its crawl date is on or before the cutoff. Same for every sourceUrl behind every decisive signal. Hallucinated references fail validation instead of decorating the writeup. Across the five runs the models made 31 archived searches and 37 archived fetches, and all 41 citations in the final artifact trace back to logged tool results.
Reality is quarantined from the sealed phase
The true outcome lives in one module, src/actual-result.mjs, and exactly one thing imports it: the scoring step, which runs after all sealed runs complete. The proxy, the runners, and the orchestrator have no path to it, and test/seal.test.mjs enforces that: it fails the suite if any sealed module ever references the result module, or if the prompt leaks the outcome.
The caveat you cannot engineer away
Web access can be frozen. Weights cannot. A model released after the tournament may have absorbed the result during training, and no prompt guarantees it ignores that. Every contestant therefore carries a badge based on its public release date versus the cutoff:
| Model | Release date | Badge |
|---|---|---|
| Claude Opus 4.8 | 2026-05-28 | pre-event model |
| MiniMax M3 | 2026-06-01 | pre-event model |
| Claude Fable 5 | 2026-06-09 | pre-event model |
| GPT-5.6 Sol | 2026-07-09 | web-locked only |
| Qwen3.8 Max (preview) | 2026-07-19 | web-locked only |
Treat the "web-locked only" rows as an exhibit, not as evidence of foresight. The pre-event models are the canonical experiment. There is one encouraging detail: GPT-5.6 Sol was released three weeks after the final and still picked France, which is the behavior you'd expect if the lock, not the weights, was driving the research.
Results
Scored with the Futuresight Score (0 to 100): 50 points for the correct champion, 20 for the correct runner-up, 7.5 per correct semifinalist compared as a set. Ties break on champion confidence, then semifinalist count. Dark horse and disappointment picks were for entertainment only.
| Rank | Model | Champion pick | Runner-up pick | Semis correct | Score |
|---|---|---|---|---|---|
| 1 | Qwen3.8 Max | Spain (27%) | Argentina | 3/4 | 92.5 |
| 2 | Claude Opus 4.8 | Spain (26%) | France | 4/4 | 80 |
| 3 | Claude Fable 5 | Spain (18%) | England | 4/4 | 80 |
| 4 | GPT-5.6 Sol | France (16%) | Argentina | 3/4 | 42.5 |
| 5 | MiniMax M3 | France (18%) | Spain | 3/4 | 22.5 |
Qwen3.8 Max named the exact final, Spain over Argentina. Opus 4.8 and Fable 5 both nailed all four real semifinalists. Every model got at least three of the four.

The winning run. Qwen3.8 Max called Spain over Argentina, the exact final, from inside the frozen snapshot.

The control that matters: GPT-5.6 Sol was released after the tournament ended and still predicted France, suggesting the sealed web, not memorized results, shaped the answers.
Repository layout
| Path | What it is |
|---|---|
src/mcp-server.mjs |
The time-lock proxy (MCP server): server-enforced as_of, post-cutoff rejection, per-model budgets, response cache, JSONL event log |
src/prompt.md |
The sealed prompt every model received |
src/models.mjs |
The contestant roster: adapters, model IDs, release dates |
src/run-model.mjs |
Runs one sealed model (adapter per harness, all built-in tools disabled) |
src/run-experiment.mjs |
Orchestrates all runs, two at a time, then assembles the sanitized artifact |
src/run-gpt-api.mjs |
The GPT runner: Responses API function calling against the same proxy (codex exec cancels custom MCP tools) |
src/actual-result.mjs |
The real outcome; imported only by scoring |
src/score.mjs |
Futuresight scoring and leaderboard |
src/schema.mjs |
Prediction schema plus citation-vs-event-log validation |
src/build-cards.mjs |
Renders the share cards with Playwright |
data/world-cup-2026.json |
Full sanitized artifact: predictions, event logs, citations with crawl dates, usage |
data/scored.json |
The artifact after scoring, with the leaderboard |
data/events-*.jsonl |
Per-model tool event logs |
cards/ |
Generated share images |
Running it
npm install
cp .env.example .env # then fill in your keys
node src/run-experiment.mjs # all sealed runs (needs the model CLIs installed)
node src/score.mjs # scores a fresh run, or the shipped data/ artifact
node src/build-cards.mjs # renders the cards (npx playwright install chromium first)
npm test # scoring tests + the seal test
Scoring and card rendering work straight from a clone: both fall back to the sanitized artifacts in data/ when no fresh runs/ output exists. A default experiment costs at most 25 OpenReward calls per model (125 for the full five), minus cache hits. The runners assume locally installed CLI harnesses plus local endpoint and key files for MiniMax and Qwen; swap in your own model access in src/models.mjs and src/run-model.mjs.
Point it at your own event
The rig is not football specific; the card renderer is. To replay a different event, change the cutoff date (it lives in src/mcp-server.mjs, src/models.mjs, src/schema.mjs, and src/prompt.md, all four), rewrite the prompt, and set the outcome in src/actual-result.mjs. The same machinery then covers any past event: an election, an earnings call, an award season, a product launch. Expect to rewrite src/build-cards.mjs too, since its layout and copy are baked for this tournament. The expensive part, a searchable web frozen at an arbitrary date, is OpenReward's problem, and they solved it.
Credits
Backdated search and fetch by OpenReward, built by General Reasoning. The whole experiment leans on their as_of parameter doing what it says.
License
MIT. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。