#5143·fastmcp

Tasks: a poll refreshes the routing keys and the snapshot, but not the stored arguments

Author: sclfczCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbugserver

What happened?

A task's stored tool arguments are written once at creation and never extended, while the other per-task keys are refreshed as the task is used. A task that outlives its arguments' TTL and then re-enters for another leg gets {} from load_task_args() and re-runs the tool with no arguments — silently, with no error and no log.

tests/tasks/server/test_task_ttl.py pins this sliding-refresh behavior for _current_leg_key and for the context snapshot; the args key has no equivalent.

Example Code

Run from the repo root with uv run python repro.py:

python
import asyncio

from fastmcp import FastMCP
from fastmcp_tasks import TasksExtension
from fastmcp_tasks.input_store import _args_key, _current_leg_key
from tests.tasks.task_helpers import get_task, running_task_server, submit_task


async def main():
    mcp = FastMCP("args-ttl")
    mcp.add_extension(TasksExtension())

    @mcp.tool(task=True)
    async def slow_task() -> str:
        await asyncio.Event().wait()

    async with running_task_server(mcp):
        created = await submit_task(mcp, "slow_task", {})
        docket = mcp._docket
        args_key = _args_key(docket, None, created.task_id)
        leg_key = _current_leg_key(docket, None, created.task_id)

        async with docket.redis() as redis:
            await redis.expire(args_key, 5)
            await redis.expire(leg_key, 5)

        await get_task(mcp, created.task_id)  # a poll

        async with docket.redis() as redis:
            print("args TTL after a poll:", await redis.ttl(args_key))
            print("current_leg TTL after a poll:", await redis.ttl(leg_key))


asyncio.run(main())

Output:

args TTL after a poll: 4
current_leg TTL after a poll: 1799

Environment

fastmcp_tasks at main (490049f), Python 3.12, in-memory Docket backend.