In a multi-user deployment — several sessions on one host, a remote console, a shared HTTP endpoint —
different callers legitimately deserve different command graphs. Repl has no way to express that
today: the routing graph is application-global, and its MCP projection varies only through module
presence predicates resolved against session services.
Why this is not simply "reinstate per-session gating"
Revision 2026-07-28 states that the advertised tool set
MUST NOT vary per-connection or as a side effect of other requests on the connection — which is
why #71 makes the modern discovery view invariant. But the same sentence carries the exception that
fits this feature exactly:
The set MAY vary by the authorization presented on the request — for example, returning only
the tools the caller's granted scopes permit — since credentials are per-request input, not
connection state.
So a per-caller graph is permitted when it is driven by authorization presented on the request,
and prohibited when it is driven by connection state (client capabilities, per-session flags). That
distinction should shape the design rather than be discovered late:
- the graph varies as a function of the request's credentials, not of the connection;
- two requests presenting the same authorization must receive the same set, on any connection;
- the snapshot cache key becomes the authorization identity, not the session.
What exists today
CoreReplApp.InvalidateRouting() is application-global and bumps McpServerHandler._snapshotState,
a handler-wide version.
McpSessionContext.SnapshotCache is per session, and BuildSnapshotCore projects against that
session's service overlay — so the signal is global while the result is per session.
_toolListChanged and its siblings are handler fields shared by every server the handler builds,
so a list_changed reaches every connection.
A per-caller graph needs the cache and the notification fan-out to become per-identity rather than
per-session or global. That is the same machinery #70 and #74 are circling, so it should be sequenced
after them.
Out of scope for #71
#71 makes the modern view invariant, which is the conformant baseline. This issue is the feature that
would reintroduce variance through the one door the specification leaves open.
In a multi-user deployment — several sessions on one host, a remote console, a shared HTTP endpoint —
different callers legitimately deserve different command graphs. Repl has no way to express that
today: the routing graph is application-global, and its MCP projection varies only through module
presence predicates resolved against session services.
Why this is not simply "reinstate per-session gating"
Revision
2026-07-28states that the advertised tool setMUST NOT vary per-connection or as a side effect of other requests on the connection — which is
why #71 makes the modern discovery view invariant. But the same sentence carries the exception that
fits this feature exactly:
So a per-caller graph is permitted when it is driven by authorization presented on the request,
and prohibited when it is driven by connection state (client capabilities, per-session flags). That
distinction should shape the design rather than be discovered late:
What exists today
CoreReplApp.InvalidateRouting()is application-global and bumpsMcpServerHandler._snapshotState,a handler-wide version.
McpSessionContext.SnapshotCacheis per session, andBuildSnapshotCoreprojects against thatsession's service overlay — so the signal is global while the result is per session.
_toolListChangedand its siblings are handler fields shared by every server the handler builds,so a
list_changedreaches every connection.A per-caller graph needs the cache and the notification fan-out to become per-identity rather than
per-session or global. That is the same machinery #70 and #74 are circling, so it should be sequenced
after them.
Out of scope for #71
#71 makes the modern view invariant, which is the conformant baseline. This issue is the feature that
would reintroduce variance through the one door the specification leaves open.