I assume what the GP means is to infer a strong type for a struct that has all the data a context has in it. This is much harder, for many reasons. A context that comes in from one path may have a RequestSource in it, but another may not; the resulting type of the function is rather complicated to infer. We prefer not to have two different functions as a result. There's also the problem that such inferences end up strongly tying types across many functions together, such that up in some middleware for your web site you add a new value into the context, and if there was a strong type for that context, that strong type would cascade throughout everything that could someday possibly touch that context. The result is much like checked exceptions in Java.
This is perhaps the hardest practical type problem I know. It seems to me to be very related to a similar problem, which is that of trying to strongly type errors. The sort of cross-scope type inference we envision in our heads is extremely unwieldy in practice, if you take the time to try to scope out what that would convert to in a real program with many nested scopes and arbitrarily complicated paths in to those scopes, plus arbitrarily complicated closures being passed around that further impact the types.
Contexts are at least a value you can see and manipulate rather than having something attached to your thread, and in particular, can pass across thread boundaries if you need to.
One way or another, you end up needing some sort of scope that carries values that can't be strongly typed because the way you're composing those values doesn't work with any known strong type system in practical use. (I've seen some super theoretical ones that can in theory do it but I've never seen them brushed up into something practical and successful.)
C# is pretty neat in having an AsyncLocal[0] that goes beyond what thread locals enable
[0]: https://vainolo.com/2022/02/23/storing-context-data-in-c-usi...