Foresight

Developer reference · API + MCP

Build forecasting into your agent.

Five MCP tools, nine HTTP endpoints, zero auth in Phase 0. Every endpoint mirrors the same data the web UI renders — no second source of truth to keep in sync. Copy any command below and run it locally.

01 · Quickstart

Pick your surface.

AI agents speak MCP — one install gives them tool-calling access to the marketplace. Server-side code or bash scripts read the HTTP endpoints directly. Same data either way.

Use from any MCP-aware agent

Claude Desktop · Claude Code · custom SDK clients

# Add to your agent's MCP config (e.g., claude_desktop_config.json)
{
  "mcpServers": {
    "foresight": {
      "command": "npx",
      "args": ["-y", "foresight-mcp@latest"]
    }
  }
}

Use from any HTTP client

curl · fetch · requests · server-to-server

curl https://foresight.cuvetsmo.com/api/markets \
  -H "Accept: application/json" | jq '.markets[0]'

02 · MCP server

Five tools, one server.

Standalone Node package. Reads from the public /api/markets endpoint with a 5-minute in-process cache, falls back to a bundled seed if the network is down. Override the base URL with FORESIGHT_API_BASE.

View on GitHub

Install

# Add to your MCP-aware agent's config
{
  "mcpServers": {
    "foresight": {
      "command": "npx",
      "args": ["-y", "foresight-mcp@latest"]
    }
  }
}

# Then in your conversation:
> List the top 5 Thai politics markets closing this quarter.
> Dry-run the resolver on 'anthropic-1m-ctx-2027-q1' as of next March.

foresight_list_markets

Browse the live forecasting marketplace.

tool

Filter by category and status, sort by volume, open interest, closing date, or newest. Read-only, free. The agent uses this to discover what is listable before any other tool.

Input

{
  "category"?: "thai-politics" | "thai-climate" | "thai-vet" | "sea-elections" |
              "crypto" | "global-tech" | "global-sports" | "ai-research",
  "status"?: "open" | "closing-soon" | "resolved",
  "sortBy"?: "volume" | "open-interest" | "closing-soonest" | "newest",  // default volume
  "limit"?: number  // 1-50, default 10
}

Output

{
  "count": 10,
  "totalAvailable": 17,
  "filters": { "category": "...", "status": "...", "sortBy": "..." },
  "source": "live" | "bundled-seed",
  "markets": [/* market summary objects with id, slug, question, probabilities, url */]
}

foresight_get_market

Full detail for one market.

tool

Pass an id or slug — either works. Returns probabilities, volume, open interest, deadline countdown, resolution criterion, named primary sources, tags, and the canonical web URL.

Input

{
  "identifier": "string"  // id like 'th-elec-2027' or slug
}

Output

{
  "id": "string",
  "slug": "string",
  "question": "string",
  "category": "...",
  "status": "open | closing-soon | resolved",
  "probabilities": { "yes": 0.42, "no": 0.58 },
  "volume": { "usd": 0, "openInterestUsd": 0 },
  "timing": { "closesAt": "ISO", "daysLeft": 240 },
  "resolution": { "criteria": "string", "sources": ["url"] },
  "tags": ["..."],
  "url": "https://foresight.cuvetsmo.com/markets/<slug>"
}

foresight_propose_market

Submit a new market proposal to the review queue.

tool

Any MCP-aware agent can propose. Proposals land in the public review queue at /admin/proposals — they are not auto-listed. Iron-Rule-0 schema: requires a machine-verifiable resolution criterion plus at least one named primary source URL.

Input

{
  "question": "string  // 10-280 chars, yes/no phrasing",
  "questionEn"?: "string  // optional English translation",
  "category": "thai-politics" | "thai-climate" | "thai-vet" |
              "sea-elections" | "crypto" | "global-tech" |
              "global-sports" | "ai-research",
  "closesAt": "ISO datetime  // must be in the future",
  "resolutionCriteria": "string  // 40-1000 chars, machine-verifiable",
  "resolutionSources": ["url"],  // 1-5 named primary sources
  "tags"?: ["string"]  // 0-8 discovery tags, ≤40 chars each
}

Output

{
  "status": "pending_review",
  "draftId": "draft-<timestamp>-<rand>",
  "proposal": { /* the validated input echoed back */ },
  "preview": {
    "url": "https://foresight.cuvetsmo.com/propose?draft=<id>",
    "reviewSlaHours": 48
  },
  "message": "string  // human-readable acceptance"
}

foresight_resolve_check

Dry-run the multi-source verifier on a market.

tool

Identical resolver as POST /api/resolve, but scoped to a known market. Read-only — never mutates state. Useful for agents that want to preview a resolution before proposing an appeal.

Input

{
  "identifier": "string",  // market id or slug
  "asOf"?: "ISO datetime"  // simulate 'resolve as of' — defaults to now
}

Output

{
  "market": { "id": "string", "slug": "string", "question": "string" },
  "asOf": "ISO",
  "status": "verifiable | pending | ambiguous | refused",
  "proposedOutcome"?: "yes | no",
  "confidence"?: 0.92,
  "reasoning": "string",
  "citedSources": [{ "source": "url", "checked": true, "note": "string" }],
  "appealAvailable": true
}

foresight_stream_events

Subscribe to market lifecycle events.

tool

Returns the recent event tail for one market or for all markets matching a filter. Phase 0 returns a synthetic tail; Phase 1 wires to Supabase realtime. Use for live dashboards, calibration trackers, or alert bots.

Input

{
  "market"?: "string",  // id or slug — omit for global stream
  "since"?: "ISO datetime"  // cursor — pass back the previous response's cursor
}

Output

{
  "events": [
    {
      "cursor": "ISO",  // use as 'since' on next call
      "type": "price-tick | trade | market-open | market-close | resolution",
      "marketId": "string",
      "marketSlug": "string",
      "marketUrl": "https://foresight.cuvetsmo.com/markets/<slug>",
      "data": { /* event-specific payload */ },
      "ts": "ISO"
    }
  ],
  "count": 4,
  "cursor": "ISO",  // next-page cursor (last event's ts)
  "pollIntervalSec": 30,
  "note": "string"
}

03 · HTTP endpoints

Nine routes, no API key.

All endpoints are public in Phase 0. No signup, no rate limit gating — fair-use only. CORS open on read endpoints; write endpoints accept JSON only.

GET

/api/markets

List every market — single source of truth.

Returns the full market catalogue in the same shape rendered by the web UI. The MCP server, downstream agents, and external tools all read from here. Headers: max-age=30, s-maxage=60, stale-while-revalidate=300. CORS open.

Example

curl https://foresight.cuvetsmo.com/api/markets | jq '.markets[0]'

Response shape

{
  "version": 1,
  "count": 17,
  "generatedAt": "2026-05-27T12:00:00.000Z",
  "markets": [
    {
      "id": "string",
      "slug": "string",
      "question": "string",
      "questionEn": "string?",
      "category": "thai-politics | thai-climate | thai-vet | sea-elections | crypto | global-tech | global-sports | ai-research",
      "status": "open | closing-soon | resolved",
      "yesProbability": 0.42,
      "volumeUsd": 0,
      "openInterestUsd": 0,
      "closesAt": "2027-01-01T00:00:00.000Z",
      "resolutionCriteria": "string",
      "resolutionSources": ["string"],
      "tags": ["string"],
      "isSample": true
    }
  ]
}
  • Phase 0 markets all carry isSample=true — they are curated demonstrations of the resolution-criteria pattern, not live order-book contracts.
  • Volume + openInterest are zero by design until real trading turns on. Do not interpret them as activity signals.
POST

/api/resolve

Dry-run the multi-source verifier.

Two input modes. Pass an existing market identifier to resolve a known market, or pass an ad-hoc question + criterion + sources for any forecast. Always returns the same ResolveResult shape. Status of pending, ambiguous, or refused is a feature — the verifier will say it doesn't know rather than fabricate.

Example

curl -X POST https://foresight.cuvetsmo.com/api/resolve \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Will the Bank of Thailand cut the policy rate at the December 2026 MPC meeting?",
    "resolutionCriteria": "YES if the BoT MPC December 2026 statement records a policy rate decrease vs the previous meeting. NO otherwise.",
    "resolutionSources": ["https://www.bot.or.th/en/our-roles/monetary-policy/mpc-meeting.html"],
    "closesAt": "2026-12-31T23:59:59Z"
  }'

Response shape

{
  "market": { "identifier": "string" } | null,
  "asOf": "ISO",
  "status": "verifiable | pending | ambiguous | refused",
  "proposedOutcome"?: "yes | no | void",
  "confidence"?: 0.92,
  "reasoning": "string",
  "citedSources": [{ "source": "url", "checked": true, "note": "string" }],
  "appealAvailable": true,
  "providerUsed"?: "groq | cerebras | sambanova | openrouter | mistral",
  "refusalReason"?: "string"
}
  • Confidence below 0.85 auto-downgrades status to ambiguous. The verifier never commits a low-confidence outcome.
  • 5-provider fallback chain (Groq → Cerebras → SambaNova → OpenRouter → Mistral). When none are configured the route returns status=pending with a transparent note; providerUsed is omitted in that case.
  • 12s abort timeout across the chain. proposedOutcome can also be 'void' when the resolver determines the market is unresolvable as-stated.
GET

/api/resolve/status

Probe which verifier providers are wired.

Returns which LLM providers are configured WITHOUT leaking key values, prefixes, or any partial form. Used by the /resolver page footer to show live vs Phase 0 fallback honestly.

Example

curl https://foresight.cuvetsmo.com/api/resolve/status

Response shape

{
  "mode": "live | fallback",
  "providersConfigured": ["groq"],
  "providersAvailable": ["groq", "cerebras", "sambanova", "openrouter", "mistral"],
  "note": "string"
}
GET

/api/cross-venue

Find matching markets on Polymarket and Kalshi.

By-slug mode resolves a Foresight market then pulls term-derived matches from Polymarket Gamma + Kalshi public APIs. Free-text mode lets you pass an arbitrary query + AND-group keyword sets. Returns exclusiveToForesight: true when neither venue carries the topic — that is a feature, not a bug.

Example

# by Foresight slug
curl 'https://foresight.cuvetsmo.com/api/cross-venue?slug=anthropic-1m-ctx-2027-q1'

# free-text + AND-groups
curl 'https://foresight.cuvetsmo.com/api/cross-venue?q=BoT%20rate%20cut&terms=bank+thailand&terms=rate+cut'

Response shape

{
  "query": "string",
  "polymarket": [{ "title": "string", "url": "string", "yesProbability": 0.18, "volumeUsd": 0 }],
  "kalshi": [{ "title": "string", "url": "string", "yesProbability": 0.22 }],
  "exclusiveToForesight": false,
  "fetchedMs": 412,
  "note": "string?"
}
  • Cached at the route level for 1h. Polymarket pagination walks offset 0/100/200; Kalshi handles their 404-with-body quirk.
  • exclusiveToForesight=true is positive social proof — it means the topic is on our venue and no global aggregator lists it.
POST

/api/waitlist

Capture trading-intent for a market.

Records a measurable demand signal — email, optional market, side, and intended size. Phase 0 sink is stdout plus an audit-log row when Supabase service-role is wired. Phase 1 swaps to a dedicated waitlist table.

Example

curl -X POST https://foresight.cuvetsmo.com/api/waitlist \
  -H "Content-Type: application/json" \
  -d '{
    "email": "trader@example.com",
    "marketId": "anthropic-1m-ctx-2027-q1",
    "side": "yes",
    "sizeUsd": 50,
    "source": "docs"
  }'

Response shape

{ "status": "ok", "saved": true }
  • Email is the only required field. Side, marketId, sizeUsd, and source are optional context.
  • No confirmation email yet — Phase 1 adds a double-opt-in flow.
GET

/api/health

Build identity + deployment URL.

Linked from the footer Status link. Phase 1 will extend to report Supabase reachability, MCP server status, and the deploy commit SHA.

Example

curl https://foresight.cuvetsmo.com/api/health

Response shape

{
  "name": "Foresight",
  "status": "ok",
  "phase": "0",
  "baseUrl": "https://foresight.cuvetsmo.com",
  "educationalBeta": true,
  "timestamp": "ISO"
}
GET

/api/proposals

Public read of the market proposal queue.

Mirrors the UI at /admin/proposals — single source of truth, no second list. Phase 0 queue is intentionally empty; submit via the foresight_propose_market MCP tool. Approve/reject actions are NOT exposed here (auth-gated, Phase 2).

Example

curl https://foresight.cuvetsmo.com/api/proposals | jq '.byStatus'

Response shape

{
  "version": 1,
  "count": 0,
  "byStatus": { "pending": 0, "revisionsRequested": 0, "approved": 0, "rejected": 0 },
  "generatedAt": "ISO",
  "queueUrl": "/admin/proposals",
  "submitVia": { "mcpTool": "foresight_propose_market", "mcpPackage": "foresight-mcp" },
  "reviewSlaHours": 48,
  "proposals": [/* MarketProposal[] */]
}
  • Empty array is the honest empty-state — no fabricated proposals to look busy.
  • Read-only. Submit via the MCP tool, not via POST here. The review panel for approvals lands in Phase 2.
GET

/api/openapi.json

OpenAPI 3.1 machine-readable spec.

The whole API in one JSON document. Drop into Postman / Insomnia / Bruno. Generate TypeScript clients via openapi-typescript. Wire into Stainless or Speakeasy for SDK pipelines. 1-hour edge cache.

Example

curl https://foresight.cuvetsmo.com/api/openapi.json | jq '.info'

# Generate TS client
npx openapi-typescript https://foresight.cuvetsmo.com/api/openapi.json -o foresight.d.ts

Response shape

{
  "openapi": "3.1.0",
  "info": { "title": "Foresight Public API", "version": "0.1.0", ... },
  "paths": { /* 8 endpoints */ },
  "components": { "schemas": { /* 12 reusable shapes */ } }
}
  • The OpenAPI version field bumps on every breaking change — track it to catch contract drift in your client.
  • Self-referential — the spec describes the 8 functional endpoints but omits itself (a meta-endpoint about the API).
GET

/api/stats

One-shot aggregate stats — for dashboards.

Market counts (by category + status + sample-vs-real), verifier mode, MCP version. Public-only — never includes per-user state. Powers external status pages without scraping the UI.

Example

curl https://foresight.cuvetsmo.com/api/stats | jq '{ total: .markets.total, verifier: .verifier.mode }'

Response shape

{
  "name": "Foresight",
  "phase": "0",
  "generatedAt": "ISO",
  "markets": {
    "total": 17,
    "sample": 17,
    "real": 0,
    "byStatus": { "open": 12, "closingSoon": 3, "resolved": 2 },
    "byCategory": [{ "key": "thai-politics", "label": "Thai Politics", "count": 3 }]
  },
  "verifier": { "mode": "live | fallback", "providersConfigured": [...] },
  "mcp": { "package": "foresight-mcp", "sourceVersion": "0.2.0", "npmPublished": false, "tools": [...] },
  "docs": { "openapi": "/api/openapi.json", "developerPage": "/docs", "changelog": "/changelog" }
}
  • Caches at the edge — same headers as /api/markets. Safe to poll once a minute from a dashboard.
  • npmPublished flips to true once the foresight-mcp package lands on the npm registry.

04 · Rate limits + status

Fair-use, transparent state.

Phase 0 limits

No quota enforcement. Edge cache absorbs reads. Don't hammer the resolver in a tight loop — it costs real LLM tokens once providers are wired.

Auth

None for reads. Writes (waitlist, future proposals) accept plain JSON. Phase 1 adds API keys + per-key budgets for the resolver.

Found a bug? Want a new endpoint?

Public review queue at /admin/proposals — submit a market proposal through foresight_propose_market and it lands there. For DX feedback, open an issue against the MCP server repo.