Skip to content

ClientSession: add public API for updating callbacks after initialization #2379

Description

@dgenio

Problem

ClientSession accepts callback parameters (list_roots_callback, sampling_callback, elicitation_callback) at initialization, but provides no public API to update them after the session is created.

This means any client that needs to change callbacks at runtime (e.g., updating roots in response to user action) must mutate private attributes like _list_roots_callback directly — which is fragile and couples consumers to implementation details.

Use case

A client connects to a server with initial roots, then the user changes the working directory or project context. The client needs to update its roots callback so that the next roots/list request from the server reflects the new roots. Today this requires:

session._list_roots_callback = new_callback  # private attribute

The same issue applies to _sampling_callback and _elicitation_callback.

Proposed solution

Add public setter methods on ClientSession for updating callbacks after initialization. For example:

session.set_list_roots_callback(callback)
session.set_sampling_callback(callback)
session.set_elicitation_callback(callback)

Or alternatively, make the callback attributes public (without the leading underscore).

Context

This came up while fixing PrefectHQ/fastmcp#326 — Client.set_roots() wasn't updating the live session because it only modified pending kwargs. The fix (PrefectHQ/fastmcp#3714) mutates _list_roots_callback directly with a comment noting the fragility. A public API would make this safe and stable.

Activity

  1. mgierschdev commented on Apr 1, 2026

    @mgierschdev

    @dgenio looking to do my first contribution inside github, can I take this task?

  2. dgenio commented on Apr 1, 2026

    @dgenio
    Author

    @dgenio looking to do my first contribution inside github, can I take this task?

    Sure, go ahead! I'm not a maintainer so I'm not sure if they agree with this proposal

  3. mgierschdev commented on Apr 2, 2026

    @mgierschdev

    I thought you were, I think I'll better wait for it to be approved

  4. added
    enhancementRequest for a new feature that's not currently supported
    P3Nice to haves, rare edge cases
    needs decisionIssue is actionable, needs maintainer decision on whether to implement
    on Aug 14, 2026
  5. dgenio commented on Sep 16, 2026

    @dgenio
    Author

    Re-checking this after the v2 rewrite: I believe the underlying issue is still relevant, although the right API may be slightly different from the original proposal.

    On current main, ClientSession still stores sampling_callback, elicitation_callback, and list_roots_callback privately. The high-level Client exposes these as public fields, but _build_session() copies them into the ClientSession when the connection is established, so changing them while the client is connected does not affect the live session.

    One distinction that seems important in v2 is replacing callback behavior vs changing advertised capabilities.

    Callbacks participate in client capability advertisement, so enabling a callback after negotiation (or disabling one that was advertised) could leave the negotiated capability state inconsistent. But replacing the implementation of a callback for a capability that is already enabled seems like a valid runtime operation.

    Would maintainers be open to a public API for that narrower operation?

    For example, either:

    session.set_elicitation_callback(callback)
    session.set_sampling_callback(callback)
    session.set_list_roots_callback(callback)

    with clearly documented capability semantics, or public mutable callback properties / another indirection mechanism.

    I'm happy to prepare a fresh v2-native PR once there is agreement on the intended API.

    For the original FastMCP roots use case, I think FastMCP can also avoid callback replacement by keeping a stable callback backed by mutable roots state, so I no longer see that particular case as requiring an SDK change. The broader runtime callback-replacement API still seems useful, particularly for elicitation/sampling and the v2 multi-round-trip flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesenhancementRequest for a new feature that's not currently supportedneeds decisionIssue is actionable, needs maintainer decision on whether to implement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions