Skip to content

Resume tokens example using FastMCP #1296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

TristonianJones
Copy link

Setup and test for the FastMCP with credit to @pwwpche for the source and illustration.

The setup here allows for multiple concurrent orders to be created with incremental polls to collect
information about the progress of each activity. The server does not need to hold the session open
to continue making advancing the customer's order, but will respond to inquiries about progress.

Each time the client sends a request to resume the order a new next_resume_token is generated
even of the state of the order has not advanced. This is a useful technique for ensuring that callers don't
infer semantic meaning from the string provided. Each subsequent request must provide the latest
value from the server in the resume_token sent by the client.

This technique is identical to an eventually consistent server model where the user's desired state
is accepted and eventually the system reflects the desired state; however, any task can provide context
about progress and these systems are setup in a manner where tasks can be served quickly in order
to simplify and improve load-balancing strategies.

You could also apply this technique to progress toward completion of a batch of operations which is
a similar idea with respect to understanding the state of individual entries within the batch without
holding the connection open.

Illustration

# ==============================================================================
# Step 1: Create a new order
#
# This first call to the 'create_order' tool does not include a 'resume_token'.
# This signals the server to start a new asynchronous order creation process.
#
# The server responds immediately with the initial status ("started") and provides
# a 'resume_token'. This token is used in subsequent calls to check the
# progress of this specific order.
# ==============================================================================
$ export DOMAIN=http://localhost:8090/mcp
$ curl -X POST "${DOMAIN}" \
-H "content-type: application/json" \
-H "accept: application/json,text/event-stream" \
-H "mcp-session-id: 123" \
-d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "create_order",
      "arguments": {
        "order_data": {
          "total_amount": 99.99
        }
      }
    }
}'

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n  \"order_id\": \"e106b36e-61e2-4506-b99d-94227e7d7004\",\n  \"status\": \"started\",\n  \"progress_percentage\": 0,\n  \"current_step\": \"Initializing order creation\",\n  \"result\": null,\n  \"error\": null,\n  \"next_resume_token\": \"a1ce7c07-3a0e-41eb-83ee-4fcd214840d8\"\n}"}],"isError":false}}


# ==============================================================================
# Step 2: Check order status using the resume_token
#
# We now call the same 'create_order' tool, but this time we include the
# 'resume_token' from the previous response.
#
# The server uses the token to look up the ongoing operation. Since the
# background task has been running (simulating a 10-second delay per step),
# the status has progressed to "Checking inventory" (30%).
# A new 'resume_token' is issued for the next status check.
# ==============================================================================
$ curl -X POST "${DOMAIN}" \
-H "content-type: application/json" \
-H "accept: application/json,text/event-stream" \
-H "mcp-session-id: 123" \
-d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "create_order",
      "arguments": {
        "order_data": {
          "total_amount": 99.99
        },
        "resume_token": "a1ce7c07-3a0e-41eb-83ee-4fcd214840d8"
       }
     }
 }'

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n  \"order_id\": \"e106b36e-61e2-4506-b99d-94227e7d7004\",\n  \"status\": \"processing\",\n  \"progress_percentage\": 30,\n  \"current_step\": \"Checking inventory\",\n  \"result\": null,\n  \"error\": null,\n  \"next_resume_token\": \"87224d34-6c36-4f23-966e-6d0d027cae73\"\n}"}],"isError":false}}


# ==============================================================================
# Step 3: Continue polling for progress
#
# We repeat the process using the new 'resume_token'.
# The order has now progressed to "Processing payment" (50%).
# Another new 'resume_token' is provided.
# ==============================================================================
$ curl -X POST "${DOMAIN}" \
  -H "content-type: application/json" \
  -H "accept: application/json,text/event-stream" \
  -H "mcp-session-id: 123" \
  -d '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "tools/call",
      "params": {
        "name": "create_order",
        "arguments": {
          "order_data": {
            "total_amount": 99.99
          },
          "resume_token": "87224d34-6c36-4f23-966e-6d0d027cae73"
        }
      }
  }'

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n  \"order_id\": \"e106b36e-61e2-4506-b99d-94227e7d7004\",\n  \"status\": \"processing\",\n  \"progress_percentage\": 50,\n  \"current_step\": \"Processing payment\",\n  \"result\": null,\n  \"error\": null,\n  \"next_resume_token\": \"069b2219-494f-414f-982c-0400b3e83d69\"\n}"}],"isError":false}}


# ==============================================================================
# Step 4: Poll again for further progress
#
# Using the latest 'resume_token', we check the status again.
# The order has advanced to "Preparing shipment" (85%).
# A final 'resume_token' is issued for the last step.
# ==============================================================================
$ curl -X POST "${DOMAIN}" \
  -H "content-type: application/json" \
  -H "accept: application/json,text/event-stream" \
  -H "mcp-session-id: 123" \
  -d '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "tools/call",
      "params": {
        "name": "create_order",
        "arguments": {
          "order_data": {
            "total_amount": 99.99
          },
          "resume_token": "069b2219-494f-414f-982c-0400b3e83d69"
        }
      }
  }'

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n  \"order_id\": \"e106b36e-61e2-4506-b99d-94227e7d7004\",\n  \"status\": \"processing\",\n  \"progress_percentage\": 85,\n  \"current_step\": \"Preparing shipment\",\n  \"result\": null,\n  \"error\": null,\n  \"next_resume_token\": \"c492a5c9-76fd-410f-9326-f7546ed8f9f6\"\n}"}],"isError":false}}


# ==============================================================================
# Step 5: Final status check - Order completion
#
# This final poll shows the order has reached "completed" status (100%).
# The 'result' field is now populated with the final order details.
# Since the process is complete, no new 'next_resume_token' is returned.
# ==============================================================================
$ curl -X POST "${DOMAIN}" \
  -H "content-type: application/json" \
  -H "accept: application/json,text/event-stream" \
  -H "mcp-session-id: 123" \
  -d '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "tools/call",
      "params": {
        "name": "create_order",
        "arguments": {
          "order_data": {
            "total_amount": 99.99
          },
          "resume_token": "c492a5c9-76fd-410f-9326-f7546ed8f9f6"
        }
      }
  }'

event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\n  \"order_id\": \"e106b36e-61e2-4506-b99d-94227e7d7004\",\n  \"status\": \"completed\",\n  \"progress_percentage\": 100,\n  \"current_step\": \"Finalizing order\",\n  \"result\": {\n    \"order_id\": \"e106b36e-61e2-4506-b99d-94227e7d7004\",\n    \"total_amount\": 99.99\n  },\n  \"error\": null\n}"}],"isError":false}}

@TristonianJones TristonianJones requested a review from a team as a code owner August 22, 2025 23:01
@TristonianJones TristonianJones requested a review from ihrpr August 22, 2025 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant