> ## Documentation Index
> Fetch the complete documentation index at: https://docs.routeur.app/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Prices are US dollars per contract that pays $1: 0.46 means 46 cents. Spread is the Yes ask plus the No ask minus $1. Liquidity grades are deep, tradeable and thin; lists hide thin markets unless include_thin=true. Relations are proven from contract terms, never inferred from wording; a lead marked informational is never executable. Every figure carries a graph_run; read /agents/reading-order first, then /concepts/relations and /concepts/liquidity.

# An agent session

> Connect to the MCP server, list the tools, and ask three questions, with the actual tool inputs and outputs.

## What you'll get

A walkthrough of what an agent sees on the Routeur MCP server: the `initialize` handshake, the tool list, and three tool calls with their real inputs and outputs from 2026-09-17: what is tradeable right now, why two markets are linked and whether prices respect it, and the same question priced on both venues. The last step is the whole session as one Python script with no MCP SDK, so you can see every byte.

## Prerequisites

* An API key in `ROUTEUR_API_KEY`
* `curl`, or Python 3 with `requests`
* Every request carries `Accept: application/json, text/event-stream`. An `Accept` that names only one of the two is refused with `400`

<Steps>
  <Step title="Connect">
    The server is stateless Streamable HTTP: one `POST` per message, JSON back, no session id to keep. `initialize` returns the server's instructions to the model.

    ```bash theme={null}
    curl -s https://api.routeur.app/mcp \
      -H "Authorization: Bearer $ROUTEUR_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{
            "protocolVersion":"2025-06-18","capabilities":{},
            "clientInfo":{"name":"cookbook","version":"0.1"}}}'
    ```

    ```json theme={null}
    {
      "jsonrpc": "2.0", "id": 0,
      "result": {
        "protocolVersion": "2025-06-18",
        "capabilities": { "logging": {}, "prompts": { "listChanged": true }, "resources": { "listChanged": true }, "tools": { "listChanged": true } },
        "serverInfo": { "name": "routeur", "title": "Routeur prediction markets", "version": "2026-09-15", "websiteUrl": "https://routeur.app",
                        "description": "Proven relations between Kalshi and Polymarket contracts, with the prices, history and signals built on them." },
        "instructions": "Routeur proves which prediction-market contracts on Kalshi and Polymarket imply, exclude or equal each other from the venues' structured terms and exact arithmetic, and serves live prices, recorded quotes and trades, mispricing leads, unusual flow and strategies measured against settled history.\n\nStart with the composed tools: find_opportunities for anything tradeable right now, compare_venues to price one question on both venues, explain_relation to see why two markets are linked, market_snapshot for one market in full, search_markets to find markets in plain language. The generated tools named after the REST operations give you everything else, one endpoint each.\n\nRead the resources under routeur://concepts before interpreting numbers: prices are asks per contract that pays $1, as decimal strings; a relation is proven, never guessed; a gap between venues is only money when the books have size and fees leave something. Everything is read-only and nothing here places orders."
      }
    }
    ```

    Send `MCP-Protocol-Version: 2025-06-18` on every request after this.
  </Step>

  <Step title="List the tools">
    ```bash theme={null}
    curl -s https://api.routeur.app/mcp \
      -H "Authorization: Bearer $ROUTEUR_API_KEY" -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" -H "MCP-Protocol-Version: 2025-06-18" \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
    ```

    38 tools on 2026-09-17: five composed (`compare_venues`, `explain_relation`, `find_opportunities`, `market_snapshot`, `search_markets`) and 33 generated from the REST operations (`get_market`, `list_relations`, `get_relation_proof`, `get_market_book`, `get_market_rules`, `list_flow_signals`, and so on). Each carries its input schema and annotations; `find_opportunities`, trimmed:

    ```json theme={null}
    {
      "name": "find_opportunities",
      "title": "Find something tradeable right now",
      "annotations": { "readOnlyHint": true, "idempotentHint": true, "destructiveHint": false, "openWorldHint": false },
      "inputSchema": {
        "type": "object", "additionalProperties": false,
        "properties": {
          "include":          { "type": "array", "items": { "type": "string", "enum": ["leads", "gaps", "strategies"] }, "description": "Which kinds to look for. All three by default." },
          "venue":            { "type": "string", "enum": ["kalshi", "polymarket"] },
          "category":         { "type": "string", "enum": ["Sports", "Politics", "Economics", "Crypto", "Culture", "Tech & Science", "Weather", "World", "Other"] },
          "cross_venue_only": { "type": "boolean", "default": false },
          "executable_only":  { "type": "boolean", "default": true, "description": "Only leads whose live order books could be filled at a profit. True by default." },
          "min_volume_usd":   { "type": "integer", "minimum": 0, "maximum": 10000000, "default": 1000 },
          "limit":            { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 },
          "cursor":           { "type": "string" },
          "detail":           { "type": "string", "enum": ["summary", "full"], "default": "summary" }
        }
      }
    }
    ```

    `resources/list` returns seven resources (`routeur://concepts/glossary`, `routeur://concepts/relations`, `routeur://concepts/leads-and-fees`, `routeur://concepts/prices`, `routeur://openapi.json`, `routeur://categories`, `routeur://topics`) and `prompts/list` three prompts (`scan_for_mispricings`, `brief_me_on_event`, `backtest_price_band`).
  </Step>

  <Step title="Question one: what can I trade right now?">
    ```json Input theme={null}
    { "jsonrpc": "2.0", "id": 2, "method": "tools/call",
      "params": { "name": "find_opportunities", "arguments": { "limit": 5 } } }
    ```

    The result carries the answer twice: as text in `content[0].text`, and as JSON in `structuredContent`. Observed 2026-09-17 12:41 UTC:

    ```json Output (structuredContent) theme={null}
    {
      "filters": { "category": "", "cross_venue_only": false, "executable_only": true, "include": ["gaps", "leads", "strategies"], "min_volume_usd": 1000, "venue": "" },
      "graph_run": 74,
      "items": [
        { "kind": "strategy_match",
          "headline": "Will \"BbY WOW - KAROL G, Judeline, rusowsky\" be the #2 US song this w… at $0.15 fits all~culture~1d~b8~no",
          "markets": [ { "venue": "polymarket", "id": "will-bby-wow-karol-g-judeline-rusowsky-be-the-2-us-song-this-week-20260918", "title": "Will \"BbY WOW - KAROL G, Judeline, rusowsky\" be the #2 US song this week?" } ],
          "strategy_id": "all~culture~1d~b8~no",
          "rule": "buy no between $0.80 and $0.90 about 1 days out (all, Culture): $100 came back as $143.03 across 119 settled markets, win rate 0.3697",
          "price_usd": "0.15", "closes_at": "2026-09-18T23:59:00Z", "volume_24h_usd": "4804.56" }
      ],
      "next_cursor": "", "notes": null, "total": 1
    }
    ```

    One row: no lead was fillable and no gap paid, so what is left is a strategy match. An agent should say exactly that, and name the rule as history rather than as a forecast.
  </Step>

  <Step title="Question two: why are these two markets linked?">
    ```json Input theme={null}
    { "jsonrpc": "2.0", "id": 3, "method": "tools/call",
      "params": { "name": "explain_relation", "arguments": {
        "a_venue": "kalshi", "a_id": "KXMLBTOTAL-26SEP171235MILPIT-9",
        "b_venue": "polymarket", "b_id": "mlb-mil-pit-2026-09-17-total-8pt5" } } }
    ```

    ```json Output (structuredContent, trimmed) theme={null}
    {
      "related": true, "relation": "equivalent", "cross_venue": true,
      "a": { "venue": "kalshi", "id": "KXMLBTOTAL-26SEP171235MILPIT-9", "title": "Over 8.5 runs scored", "category": "Sports",
             "quote": { "yes_ask_usd": "0.55", "no_ask_usd": "0.46", "observed_at": "2026-09-17T12:33:13.911081Z" },
             "strike": { "type": "greater", "floor": "17/2" }, "rules_hash": "73562783085732ec…",
             "relations": { "implies": 5, "implied_by": 7, "equivalent": 1, "contradicts": 0, "overlaps": 0, "cross_venue": 3 } },
      "b": { "venue": "polymarket", "id": "mlb-mil-pit-2026-09-17-total-8pt5", "title": "Milwaukee Brewers vs. Pittsburgh Pirates: O/U 8.5",
             "quote": { "yes_ask_usd": "0.56", "no_ask_usd": "0.45", "observed_at": "2026-09-17T12:33:40.639111Z" } },
      "evidence": ["cross-venue: same mlb game scheduled 2026-09-17 ET, matched by team codes and team names; both settle on the game's final score; neither venue restricts periods; postponement and cancellation settle differently"],
      "meaning": "\"Over 8.5 runs scored\" and \"Milwaukee Brewers vs. Pittsburgh Pirates: O/U 8.5\" resolve the same way in every outcome, so they should cost the same.",
      "price_check": {
        "rule": "Equivalent markets should cost the same.",
        "a_yes_ask_usd": "0.55", "b_yes_ask_usd": "0.56", "gap_cents": "1", "consistent": false,
        "note": "The recorded asks break this relation. Check find_opportunities for whether the books can actually be filled: top-of-book asks are often too thin, and fees can take the difference."
      }
    }
    ```

    `consistent: false` on a 1¢ gap is arithmetic, not an opportunity; the note says where to check, and question one already answered it.
  </Step>

  <Step title="Question three: price it on both venues">
    ```json Input theme={null}
    { "jsonrpc": "2.0", "id": 4, "method": "tools/call",
      "params": { "name": "compare_venues", "arguments": { "question": "Brewers Pirates", "limit": 1 } } }
    ```

    ```json Output (structuredContent, trimmed) theme={null}
    {
      "total": 10, "graph_run": 74, "notes": null, "next_cursor": "MQ",
      "events": [
        { "venue": "polymarket", "id": "1004384", "title": "Milwaukee vs Pittsburgh: Total Runs", "category": "Sports",
          "venues": ["kalshi", "polymarket"], "twin": { "venue": "kalshi", "id": "KXMLBTOTAL-26SEP171235MILPIT" },
          "closes_at": "2026-09-17T16:35:00Z", "volume_24h_usd": "31344.06", "widest_gap_cents": "1",
          "outcomes": [
            { "label": "O/U 8.5", "chance": "0.5500", "best_venue": "kalshi", "gap_cents": "1", "volume_24h_usd": "18616.80",
              "prices": [ { "venue": "kalshi", "market_id": "KXMLBTOTAL-26SEP171235MILPIT-9", "yes_ask_usd": "0.55", "no_ask_usd": "0.46" },
                          { "venue": "polymarket", "market_id": "mlb-mil-pit-2026-09-17-total-8pt5", "yes_ask_usd": "0.56", "no_ask_usd": "0.45" } ] },
            { "label": "Milwaukee Brewers vs. Pittsburgh Pirates", "chance": "0.5685", "volume_24h_usd": "8384.44",
              "prices": [ { "venue": "polymarket", "market_id": "mlb-mil-pit-2026-09-17", "yes_ask_usd": "0.569", "no_ask_usd": "0.432" } ] },
            { "label": "O/U 9.5", "chance": "0.4350", "best_venue": "polymarket", "volume_24h_usd": "2446.33",
              "prices": [ { "venue": "kalshi", "market_id": "KXMLBTOTAL-26SEP171235MILPIT-10", "yes_ask_usd": "0.44", "no_ask_usd": "0.57" },
                          { "venue": "polymarket", "market_id": "mlb-mil-pit-2026-09-17-total-9pt5", "yes_ask_usd": "0.44", "no_ask_usd": "0.57" } ] }
          ] }
      ]
    }
    ```

    Outcomes are matched across venues by proven equivalence, not by wording: "O/U 8.5" on Polymarket lines up with "Over 8.5 runs scored" on Kalshi because the graph proved them equivalent. The moneyline outcome has one price because only Polymarket lists it in this event.
  </Step>

  <Step title="The whole session as one script">
    No SDK, so every message is visible. The tool ids come from question two.

    ```python Python theme={null}
    import json
    import os
    import requests

    MCP = "https://api.routeur.app/mcp"
    session = requests.Session()
    session.headers.update({
        "Authorization": f"Bearer {os.environ['ROUTEUR_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    })
    _next = iter(range(1, 1000))


    def rpc(method, params=None):
        body = {"jsonrpc": "2.0", "id": next(_next), "method": method}
        if params is not None:
            body["params"] = params
        res = session.post(MCP, json=body, timeout=60)
        res.raise_for_status()
        msg = res.json()
        if "error" in msg:
            raise RuntimeError(msg["error"])
        return msg["result"]


    def call(name, **arguments):
        result = rpc("tools/call", {"name": name, "arguments": arguments})
        if result.get("isError"):
            raise RuntimeError(result["content"][0]["text"])
        return result.get("structuredContent") or json.loads(result["content"][0]["text"])


    # 1. Connect.
    init = rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "cookbook", "version": "0.1"}})
    session.headers["MCP-Protocol-Version"] = init["protocolVersion"]
    print(f"connected to {init['serverInfo']['name']} {init['serverInfo']['version']} (protocol {init['protocolVersion']})")

    # 2. List the tools.
    tools = rpc("tools/list")["tools"]
    composed = [t["name"] for t in tools if t["name"] in {"find_opportunities", "compare_venues", "explain_relation", "market_snapshot", "search_markets"}]
    print(f"{len(tools)} tools; composed: {', '.join(sorted(composed))}")

    # 3. Question one: what can I trade right now?
    found = call("find_opportunities", limit=5)
    print(f"\nQ1 find_opportunities: {found['total']} row(s) from graph run {found['graph_run']}")
    for row in found["items"]:
        print(f"  [{row['kind']}] {row['headline']}")
    if found.get("notes"):
        print(f"  notes: {found['notes']}")

    # 4. Question two: why are these two markets linked, and do prices respect it?
    pair = call("explain_relation", a_venue="kalshi", a_id=os.environ["A_ID"], b_venue="polymarket", b_id=os.environ["B_ID"])
    print(f"\nQ2 explain_relation: related={pair['related']}")
    if pair["related"]:
        print(f"  {pair['relation']} · {pair['meaning']}")
        check = pair["price_check"]
        print(f"  {check['rule']} Kalshi {check['a_yes_ask_usd']} vs Polymarket {check['b_yes_ask_usd']} → consistent={check['consistent']}")
    else:
        print(f"  {pair['explanation']}")

    # 5. Question three: the same question on both venues.
    compared = call("compare_venues", question=os.environ.get("QUESTION", "Brewers Pirates"), limit=1)
    print(f"\nQ3 compare_venues: {compared['total']} event(s)")
    for event in compared["events"]:
        print(f"  {event['title']} · on {', '.join(event['venues'])} · widest gap {event.get('widest_gap_cents', '0')}¢")
        for o in event["outcomes"]:
            prices = ", ".join(f"{p['venue']} {p['yes_ask_usd']}" for p in o["prices"])
            print(f"    {o['label']}: {prices}" + (f" · cheaper on {o['best_venue']}" if o.get("best_venue") else ""))
    ```

    Output observed 2026-09-17 12:48 UTC:

    ```text theme={null}
    $ A_ID=KXMLBTOTAL-26SEP171235MILPIT-9 B_ID=mlb-mil-pit-2026-09-17-total-8pt5 python3 session.py
    connected to routeur 2026-09-15 (protocol 2025-06-18)
    38 tools; composed: compare_venues, explain_relation, find_opportunities, market_snapshot, search_markets

    Q1 find_opportunities: 1 row(s) from graph run 74
      [strategy_match] Will "BbY WOW - KAROL G, Judeline, rusowsky" be the #2 US song this w… at $0.15 fits all~culture~1d~b8~no

    Q2 explain_relation: related=True
      equivalent · "Over 8.5 runs scored" and "Milwaukee Brewers vs. Pittsburgh Pirates: O/U 8.5" resolve the same way in every outcome, so they should cost the same.
      Equivalent markets should cost the same. Kalshi 0.55 vs Polymarket 0.56 → consistent=False

    Q3 compare_venues: 10 event(s)
      Milwaukee vs Pittsburgh: Total Runs · on kalshi, polymarket · widest gap 1¢
        O/U 8.5: kalshi 0.55, polymarket 0.56 · cheaper on kalshi
        Milwaukee Brewers vs. Pittsburgh Pirates: polymarket 0.569
        O/U 9.5: kalshi 0.44, polymarket 0.44 · cheaper on polymarket
    ```
  </Step>
</Steps>

## Read the result

| Field                                                           | Meaning                                                                                                                                                            |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `result.content[0].text`                                        | The tool's answer as JSON text, for clients that only read text.                                                                                                   |
| `result.structuredContent`                                      | The same object as JSON, on the composed tools. Prefer it.                                                                                                         |
| `result.isError`                                                | The call failed; `content[0].text` says why, including `rate_limited` and `daily_quota_exceeded` with the seconds to wait. The HTTP status is still 200.           |
| `graph_run`                                                     | The hourly graph run the answer was read from. Two calls with different runs can disagree.                                                                         |
| `filters` (find\_opportunities)                                 | What the tool applied, including the defaults you did not set. `executable_only: true` is the one that empties the list most often.                                |
| `price_check` (explain\_relation)                               | The bound the relation puts on the two asks, and whether the recorded asks respect it. `consistent: false` is a mispricing at the top of book, not a fillable one. |
| `gap_cents`, `best_venue`, `widest_gap_cents` (compare\_venues) | Per outcome, how far apart the venues' Yes asks are and which is cheaper; per event, the widest of them. Absent when one venue lists the outcome.                  |
| `next_cursor`                                                   | Opaque; pass it back as `cursor` for the next page.                                                                                                                |

## Pitfalls

* **`Accept` must name both types or neither.** `Accept: application/json` alone is refused with `400 Accept must contain both 'application/json' and 'text/event-stream'`; HTTP clients that add a default `Accept` hit this. Set the full value once on the session.
* **Ask by question and you get search's fallback.** `compare_venues` and `search_markets` never come back empty: on 2026-09-17, `search_markets {query: "fed decision", cross_venue: true}` returned NFL and college-football totals labelled `match: "trending"`, because no listed event matched. Read `match` (or `notes`) before quoting a result as the answer to the question.
* **`find_opportunities` is empty more often than not.** `executable_only` defaults to true and fillable leads are rare. Say "nothing fillable right now" rather than loosening the filter silently; if you do set `executable_only: false`, the rows say "0 pairs fillable".
* **Tool calls cost API requests.** `find_opportunities` up to four, `market_snapshot` five, `explain_relation` two or three. The per-minute limit and daily quota are the key's; `initialize` and `tools/list` are free.
* **Prices are strings.** `"0.55"` is 55¢ per contract that pays \$1. Do not round them to booleans or percentages in the model's reply without saying so.

## Related

* [Agents & MCP](/agents/overview): what the server exposes and how it is metered.
* [Tool reference](/agents/tools): every tool, its arguments and what it costs.
* [Agent workflows](/agents/workflows): the sequences behind the three built-in prompts.
* [Liquid opportunities](/cookbook/liquid-opportunities): the REST scan `find_opportunities` wraps.
* [Proof trail](/cookbook/proof-trail): the full proof `explain_relation` summarises, from `GET /v1/relations/…`.
