Every package init in the SDK resolves its submodules on attribute
access except four docstring-only leaves - mcp.os.posix, mcp.os.win32,
mcp.server.auth.handlers and mcp.server.auth.middleware - so a chain like
`import mcp; mcp.server.auth.handlers.token` stopped resolving even though
`mcp.client.stdio` and friends do. Give them the same lazy fallback, so
"attribute chains still resolve" is true without a footnote (explicit
imports remain the supported form), and pin one chain through each in the
ratchet.
Three implementations of lazy module attributes had grown: the
hand-rolled __getattr__/__dir__ in mcp/__init__.py, the submodule
fallback factory used by the four package inits, and a one-name
__getattr__ in mcp.server.elicitation. They shared two problems.
An unconditional module-level __getattr__ is visible to type checkers,
so pyright typed every misspelled `mcp.<name>` (and `mcp.client.<name>`,
...) as `object` instead of reporting it. And the submodule fallback ran
a filesystem find_spec on every attribute miss and speculatively imported
whatever matched, so a name sweep (cloudpickle's whichmodule, hasattr
probes) could import real submodules as a side effect, while
dir(mcp.client) no longer listed submodules it would happily resolve.
mcp.shared._lazy.lazy_module_attrs now serves all of them: lazy exports
(a `(module, attr)` pair or a zero-argument loader, resolved once and
cached in the namespace) plus known submodules (an explicit set, or the
package's real submodules listed once on first need). A miss is a plain
AttributeError with no search and no import, and __dir__ reports the
exports and submodules. Every caller binds the pair under
`if not TYPE_CHECKING:`, so attribute typos are pyright errors again
while the TYPE_CHECKING mirrors keep the real names typed. The
mcp.server.auth package gets the same fallback so qualified annotations
such as `mcp.server.auth.provider.TokenVerifier` resolve on demand.
The elicitation gate's wire-schema type is resolved through the same
mechanism from a cached accessor, so validating rendered schemas no
longer re-executes the wire-package import on every call.