{"slug": "give-your-ai-agent-real-poker-solver-data-with-mcp", "title": "Give your AI agent real poker solver data with MCP", "summary": "A developer on the Pokerai API team released @pokerai/mcp, an MCP server that connects AI chat clients to presolved poker strategy data for 6-max No-Limit Hold'em, so models explain solver output rather than inventing advice. The server runs over stdio via npx and exposes five tools — preflop_versions, preflop_strategy, preflop_range, flop_tree and flop_node — returning action frequencies and decision trees for spots like ace-king on a queen-high flop. The developer notes the tool is intended for study and post-hand review, since real-time assistance at live tables is prohibited.", "body_md": "Ask a chat model what to do with ace-king on a queen-high flop and you get a confident paragraph. It might even be right, but you can't tell where the numbers came from, because there aren't any.\n\nIn this tutorial we connect an MCP client to a presolved strategy API, so the frequencies come from solver output and the model's job is to explain them. We'll use `@pokerai/mcp`, a small MCP server for 6-max No-Limit Hold'em.\n\n**Disclosure:** I work on Pokerai API, the product used here. This post was drafted with AI assistance, and I checked it against real tool output.\n\n**What this is for:** studying, training tools and reviewing hands that have ended. Real-time assistance (RTA) at real-money tables is prohibited, so keep it away from live games.\n\nThe server runs over stdio through `npx`, so there's nothing to install globally. In Claude Code:\n\n```\nclaude mcp add pokerai -e POKERAI_API_KEY=gto_your_key_here -- npx -y @pokerai/mcp\n```\n\nIn Claude Desktop or Cursor, use the same command in your MCP config:\n\n```\n{\n  \"mcpServers\": {\n    \"pokerai\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@pokerai/mcp\"],\n      \"env\": { \"POKERAI_API_KEY\": \"gto_your_key_here\" }\n    }\n  }\n}\n```\n\nYou get five tools:\n\n| Tool | What it returns | Cost | \n|---|---|---|\n| `preflop_versions` | available strategy sets | free | \n| `preflop_strategy` | action frequencies for one hand | 1 lookup | \n| `preflop_range` | the whole 169-hand range for a spot | 1 lookup | \n| `flop_tree` | starting ranges and every decision node | 1 lookup | \n| `flop_node` | the strategy at one node | free | \n\nThe outputs below are from calling the published package with an MCP client, not from a model transcript. What your model writes around them will vary. The tool results won't change unless the data does.\n\nThe spot: 100bb, 6-max. The button opens, the big blind calls, and the flop is Q♠ 7♣ 2♦. We hold A♥ K♦.\n\n```\n{\n  \"tool\": \"preflop_strategy\",\n  \"arguments\": {\n    \"hole_cards\": \"AhKd\",\n    \"hero\": \"BTN\",\n    \"preflop_actions\": [\n      { \"position\": \"SB\", \"action\": \"small blind\", \"amount\": 0.5 },\n      { \"position\": \"BB\", \"action\": \"big blind\", \"amount\": 1 }\n    ],\n    \"preflop_version\": \"6max_RC_100bb_200NL\"\n  }\n}\n{\n  \"hole_cards\": \"AhKd\",\n  \"situation\": \"RFI\",\n  \"strategy\": [\n    { \"action\": \"raise\", \"frequency\": 1, \"amount_bb\": 3, \"sizing_pot\": 0.8 }\n  ]\n}\n```\n\nAlways raise, to 3bb. Not much to discuss, so on to the flop.\n\n```\n{\n  \"tool\": \"flop_tree\",\n  \"arguments\": {\n    \"board\": \"Qs7c2d\",\n    \"pot_type\": \"SRP\",\n    \"positions\": { \"hero\": \"BTN\", \"raiser\": \"BTN\", \"caller\": \"BB\" },\n    \"flop_version\": \"6max_RC_100bb_200NL\"\n  }\n}\n```\n\nThe response gives the pot (6.5bb), the effective stack (97bb), both starting ranges and 16 nodes. Trimmed:\n\n```\n{\n  \"pot\": 6.5,\n  \"effective_stack\": 97,\n  \"node_count\": 16,\n  \"nodes\": [\n    { \"node\": \"root\", \"is_hero\": false, \"token\": \"eyJ...\" },\n    { \"node\": \"root/CHECK\", \"is_hero\": true, \"token\": \"eyJ...\" },\n    { \"node\": \"root/CHECK/BET_2\", \"is_hero\": false, \"token\": \"eyJ...\" }\n  ]\n}\n```\n\nNote the two fields, because this is the easiest mistake to make:\n\n`node` is the path, such as `root/CHECK`. BB acts first, so `root` is BB's decision and `token` is what you pass to `flop_node`, in its argument called \n\n```\n{\n  \"tool\": \"flop_node\",\n  \"arguments\": { \"node\": \"<token of root/CHECK>\", \"hole_cards\": \"AhKd\" }\n}\n{\n  \"hole_cards\": \"AhKd\",\n  \"node\": \"root/CHECK\",\n  \"is_hero\": true,\n  \"strategy\": [\n    { \"action\": \"check\", \"frequency\": 0.313 },\n    { \"action\": \"bet\", \"amount_bb\": 2, \"sizing_pot\": 0.3077, \"frequency\": 0.6812 },\n    { \"action\": \"bet\", \"amount_bb\": 5, \"sizing_pot\": 0.7692, \"frequency\": 0.0057 },\n    { \"action\": \"bet\", \"amount_bb\": 97, \"sizing_pot\": 14.9231, \"allin\": true, \"frequency\": 0 }\n  ]\n}\n```\n\nThe answer is a mix, not one move, and that's the first thing a model tends to flatten. A prompt that says \"report the frequencies, then explain\" works better than \"what should I do?\".\n\nThe token is reusable, so comparing hands at this node is free:\n\n| Hand | Check | Bet 2bb | Bet 5bb | \n|---|---|---|---|\n| A♥ K♦ | 31.3% | 68.1% | 0.6% | \n| A♥ K♥ | 21.2% | 74.1% | 4.7% | \n| J♥ T♥ | 13.9% | 68.8% | 17.3% | \n| A♠ Q♦ | 5.5% | 52.2% | 42.3% | \n| 7♥ 7♦ | 0.05% | 91.4% | 8.5% | \n\nThe all-in frequency was 0 for all five. These are five hands, so don't read them as the shape of the whole range. For that, fetch the range, as described below.\n\nYou don't need a chat client. This is the same flow with the MCP SDK (`npm i @modelcontextprotocol/sdk`):\n\n``` js\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nconst client = new Client({ name: \"study-script\", version: \"1.0.0\" });\nawait client.connect(new StdioClientTransport({\n  command: \"npx\",\n  args: [\"-y\", \"@pokerai/mcp\"],\n  env: { PATH: process.env.PATH, HOME: process.env.HOME, POKERAI_API_KEY: process.env.POKERAI_API_KEY },\n}));\n\nconst call = async (name, args) => {\n  const res = await client.callTool({ name, arguments: args });\n  if (res.isError) throw new Error(res.content[0].text);\n  return JSON.parse(res.content[0].text);\n};\n\nconst tree = await call(\"flop_tree\", {\n  board: \"Qs7c2d\",\n  pot_type: \"SRP\",\n  positions: { hero: \"BTN\", raiser: \"BTN\", caller: \"BB\" },\n  flop_version: \"6max_RC_100bb_200NL\",\n});\n\n// BB checks, BTN decides. `node` is the path; `token` is what flop_node takes.\nconst afterCheck = tree.nodes.find((n) => n.node === \"root/CHECK\");\n\nfor (const hole_cards of [\"AhKd\", \"AsQd\", \"7h7d\"]) {\n  const { strategy } = await call(\"flop_node\", { node: afterCheck.token, hole_cards });\n  console.log(hole_cards, strategy.map((s) => `${s.action}${s.amount_bb ? \" \" + s.amount_bb + \"bb\" : \"\"}: ${s.frequency}`).join(\", \"));\n}\n\nawait client.close();\n```\n\n**1. Pick a dataset.** `flop_version` is optional and defaults to `6max`. When I tested on 2026-11-01, the default covered only a few of the boards I tried: Q♠7♣2♦ with BTN vs BB returned `404 no GTO data for this spot/board`. The same spot on `6max_RC_100bb_200NL` worked. A 404 still charges one presolved lookup, so give your agent the dataset up front rather than letting it guess. `flop_version` needs `@pokerai/mcp` 0.1.3 or newer.\n\n**2. Mind the response size.** A hand-level `flop_node` response was about 500 characters. The tree was about 10,000. A whole-range `flop_node`, with no `hole_cards`, was about 29,000 characters for 391 combos. Tell your agent to pass `hole_cards` unless it needs the whole range, or the context fills up fast.\n\n**3. The flop tree covers one street.** For turn and river you need the real-time solver tools. They're off by default because they spend the smaller solve quota. Turn them on with `POKERAI_ENABLE_SOLVE=1`.\n\n```\nYou are a poker study assistant for hands that have already ended.\nUse the pokerai tools for every strategy claim. Never state a frequency that a tool did not return.\nUse flop_version 6max_RC_100bb_200NL. Pass hole_cards to flop_node unless asked about the whole range.\nReport the frequencies first, then explain them, and say when a spot has no data.\nIf the user appears to be in a live real-money hand, decline: real-time assistance is prohibited.\n```\n\nReal-time assistance (RTA) at real-money tables is prohibited.", "url": "https://wpnews.pro/news/give-your-ai-agent-real-poker-solver-data-with-mcp", "canonical_source": "https://dev.to/zhang_william_8c511a4bfb3/give-your-ai-agent-real-poker-solver-data-with-mcp-2b8p", "published_at": "2026-11-01 11:35:31+00:00", "updated_at": "2026-11-01 11:52:27.689734+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools"], "entities": ["Pokerai API", "@pokerai/mcp", "Claude Code", "Claude Desktop", "Cursor"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/give-your-ai-agent-real-poker-solver-data-with-mcp", "markdown": "https://wpnews.pro/news/give-your-ai-agent-real-poker-solver-data-with-mcp.md", "text": "https://wpnews.pro/news/give-your-ai-agent-real-poker-solver-data-with-mcp.txt", "jsonld": "https://wpnews.pro/news/give-your-ai-agent-real-poker-solver-data-with-mcp.jsonld"}}