On this page
  • MCP
  • OAuth 2.1
  • Streamable HTTP

Estimay MCP server

Support

Estimay runs a remote Model Context Protocol server, so an AI assistant can create planning poker rooms, add stories, read a room and record the agreed estimates, as the person who connected it. This page lists how to connect it, how it signs in, and every tool. For what it is good for, see planning poker from your AI assistant.

REST API. There is no public REST API yet: the MCP server is how other software works with Estimay today. A public API, with personal access tokens and an OpenAPI description, is on our roadmap.

Server

  • URL:https://estimay.io/api/mcp
  • Transport: Streamable HTTP, stateless: every request stands alone, so there is no session to resume and GET streams are not offered. Both the current protocol and the 2025 revisions are served.
  • Auth: OAuth 2.1 bearer tokens (below). A request without one is answered 401 with a WWW-Authenticate header pointing to the protected resource metadata.
  • Price: free today, with no account, like the rest of Estimay.

Connecting a client

Add the server the way your client takes it; the Connect panel has each one to copy.

URL
Claude (claude.ai and the desktop app): Settings → Connectors → Add custom connector, then paste it. ChatGPT, VS Code and most other clients take a URL in their "Add server" dialog too.
CLI
Claude Code. Then type /mcp in a session and choose estimay to sign in.
JSON config
For clients configured with a file, such as Cursor (~/.cursor/mcp.json, or .cursor/mcp.json in a project).

The first time a tool is called, the client opens Estimay in your browser. You allow it there, as the guest that browser already is: rooms it creates are hosted by you in that browser.

Authentication

Estimay has no accounts, so the authorization server binds each connection to a browser's guest identity. Clients find everything themselves:

  1. Discovery.https://estimay.io/.well-known/oauth-protected-resource/api/mcp (RFC 9728) names the authorization server, whose metadata is at https://estimay.io/.well-known/oauth-authorization-server (RFC 8414).
  2. Registration.POST /api/oauth/register (RFC 7591) with redirect_uris: https, http to localhost or 127.0.0.1, or an app's own scheme. Public clients (none) and client_secret_post / client_secret_basic are supported.
  3. Authorization.GET /api/oauth/authorize with PKCE (S256 only). The person sees the consent page, chooses Read only or Read & write, and the browser returns to the client with a code (and iss, RFC 9207).
  4. Tokens.POST /api/oauth/token trades the code for an access token (one hour) and a refresh token. Each refresh returns a new refresh token and retires the old one.

Scopes

ScopeAllows
rooms:read Reading the rooms the person is in, with the READ tools: get_room, export_results.
rooms:write Every tool. Asked for by default; the person can still choose Read only.

A connection unused for 30 days is deleted. The person sees and disconnects their connections under Connected assistants; a disconnected connection's next request gets 401.

Tools

Every tool that takes a room accepts its 5-character code or its invitation link. Each answers with the room as the person's own view shows it: its cards (deck), the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results. Votes are never shown before the reveal.

Tools are called with JSON-RPC tools/call at the server's URL; an answer's content holds one text item.

create_room

WRITE

Create a room

Creates a planning poker room with you as its host, optionally with its first stories. Returns the room and its invitation link to share with the team.

Scope: rooms:write

Arguments

  • titlestringoptional

    The room name everyone sees, like "Sprint 42 planning".

  • modelive | asyncoptional

    live (the default): the team votes together on the story on the table. async: every story is open for voting as it is added, and people vote in their own time.

  • deckfibonacci | modifiedFibonacci | tshirt | powersOfTwo | aiTokens | risk | clarity | agentReadyoptional

    The cards to vote with. Fibonacci when left out.

  • custom_cardsstring[]optional

    The room's own cards instead of a deck, 2 to 20 of up to 5 characters.

  • anonymousbooleanoptional

    Show revealed votes only as totals, never who picked which card.

  • auto_revealbooleanoptional

    Live rooms: turn the cards over once everyone present has voted.

  • passwordstringoptional

    A password newcomers must type. Left out, anyone with the link can join.

  • stories{ title, ticket? }[]optional

    Up to 200 stories, in the order to estimate them.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_room",
    "arguments": {
      "title": "Sprint 42 planning",
      "stories": [
        {
          "title": "Refund to the original card"
        }
      ]
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

join_room

WRITE

Join a room

Joins a room someone else created, so you can read it and act in it. Not needed for rooms you created.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • passwordstringoptional

    The room's password, when it has one.

  • spectatorbooleanoptional

    Join to watch, without voting.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "join_room",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

get_room

READ

Read a room

Reads a room you are in: its cards, who is there, the story on the table, and every story with its status and agreed estimate. Votes show only once revealed.

Scope: rooms:read

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_room",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

add_stories

WRITE

Add stories

Adds stories to a room's list, on top, in the order given. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • stories{ title, ticket? }[]required

    Up to 200 stories, in the order to estimate them.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "add_stories",
    "arguments": {
      "room": "AB2CD",
      "stories": [
        {
          "title": "Refund to the original card"
        }
      ]
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

start_voting

WRITE

Start voting

Puts a story on the table for the team to vote on. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • story_idstring (story id)optional

    The story to put on the table. Left out, the next one not estimated yet.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "start_voting",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

vote

WRITE

Vote

Votes on the story on the table as you. Only when the person asks you to vote for them, with the card they chose: never offer the team a number before the reveal.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • cardstringrequired

    One of the room's cards, as get_room lists them in deck.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "vote",
    "arguments": {
      "room": "AB2CD",
      "card": "5"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

reveal

WRITE

Reveal the cards

Turns the cards over on the story on the table; live rooms count down for a few seconds first. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "reveal",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

set_estimate

WRITE

Set the estimate

Saves a story's agreed estimate once its cards are revealed, or on any story not being voted on. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • estimatestring or nullrequired

    The agreed estimate, one of the room's cards; null clears it.

  • story_idstring (story id)optional

    The story to estimate. Left out, the revealed story on the table.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "set_estimate",
    "arguments": {
      "room": "AB2CD",
      "estimate": "5"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

edit_story

WRITE

Edit a story

Changes a story's title or tracker key. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • story_idstring (story id)required

    A story's id, as get_room lists it.

  • titlestringrequired

    The new title.

  • ticketstringoptional

    The new tracker key. Left out, it stays as it is.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "edit_story",
    "arguments": {
      "room": "AB2CD",
      "story_id": "3f8c2a6e-5b1d-4e7a-9c0f-2d4b6a8e1c3f",
      "title": "Refund to the original card, in cents"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

delete_story

WRITE

Delete a story

Removes a story and its votes from the room. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • story_idstring (story id)required

    A story's id, as get_room lists it.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "delete_story",
    "arguments": {
      "room": "AB2CD",
      "story_id": "3f8c2a6e-5b1d-4e7a-9c0f-2d4b6a8e1c3f"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

revote

WRITE

Vote again

Clears the cards on the story on the table and opens it for another round, when the team wants to vote again after talking. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "revote",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

start_timer

WRITE

Start the discussion timer

Starts a discussion timer everyone in a live room sees, or stops it with null. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • secondsinteger or nullrequired

    The discussion's length in seconds, 1 to 3599; null stops the timer.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "start_timer",
    "arguments": {
      "room": "AB2CD",
      "seconds": 120
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

change_deck

WRITE

Change the cards

Changes the room's cards to a built-in deck or its own; votes still open are cleared. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • deckfibonacci | modifiedFibonacci | tshirt | powersOfTwo | aiTokens | risk | clarity | agentReadyoptional

    A built-in deck. Give this or custom_cards.

  • custom_cardsstring[]optional

    The room's own cards, 2 to 20 of up to 5 characters. Give this or deck.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "change_deck",
    "arguments": {
      "room": "AB2CD",
      "deck": "fibonacci"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

set_facilitator

WRITE

Make someone a facilitator

Lets someone in the room run the game with the host (stories, voting, reveal, settings), or takes it back. Host only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • personstringrequired

    The person's name, as get_room lists it.

  • facilitatorbooleanrequired

    true to let them run the game (stories, voting, reveal, settings), false to take it back.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "set_facilitator",
    "arguments": {
      "room": "AB2CD",
      "person": "Ada",
      "facilitator": true
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

export_results

READ

Export the results

The room's stories with their agreed estimates and extra answers, and the total, as a Markdown table or CSV, ready to paste into notes, a ticket or a spreadsheet.

Scope: rooms:read

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

  • formatmarkdown | csvoptional

    markdown (the default): a table and the total. csv: comma-separated text.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "export_results",
    "arguments": {
      "room": "AB2CD",
      "format": "markdown"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

A Markdown table of the stories with their agreed estimates and the total, or comma-separated CSV.

write_estimates

WRITE

Write estimates to the tracker

Writes every agreed estimate back to the Jira, Linear, Trello or Asana item it was imported from, through the room's own tracker connection. Host or facilitator only.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "write_estimates",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

A line per tracker: how many estimates were written to it, and how many of its stories have none yet or are deleted there. When the room isn't connected to a tracker, what to do instead.

end_room

WRITE

End the room

Ends the room and deletes it for everyone, stories and votes included. Host only. Export the results first if they are needed.

Scope: rooms:write

Arguments

  • roomstringrequired

    The room: its 5-character code (like K7PQM) or its invitation link.

Request exampletools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "end_room",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Response
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "…"
      }
    ]
  }
}

The room as JSON, as the person's own view shows it: its cards, the people in it, the story on the table, and every story with its status, estimate and, once revealed, its results.

Prompts

Ready-made requests your assistant can offer as commands (in Claude Code, as /mcp__estimay__<name>). Every argument is optional text; the message comes back filled in, with a fallback for each one left out.

plan_sprint

Set up a planning poker room

Gathers the stories to estimate and creates an Estimay room with them, ready to share.

Arguments

  • sourcestringoptional

    Where the stories come from, like "the top of our Jira backlog" or "milestone 3 on GitHub".

  • titlestringoptional

    The room name, like "Sprint 42 planning".

Request exampleprompts/get
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prompts/get",
  "params": {
    "name": "plan_sprint",
    "arguments": {
      "source": "the top of our Jira backlog",
      "title": "Sprint 42 planning"
    }
  }
}
Messagerole: user
Set up an Estimay planning poker room called "Sprint 42 planning" for the top of our Jira backlog.
1. Gather the stories (use my tracker's own tools if it is connected). Keep each title to one line and keep the tracker key as the ticket.
2. Show me the list and ask whether anything should be added, dropped or reordered.
3. Call create_room once with all the stories, in that order.
4. Give me the room link to share with the team.
Do not suggest estimates: the team votes on its own, in the room.

refine_stories

Refine stories before the vote

Critiques stories the team wrote (edge cases, assumptions, gaps in acceptance criteria) without estimating them.

Arguments

  • storiesstringoptional

    The stories to refine, or where to find them.

Request exampleprompts/get
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prompts/get",
  "params": {
    "name": "refine_stories",
    "arguments": {
      "stories": "PAY-142 and PAY-151"
    }
  }
}
Messagerole: user
Help us refine PAY-142 and PAY-151 before we estimate them.
For each story list: the edge cases, the assumptions it makes, the acceptance criteria it is missing, and risks or dependencies on other teams.
Where a story looks too big, propose a split by workflow step or by data variation, keeping the happy path first.
Keep it short, as a list per story the team can tick or cross out. Do not give sizes or estimates.
If we agree on changes, update the stories in the room with edit_story or add_stories.

record_outcome

Record the estimates

Reads a finished room and records its agreed estimates: in the tracker, and as a summary for the team's notes.

Arguments

  • roomstringoptional

    The room's code or link.

Request exampleprompts/get
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prompts/get",
  "params": {
    "name": "record_outcome",
    "arguments": {
      "room": "AB2CD"
    }
  }
}
Messagerole: user
Record the outcome of AB2CD.
1. Call export_results and show me the table and the total.
2. Point out the stories whose votes were far apart (get_room has the results) as questions for the team, not as judgements.
3. If the room's stories came from Jira, Linear, Trello or Asana, call write_estimates. Otherwise offer to update the issues with my tracker's own tools.

Limits and errors

  • Rate: 120 requests a minute per connection; more are answered 429.
  • Stories: up to 200 in one create_room or add_stories; titles up to 120 characters, tracker keys up to 40.
  • Who may do what: the room's rules apply as in the browser. Adding stories, starting a vote, revealing and setting estimates need the host or a facilitator; anyone in the room may vote.
  • Errors come back as tool results with isError, in plain words with what to do next ("Join the session first. Call join_room first."), so the assistant can recover by itself.

Questions or a client that doesn't connect? Tell us, with the client's name and what it said.