# Give your AI agent real poker solver data with MCP

> Source: <https://dev.to/zhang_william_8c511a4bfb3/give-your-ai-agent-real-poker-solver-data-with-mcp-2b8p>
> Published: 2026-11-01 11:35:31+00:00

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.

In 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.

**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.

**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.

The server runs over stdio through `npx`, so there's nothing to install globally. In Claude Code:

```
claude mcp add pokerai -e POKERAI_API_KEY=gto_your_key_here -- npx -y @pokerai/mcp
```

In Claude Desktop or Cursor, use the same command in your MCP config:

```
{
  "mcpServers": {
    "pokerai": {
      "command": "npx",
      "args": ["-y", "@pokerai/mcp"],
      "env": { "POKERAI_API_KEY": "gto_your_key_here" }
    }
  }
}
```

You get five tools:

| Tool | What it returns | Cost | 
|---|---|---|
| `preflop_versions` | available strategy sets | free | 
| `preflop_strategy` | action frequencies for one hand | 1 lookup | 
| `preflop_range` | the whole 169-hand range for a spot | 1 lookup | 
| `flop_tree` | starting ranges and every decision node | 1 lookup | 
| `flop_node` | the strategy at one node | free | 

The 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.

The spot: 100bb, 6-max. The button opens, the big blind calls, and the flop is Q♠ 7♣ 2♦. We hold A♥ K♦.

```
{
  "tool": "preflop_strategy",
  "arguments": {
    "hole_cards": "AhKd",
    "hero": "BTN",
    "preflop_actions": [
      { "position": "SB", "action": "small blind", "amount": 0.5 },
      { "position": "BB", "action": "big blind", "amount": 1 }
    ],
    "preflop_version": "6max_RC_100bb_200NL"
  }
}
{
  "hole_cards": "AhKd",
  "situation": "RFI",
  "strategy": [
    { "action": "raise", "frequency": 1, "amount_bb": 3, "sizing_pot": 0.8 }
  ]
}
```

Always raise, to 3bb. Not much to discuss, so on to the flop.

```
{
  "tool": "flop_tree",
  "arguments": {
    "board": "Qs7c2d",
    "pot_type": "SRP",
    "positions": { "hero": "BTN", "raiser": "BTN", "caller": "BB" },
    "flop_version": "6max_RC_100bb_200NL"
  }
}
```

The response gives the pot (6.5bb), the effective stack (97bb), both starting ranges and 16 nodes. Trimmed:

```
{
  "pot": 6.5,
  "effective_stack": 97,
  "node_count": 16,
  "nodes": [
    { "node": "root", "is_hero": false, "token": "eyJ..." },
    { "node": "root/CHECK", "is_hero": true, "token": "eyJ..." },
    { "node": "root/CHECK/BET_2", "is_hero": false, "token": "eyJ..." }
  ]
}
```

Note the two fields, because this is the easiest mistake to make:

`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 

```
{
  "tool": "flop_node",
  "arguments": { "node": "<token of root/CHECK>", "hole_cards": "AhKd" }
}
{
  "hole_cards": "AhKd",
  "node": "root/CHECK",
  "is_hero": true,
  "strategy": [
    { "action": "check", "frequency": 0.313 },
    { "action": "bet", "amount_bb": 2, "sizing_pot": 0.3077, "frequency": 0.6812 },
    { "action": "bet", "amount_bb": 5, "sizing_pot": 0.7692, "frequency": 0.0057 },
    { "action": "bet", "amount_bb": 97, "sizing_pot": 14.9231, "allin": true, "frequency": 0 }
  ]
}
```

The 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?".

The token is reusable, so comparing hands at this node is free:

| Hand | Check | Bet 2bb | Bet 5bb | 
|---|---|---|---|
| A♥ K♦ | 31.3% | 68.1% | 0.6% | 
| A♥ K♥ | 21.2% | 74.1% | 4.7% | 
| J♥ T♥ | 13.9% | 68.8% | 17.3% | 
| A♠ Q♦ | 5.5% | 52.2% | 42.3% | 
| 7♥ 7♦ | 0.05% | 91.4% | 8.5% | 

The 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.

You don't need a chat client. This is the same flow with the MCP SDK (`npm i @modelcontextprotocol/sdk`):

``` js
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const client = new Client({ name: "study-script", version: "1.0.0" });
await client.connect(new StdioClientTransport({
  command: "npx",
  args: ["-y", "@pokerai/mcp"],
  env: { PATH: process.env.PATH, HOME: process.env.HOME, POKERAI_API_KEY: process.env.POKERAI_API_KEY },
}));

const call = async (name, args) => {
  const res = await client.callTool({ name, arguments: args });
  if (res.isError) throw new Error(res.content[0].text);
  return JSON.parse(res.content[0].text);
};

const tree = await call("flop_tree", {
  board: "Qs7c2d",
  pot_type: "SRP",
  positions: { hero: "BTN", raiser: "BTN", caller: "BB" },
  flop_version: "6max_RC_100bb_200NL",
});

// BB checks, BTN decides. `node` is the path; `token` is what flop_node takes.
const afterCheck = tree.nodes.find((n) => n.node === "root/CHECK");

for (const hole_cards of ["AhKd", "AsQd", "7h7d"]) {
  const { strategy } = await call("flop_node", { node: afterCheck.token, hole_cards });
  console.log(hole_cards, strategy.map((s) => `${s.action}${s.amount_bb ? " " + s.amount_bb + "bb" : ""}: ${s.frequency}`).join(", "));
}

await client.close();
```

**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.

**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.

**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`.

```
You are a poker study assistant for hands that have already ended.
Use the pokerai tools for every strategy claim. Never state a frequency that a tool did not return.
Use flop_version 6max_RC_100bb_200NL. Pass hole_cards to flop_node unless asked about the whole range.
Report the frequencies first, then explain them, and say when a spot has no data.
If the user appears to be in a live real-money hand, decline: real-time assistance is prohibited.
```

Real-time assistance (RTA) at real-money tables is prohibited.
