mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-14 09:39:11 +02:00
* Scope tasks to authorization context, not session Tasks were keyed by the transport-layer Mcp-Session-Id, which is server-assigned and changes on reconnect — so clients lost access to their running tasks after any connection interruption. The MCP spec says tasks should be bound to authorization context, not session. This replaces session_id with task_scope (derived from AccessToken.client_id, URL-encoded) in all task data Redis keys and Docket task keys. When no auth is configured, a "_" sentinel is used and security comes from UUID task ID entropy per the spec. Session ID is still used for transport-level concerns (notification queues, subscriber registration) and is now stored in the TaskContextSnapshot payload so background workers can still deliver notifications. Also extracts all the task context infrastructure (TaskContextInfo, TaskContextSnapshot, snapshot loading, session/server registries) from server/dependencies.py into a new server/tasks/context.py to keep the DI module from sprawling further. Closes #3758 🤖 Generated with Claude Code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Rename _redis_key to _snapshot_redis_key Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Document in-process session registry as an optimization Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Tidy imports and docstrings Hoist imports where safe, keep subscriptions/notifications deferred in handlers.py since they pull in docket at module level. Sharpen docstrings on keys.py and context.py so each module owns its lane. Clean up the re-export block in dependencies.py. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix misleading comment on re-export block Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Tighten task scope: include sub claim, partition keyspaces Addresses review feedback on #3800: - Compose task scope from client_id and the JWT sub claim (when present) so fixed-OAuth deployments isolate per user, not just per client. - Replace the "_" anonymous sentinel with a tagged keyspace partition. Docket keys are now auth:{enc_scope}:... or anon:..., and Redis keys use fastmcp:task:auth:{enc_scope}:... or fastmcp:task:anon:..., routed through a single task_redis_prefix() helper. - get_task_scope() returns the raw scope (or None); encoding happens once at the keys.py boundary, collapsing the previous double-quote invariant. - Drop the dormant fallback in notifications.py that routed input_required relays into the anon keyspace when task_scope was missing -- log and skip instead. - Add comprehensive parser/encoder tests in test_task_keys.py covering round-trips, malformed keys, and adversarial scopes ("anon", "_", and scopes containing : / | %). - Add cross-scope rejection tests: distinct client_ids, distinct sub claims under a shared client_id, and authenticated vs anonymous. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| __init__.py | ||
| conftest.py | ||
| test_concurrent_dependencies.py | ||
| test_context_background_task.py | ||
| test_custom_subclass_tasks.py | ||
| test_notifications.py | ||
| test_progress_dependency.py | ||
| test_resource_task_meta_parameter.py | ||
| test_server_tasks_parameter.py | ||
| test_sync_function_task_disabled.py | ||
| test_task_capabilities.py | ||
| test_task_config.py | ||
| test_task_dependencies.py | ||
| test_task_elicitation_relay.py | ||
| test_task_keys.py | ||
| test_task_meta_parameter.py | ||
| test_task_metadata.py | ||
| test_task_methods.py | ||
| test_task_mount.py | ||
| test_task_prompts.py | ||
| test_task_protocol.py | ||
| test_task_proxy.py | ||
| test_task_resources.py | ||
| test_task_return_types.py | ||
| test_task_security.py | ||
| test_task_status_notifications.py | ||
| test_task_tools.py | ||
| test_task_ttl.py | ||