mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-23 05:54:19 +02:00
Prefix Redis keys with docket name for ACL isolation (#2811)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
627c6cdad4
commit
08caf77ef6
5 changed files with 25 additions and 20 deletions
|
|
@ -10,7 +10,7 @@ dependencies = [
|
|||
"mcp>=1.24.0,<2.0",
|
||||
"openapi-pydantic>=0.5.1",
|
||||
"platformdirs>=4.0.0",
|
||||
"pydocket>=0.16.3",
|
||||
"pydocket>=0.16.4",
|
||||
"rich>=13.9.4",
|
||||
"cyclopts>=4.0.0",
|
||||
"authlib>=1.6.5",
|
||||
|
|
|
|||
|
|
@ -88,12 +88,16 @@ async def submit_to_docket(
|
|||
ttl_seconds = int(ttl_ms / 1000) + TASK_MAPPING_TTL_BUFFER_SECONDS
|
||||
|
||||
# Store task metadata in Redis for protocol handlers
|
||||
redis_key = f"fastmcp:task:{session_id}:{server_task_id}"
|
||||
created_at_key = f"fastmcp:task:{session_id}:{server_task_id}:created_at"
|
||||
poll_interval_key = f"fastmcp:task:{session_id}:{server_task_id}:poll_interval"
|
||||
task_meta_key = docket.key(f"fastmcp:task:{session_id}:{server_task_id}")
|
||||
created_at_key = docket.key(
|
||||
f"fastmcp:task:{session_id}:{server_task_id}:created_at"
|
||||
)
|
||||
poll_interval_key = docket.key(
|
||||
f"fastmcp:task:{session_id}:{server_task_id}:poll_interval"
|
||||
)
|
||||
poll_interval_ms = int(component.task_config.poll_interval.total_seconds() * 1000)
|
||||
async with docket.redis() as redis:
|
||||
await redis.set(redis_key, task_key, ex=ttl_seconds)
|
||||
await redis.set(task_meta_key, task_key, ex=ttl_seconds)
|
||||
await redis.set(created_at_key, created_at.isoformat(), ex=ttl_seconds)
|
||||
await redis.set(poll_interval_key, str(poll_interval_ms), ex=ttl_seconds)
|
||||
|
||||
|
|
|
|||
|
|
@ -60,15 +60,18 @@ async def _lookup_task_execution(
|
|||
Raises:
|
||||
McpError: If task not found or execution not found
|
||||
"""
|
||||
# Build Redis keys
|
||||
redis_key = f"fastmcp:task:{session_id}:{client_task_id}"
|
||||
created_at_key = f"{redis_key}:created_at"
|
||||
poll_interval_key = f"{redis_key}:poll_interval"
|
||||
task_meta_key = docket.key(f"fastmcp:task:{session_id}:{client_task_id}")
|
||||
created_at_key = docket.key(
|
||||
f"fastmcp:task:{session_id}:{client_task_id}:created_at"
|
||||
)
|
||||
poll_interval_key = docket.key(
|
||||
f"fastmcp:task:{session_id}:{client_task_id}:poll_interval"
|
||||
)
|
||||
|
||||
# Fetch metadata (single round-trip with mget)
|
||||
async with docket.redis() as redis:
|
||||
task_key_bytes, created_at_bytes, poll_interval_bytes = await redis.mget(
|
||||
redis_key, created_at_key, poll_interval_key
|
||||
task_meta_key, created_at_key, poll_interval_key
|
||||
)
|
||||
|
||||
# Decode and validate task_key
|
||||
|
|
@ -225,9 +228,9 @@ async def tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any:
|
|||
)
|
||||
|
||||
# Look up full task key from Redis
|
||||
redis_key = f"fastmcp:task:{session_id}:{client_task_id}"
|
||||
task_meta_key = docket.key(f"fastmcp:task:{session_id}:{client_task_id}")
|
||||
async with docket.redis() as redis:
|
||||
task_key_bytes = await redis.get(redis_key)
|
||||
task_key_bytes = await redis.get(task_meta_key)
|
||||
|
||||
task_key = None if task_key_bytes is None else task_key_bytes.decode("utf-8")
|
||||
|
||||
|
|
|
|||
|
|
@ -107,8 +107,7 @@ async def _send_status_notification(
|
|||
key_parts = parse_task_key(task_key)
|
||||
session_id = key_parts["session_id"]
|
||||
|
||||
# Retrieve createdAt timestamp from Redis
|
||||
created_at_key = f"fastmcp:task:{session_id}:{task_id}:created_at"
|
||||
created_at_key = docket.key(f"fastmcp:task:{session_id}:{task_id}:created_at")
|
||||
async with docket.redis() as redis:
|
||||
created_at_bytes = await redis.get(created_at_key)
|
||||
|
||||
|
|
@ -183,8 +182,7 @@ async def _send_progress_notification(
|
|||
key_parts = parse_task_key(task_key)
|
||||
session_id = key_parts["session_id"]
|
||||
|
||||
# Retrieve createdAt timestamp from Redis
|
||||
created_at_key = f"fastmcp:task:{session_id}:{task_id}:created_at"
|
||||
created_at_key = docket.key(f"fastmcp:task:{session_id}:{task_id}:created_at")
|
||||
async with docket.redis() as redis:
|
||||
created_at_bytes = await redis.get(created_at_key)
|
||||
|
||||
|
|
|
|||
8
uv.lock
generated
8
uv.lock
generated
|
|
@ -754,7 +754,7 @@ requires-dist = [
|
|||
{ name = "platformdirs", specifier = ">=4.0.0" },
|
||||
{ name = "py-key-value-aio", extras = ["disk", "keyring", "memory"], specifier = ">=0.3.0,<0.4.0" },
|
||||
{ name = "pydantic", extras = ["email"], specifier = ">=2.11.7" },
|
||||
{ name = "pydocket", specifier = ">=0.16.3" },
|
||||
{ name = "pydocket", specifier = ">=0.16.4" },
|
||||
{ name = "pyperclip", specifier = ">=1.9.0" },
|
||||
{ name = "python-dotenv", specifier = ">=1.1.0" },
|
||||
{ name = "rich", specifier = ">=13.9.4" },
|
||||
|
|
@ -1786,7 +1786,7 @@ wheels = [
|
|||
|
||||
[[package]]
|
||||
name = "pydocket"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cloudpickle" },
|
||||
|
|
@ -1803,9 +1803,9 @@ dependencies = [
|
|||
{ name = "typer" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/c5/61dcfce4d50b66a3f09743294d37fab598b81bb0975054b7f732da9243ec/pydocket-0.16.3.tar.gz", hash = "sha256:78e9da576de09e9f3f410d2471ef1c679b7741ddd21b586c97a13872b69bd265", size = 297080, upload-time = "2025-12-23T23:37:33.32Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4d/c6/eb7f3af72fa5c04b52a3f9390ff0c948987441987f9526dd992d2a6b3524/pydocket-0.16.4.tar.gz", hash = "sha256:d034d1ac75877560d86329fb3643e7b862fcbcdac407d876a62f5d9e386e8753", size = 297949, upload-time = "2026-01-08T21:58:31.637Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/94/93b7f5981aa04f922e0d9ce7326a4587866ec7e39f7c180ffcf408e66ee8/pydocket-0.16.3-py3-none-any.whl", hash = "sha256:e2b50925356e7cd535286255195458ac7bba15f25293356651b36d223db5dd7c", size = 67087, upload-time = "2025-12-23T23:37:31.829Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/c5/e6ffed3902ead6cb906758749c42211f7f24ea6d8fdd772f531f5a81c9fa/pydocket-0.16.4-py3-none-any.whl", hash = "sha256:cdcdf74b987c2cd5d03c7353d15f8dd2ac9bd43f2a91f7441748d5a8ebd617c9", size = 67374, upload-time = "2026-01-08T21:58:30.01Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue