1f3fbc7ca3
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.