|
| 1 | +# Public API Hardening Plan for v2 |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | + |
| 5 | +In v1, nearly every module, class, function, and type was importable by end users—even internal implementation details. This meant routine refactors (renaming a helper, reordering a base class, changing an internal constant) could break downstream code. The root cause: there was no enforced distinction between "public" and "private." |
| 6 | + |
| 7 | +Audit of the current surface reveals: |
| 8 | + |
| 9 | +- **~40 implementation modules** have no `_` prefix and are directly importable, but are never surfaced through any `__init__.py`—they are accidental public API |
| 10 | +- **Zero implementation modules** (except `_httpx_utils.py`) declare `__all__`, so star imports from them leak every top-level name |
| 11 | +- **Name collisions** exist (two independent `RequestContext` classes in `shared.context` and `client.streamable_http`) |
| 12 | +- **`shared/` has an empty `__init__.py`**—every internal utility is importable via `from mcp.shared.session import BaseSession` |
| 13 | +- **`server/auth/` subtree has zero re-exports**—all auth internals are reachable by deep module path but never declared public |
| 14 | +- **The `mcp/__init__.py` imports `ServerSession` directly from `server/session.py`**, bypassing `server/__init__.py` |
| 15 | + |
| 16 | +## Design Principles |
| 17 | + |
| 18 | +1. **Private by default.** Every symbol starts as private. Public status must be explicitly granted via `__all__` in the package `__init__.py`. |
| 19 | +2. **Module filenames enforce privacy.** Implementation files are named `_module.py`. Only `__init__.py` files are the public face of each package. |
| 20 | +3. **Stable import paths.** Users import from package roots (`mcp`, `mcp.types`, `mcp.server`, `mcp.client`), never from leaf implementation files. |
| 21 | +4. **Protocol types are inherently public.** The `mcp.types` module mirrors the MCP spec schema—every type in the spec is public. |
| 22 | +5. **Experimental is opt-in and explicitly unstable.** Anything under `.experimental` namespaces carries no stability guarantee. |
| 23 | +6. **Enforcement is automated.** A CI-checked allowlist script prevents accidental surface growth. |
| 24 | + |
| 25 | +## Methodology |
| 26 | + |
| 27 | +The enumeration uses a "private-by-default, explicitly public" approach: |
| 28 | + |
| 29 | +1. **Enumerate** every importable symbol in the package (automated — see Tooling) |
| 30 | +2. **Classify** each symbol: Public / Private / Experimental / Needs-Decision |
| 31 | +3. **Public symbols**: surface through the correct `__init__.py` + `__all__`, reachable from a stable path |
| 32 | +4. **Private symbols**: rename the containing module to `_module.py`, or prefix the name with `_` |
| 33 | +5. **Needs-Decision**: explicit team call before shipping |
| 34 | +6. **Enforce**: CI audit script compares the live importable surface against the checked-in allowlist on every PR |
| 35 | + |
| 36 | +--- |
| 37 | + |
| 38 | +## Recommended Tooling |
| 39 | + |
| 40 | +### 1. Custom CI audit script (`scripts/audit_public_api.py`) — **most important** |
| 41 | + |
| 42 | +This is the enforcement mechanism that makes the public API a reviewable artifact. It does: |
| 43 | + |
| 44 | +- Walks all modules in `src/mcp/` |
| 45 | +- Flags any non-`__init__.py` module without a `_` prefix as a "leaked module" |
| 46 | +- Flags any `__init__.py` missing `__all__` |
| 47 | +- Collects the full public surface (union of all `__all__` declarations) |
| 48 | +- Compares against a checked-in allowlist (`docs/public_api_allowlist.txt`) |
| 49 | +- Fails if any new name appears that isn't allowlisted, or if an allowlisted name disappears |
| 50 | + |
| 51 | +This runs in CI on every PR. Adding to the public API becomes an explicit, reviewable choice. |
| 52 | + |
| 53 | +### 2. `griffe` — already available via `mkdocstrings` |
| 54 | + |
| 55 | +`griffe` extracts the full public API from source without executing code, respecting `__all__` and `_` conventions. Since it's already a dependency for the docs build, it can power the audit script or standalone analysis: |
| 56 | + |
| 57 | +```python |
| 58 | +from griffe import load |
| 59 | + |
| 60 | +package = load("mcp") |
| 61 | +# package.members gives the full public surface tree |
| 62 | +``` |
| 63 | + |
| 64 | +This also means the mkdocs API reference docs automatically reflect only the declared public surface once we tighten the modules. |
| 65 | + |
| 66 | +### 3. `pyright` `reportPrivateUsage` — already enabled in strict mode |
| 67 | + |
| 68 | +Once modules are renamed to `_module.py`, pyright will flag any import from them outside the `mcp` package. This gives free enforcement for test code and downstream users without additional configuration. |
| 69 | + |
| 70 | +### 4. `ruff` rules already in place |
| 71 | + |
| 72 | +- `F822` catches undefined names in `__all__` |
| 73 | +- `F401` catches unused imports (drift between import and `__all__`) |
| 74 | +- These already run in pre-commit; they'll catch `__init__.py` drift automatically |
| 75 | + |
| 76 | +### 5. Type stubs (`.pyi`) — Phase 3, long-term |
| 77 | + |
| 78 | +Generate `.pyi` stubs for the public surface and check them in. API changes become visible as stub file diffs in PRs. Tooling: `stubgen` (mypy) or griffe can generate them. |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +## The Public API Allowlist |
| 83 | + |
| 84 | +Everything below is explicitly public. Everything not listed becomes private. |
| 85 | + |
| 86 | +### Tier 1: Core Entry Points |
| 87 | + |
| 88 | +Import from `mcp` or `mcp.client` / `mcp.server`. |
| 89 | + |
| 90 | +| Symbol | Stable import path | Notes | |
| 91 | +|---|---|---| |
| 92 | +| `Client` | `mcp.Client` | Main client entry point (wraps in-memory server) | |
| 93 | +| `ClientSession` | `mcp.ClientSession` | Full-featured session over any transport | |
| 94 | +| `ClientSessionGroup` | `mcp.ClientSessionGroup` | Multi-server aggregation | |
| 95 | +| `MCPServer` | `mcp.server.MCPServer` | High-level decorator-based server | |
| 96 | +| `Server` | `mcp.server.Server` | Low-level handler-registration server | |
| 97 | +| `ServerSession` | `mcp.ServerSession` | Server-side session | |
| 98 | +| `Context` | `mcp.server.mcpserver.Context` | Request context passed to MCPServer handlers | |
| 99 | +| `MCPError` | `mcp.MCPError` | Base exception for MCP protocol errors | |
| 100 | +| `UrlElicitationRequiredError` | `mcp.UrlElicitationRequiredError` | Thrown when URL elicitation is required | |
| 101 | + |
| 102 | +### Tier 2: Transports & Connection Parameters |
| 103 | + |
| 104 | +Transport entry-point functions and their configuration types. Currently these leak via direct module import; they should be surfaced through `__init__.py`. |
| 105 | + |
| 106 | +| Symbol | Proposed stable path | Current location | |
| 107 | +|---|---|---| |
| 108 | +| `stdio_client` | `mcp.stdio_client` | `mcp.client.stdio` ✓ already re-exported | |
| 109 | +| `stdio_server` | `mcp.stdio_server` | `mcp.server.stdio` ✓ already re-exported | |
| 110 | +| `sse_client` | `mcp.client.sse_client` | `mcp.client.sse` — **not re-exported** | |
| 111 | +| `streamable_http_client` | `mcp.client.streamable_http_client` | `mcp.client.streamable_http` — **not re-exported** | |
| 112 | +| `websocket_client` | `mcp.client.websocket_client` | `mcp.client.websocket` — **not re-exported** | |
| 113 | +| `StdioServerParameters` | `mcp.StdioServerParameters` | ✓ already re-exported | |
| 114 | +| `SseServerParameters` | `mcp.client.SseServerParameters` | `mcp.client.session_group` — **not re-exported** | |
| 115 | +| `StreamableHttpParameters` | `mcp.client.StreamableHttpParameters` | `mcp.client.session_group` — **not re-exported** | |
| 116 | +| `ServerParameters` | `mcp.client.ServerParameters` | `mcp.client.session_group` — **not re-exported** | |
| 117 | +| `ClientSessionParameters` | `mcp.client.ClientSessionParameters` | `mcp.client.session_group` — **not re-exported** | |
| 118 | +| `SseServerTransport` | `mcp.server.SseServerTransport` | `mcp.server.sse` — **not re-exported** | |
| 119 | +| `EventStore` | `mcp.server.EventStore` | `mcp.server.streamable_http` — **not re-exported** | |
| 120 | +| `TransportSecuritySettings` | `mcp.server.TransportSecuritySettings` | `mcp.server.transport_security` — **not re-exported** | |
| 121 | + |
| 122 | +### Tier 3: MCPServer Resource / Tool / Prompt Types |
| 123 | + |
| 124 | +User-facing wrapper types for defining server capabilities. |
| 125 | + |
| 126 | +| Symbol | Stable import path | |
| 127 | +|---|---| |
| 128 | +| `Tool` | `mcp.server.mcpserver.tools.Tool` | |
| 129 | +| `Resource` | `mcp.server.mcpserver.resources.Resource` | |
| 130 | +| `TextResource` | `mcp.server.mcpserver.resources.TextResource` | |
| 131 | +| `BinaryResource` | `mcp.server.mcpserver.resources.BinaryResource` | |
| 132 | +| `FunctionResource` | `mcp.server.mcpserver.resources.FunctionResource` | |
| 133 | +| `FileResource` | `mcp.server.mcpserver.resources.FileResource` | |
| 134 | +| `HttpResource` | `mcp.server.mcpserver.resources.HttpResource` | |
| 135 | +| `DirectoryResource` | `mcp.server.mcpserver.resources.DirectoryResource` | |
| 136 | +| `ResourceTemplate` | `mcp.server.mcpserver.resources.ResourceTemplate` | |
| 137 | +| `Prompt` | `mcp.server.mcpserver.prompts.Prompt` | |
| 138 | +| `Image` | `mcp.server.mcpserver.Image` | |
| 139 | +| `Audio` | `mcp.server.mcpserver.Audio` | |
| 140 | +| `NotificationOptions` | `mcp.server.NotificationOptions` | |
| 141 | +| `InitializationOptions` | `mcp.server.InitializationOptions` | |
| 142 | + |
| 143 | +### Tier 4: Auth |
| 144 | + |
| 145 | +Client and server auth types. Currently all server auth types are importable only via deep paths with zero `__init__.py` re-export. |
| 146 | + |
| 147 | +| Symbol | Proposed stable path | Current location | |
| 148 | +|---|---|---| |
| 149 | +| `OAuthClientProvider` | `mcp.client.auth.OAuthClientProvider` | ✓ already re-exported | |
| 150 | +| `TokenStorage` | `mcp.client.auth.TokenStorage` | ✓ already re-exported | |
| 151 | +| `PKCEParameters` | `mcp.client.auth.PKCEParameters` | ✓ already re-exported | |
| 152 | +| `OAuthFlowError` | `mcp.client.auth.OAuthFlowError` | ✓ already re-exported | |
| 153 | +| `OAuthRegistrationError` | `mcp.client.auth.OAuthRegistrationError` | ✓ already re-exported | |
| 154 | +| `OAuthTokenError` | `mcp.client.auth.OAuthTokenError` | ✓ already re-exported | |
| 155 | +| `OAuthAuthorizationServerProvider` | `mcp.server.auth.OAuthAuthorizationServerProvider` | `mcp.server.auth.provider` — **not re-exported** | |
| 156 | +| `TokenVerifier` | `mcp.server.auth.TokenVerifier` | `mcp.server.auth.provider` — **not re-exported** | |
| 157 | +| `AuthSettings` | `mcp.server.auth.AuthSettings` | `mcp.server.auth.settings` — **not re-exported** | |
| 158 | + |
| 159 | +### Tier 5: Elicitation Result Types |
| 160 | + |
| 161 | +Returned from `Context.elicit()` and `Context.elicit_url()` in MCPServer handlers. |
| 162 | + |
| 163 | +| Symbol | Proposed stable path | Current location | |
| 164 | +|---|---|---| |
| 165 | +| `ElicitationResult` | `mcp.server.ElicitationResult` | `mcp.server.elicitation` — **not re-exported** | |
| 166 | +| `UrlElicitationResult` | `mcp.server.UrlElicitationResult` | `mcp.server.elicitation` — **not re-exported** | |
| 167 | +| `AcceptedElicitation` | `mcp.server.AcceptedElicitation` | `mcp.server.elicitation` — **not re-exported** | |
| 168 | +| `DeclinedElicitation` | `mcp.server.DeclinedElicitation` | `mcp.server.elicitation` — **not re-exported** | |
| 169 | +| `CancelledElicitation` | `mcp.server.CancelledElicitation` | `mcp.server.elicitation` — **not re-exported** | |
| 170 | + |
| 171 | +### Tier 6: Callback Protocol Types |
| 172 | + |
| 173 | +Users need these for type annotations when passing callbacks to `Client` or `ClientSession`. Currently defined in `client/session.py` with no re-export. |
| 174 | + |
| 175 | +| Symbol | Proposed stable path | |
| 176 | +|---|---| |
| 177 | +| `SamplingFnT` | `mcp.client.SamplingFnT` | |
| 178 | +| `ElicitationFnT` | `mcp.client.ElicitationFnT` | |
| 179 | +| `ListRootsFnT` | `mcp.client.ListRootsFnT` | |
| 180 | +| `LoggingFnT` | `mcp.client.LoggingFnT` | |
| 181 | +| `MessageHandlerFnT` | `mcp.client.MessageHandlerFnT` | |
| 182 | + |
| 183 | +### Tier 7: Protocol Types (`mcp.types`) |
| 184 | + |
| 185 | +**All ~200 types currently in `mcp.types.__all__` remain public.** These mirror the MCP spec and are inherently part of the public contract. The internal `mcp.types._types` is already correctly private. The `mcp.types.jsonrpc` module should be renamed to `_jsonrpc.py` (everything is already re-exported through `types/__init__.py`). |
| 186 | + |
| 187 | +### Tier 8: Experimental (Explicitly Unstable) |
| 188 | + |
| 189 | +Everything under `*.experimental.*` namespaces. Importable, but documented as "may change without notice" and not covered by semver stability. |
| 190 | + |
| 191 | +--- |
| 192 | + |
| 193 | +## What Gets Privatized |
| 194 | + |
| 195 | +### Implementation Modules to Rename |
| 196 | + |
| 197 | +Rename from `module.py` to `_module.py`. All public symbols are preserved via re-exports in the package `__init__.py`. |
| 198 | + |
| 199 | +**`src/mcp/client/`** |
| 200 | + |
| 201 | +| Current | Renamed | Re-export destination | |
| 202 | +|---|---|---| |
| 203 | +| `client.py` | `_client.py` | `client/__init__.py` | |
| 204 | +| `session.py` | `_session.py` | `client/__init__.py` | |
| 205 | +| `session_group.py` | `_session_group.py` | `client/__init__.py` | |
| 206 | +| `sse.py` | `_sse.py` | `client/__init__.py` | |
| 207 | +| `stdio.py` | `_stdio.py` | `client/__init__.py` + `mcp/__init__.py` | |
| 208 | +| `streamable_http.py` | `_streamable_http.py` | `client/__init__.py` | |
| 209 | +| `websocket.py` | `_websocket.py` | `client/__init__.py` | |
| 210 | + |
| 211 | +**`src/mcp/client/auth/`** |
| 212 | + |
| 213 | +| Current | Renamed | |
| 214 | +|---|---| |
| 215 | +| `exceptions.py` | `_exceptions.py` | |
| 216 | +| `oauth2.py` | `_oauth2.py` | |
| 217 | +| `utils.py` | `_utils.py` | |
| 218 | +| `extensions/client_credentials.py` | `extensions/_client_credentials.py` | |
| 219 | + |
| 220 | +**`src/mcp/server/`** |
| 221 | + |
| 222 | +| Current | Renamed | Re-export destination | |
| 223 | +|---|---|---| |
| 224 | +| `session.py` | `_session.py` | `mcp/__init__.py` | |
| 225 | +| `sse.py` | `_sse.py` | `server/__init__.py` | |
| 226 | +| `stdio.py` | `_stdio.py` | `mcp/__init__.py` | |
| 227 | +| `streamable_http.py` | `_streamable_http.py` | `server/__init__.py` | |
| 228 | +| `streamable_http_manager.py` | `_streamable_http_manager.py` | — (internal only) | |
| 229 | +| `transport_security.py` | `_transport_security.py` | `server/__init__.py` | |
| 230 | +| `validation.py` | `_validation.py` | — (internal only) | |
| 231 | +| `elicitation.py` | `_elicitation.py` | `server/__init__.py` | |
| 232 | +| `models.py` | `_models.py` | `server/__init__.py` | |
| 233 | +| `websocket.py` | `_websocket.py` | — (internal only, server side) | |
| 234 | + |
| 235 | +**`src/mcp/server/lowlevel/`** |
| 236 | + |
| 237 | +| Current | Renamed | |
| 238 | +|---|---| |
| 239 | +| `server.py` | `_server.py` | |
| 240 | +| `func_inspection.py` | `_func_inspection.py` | |
| 241 | +| `helper_types.py` | `_helper_types.py` | |
| 242 | +| `experimental.py` | `_experimental.py` | |
| 243 | + |
| 244 | +**`src/mcp/server/mcpserver/`** |
| 245 | + |
| 246 | +| Current | Renamed | |
| 247 | +|---|---| |
| 248 | +| `server.py` | `_server.py` | |
| 249 | +| `exceptions.py` | `_exceptions.py` | |
| 250 | +| `prompts/base.py` | `prompts/_base.py` | |
| 251 | +| `prompts/manager.py` | `prompts/_manager.py` | |
| 252 | +| `resources/base.py` | `resources/_base.py` | |
| 253 | +| `resources/resource_manager.py` | `resources/_resource_manager.py` | |
| 254 | +| `resources/templates.py` | `resources/_templates.py` | |
| 255 | +| `resources/types.py` | `resources/_types.py` | |
| 256 | +| `tools/base.py` | `tools/_base.py` | |
| 257 | +| `tools/tool_manager.py` | `tools/_tool_manager.py` | |
| 258 | +| `utilities/context_injection.py` | `utilities/_context_injection.py` | |
| 259 | +| `utilities/func_metadata.py` | `utilities/_func_metadata.py` | |
| 260 | +| `utilities/logging.py` | `utilities/_logging.py` | |
| 261 | +| `utilities/types.py` | `utilities/_types.py` | |
| 262 | + |
| 263 | +**`src/mcp/server/auth/` (entire subtree)** |
| 264 | + |
| 265 | +All handler, middleware, route, and settings files → prefix with `_`. Re-export public types through `auth/__init__.py`. |
| 266 | + |
| 267 | +**`src/mcp/shared/` (entire package is internal)** |
| 268 | + |
| 269 | +| Current | Renamed | |
| 270 | +|---|---| |
| 271 | +| `auth.py` | `_auth.py` | |
| 272 | +| `auth_utils.py` | `_auth_utils.py` | |
| 273 | +| `context.py` | `_context.py` | |
| 274 | +| `exceptions.py` | `_exceptions.py` | |
| 275 | +| `memory.py` | `_memory.py` | |
| 276 | +| `message.py` | `_message.py` | |
| 277 | +| `metadata_utils.py` | `_metadata_utils.py` | |
| 278 | +| `progress.py` | `_progress.py` | |
| 279 | +| `response_router.py` | `_response_router.py` | |
| 280 | +| `session.py` | `_session.py` | |
| 281 | +| `tool_name_validation.py` | `_tool_name_validation.py` | |
| 282 | +| `version.py` | `_version.py` | |
| 283 | + |
| 284 | +**`src/mcp/types/`** |
| 285 | + |
| 286 | +| Current | Renamed | |
| 287 | +|---|---| |
| 288 | +| `jsonrpc.py` | `_jsonrpc.py` | |
| 289 | + |
| 290 | +**`src/mcp/cli/`** |
| 291 | + |
| 292 | +| Current | Renamed | |
| 293 | +|---|---| |
| 294 | +| `cli.py` | `_cli.py` | |
| 295 | +| `claude.py` | `_claude.py` | |
| 296 | + |
| 297 | +**`src/mcp/os/`** |
| 298 | + |
| 299 | +| Current | Renamed | |
| 300 | +|---|---| |
| 301 | +| `posix/utilities.py` | `posix/_utilities.py` | |
| 302 | +| `win32/utilities.py` | `win32/_utilities.py` | |
| 303 | + |
| 304 | +### Internal Names to Prefix with `_` |
| 305 | + |
| 306 | +These are names inside modules that should not be part of any public surface: |
| 307 | + |
| 308 | +| Name | Module | Reason | |
| 309 | +|---|---|---| |
| 310 | +| `DEFAULT_CLIENT_INFO` | `client/session.py` | Implementation detail | |
| 311 | +| `ClientResponse` (TypeAdapter) | `client/session.py` | Internal deserialization | |
| 312 | +| `PROCESS_TERMINATION_TIMEOUT` | `client/stdio.py` | Hardcoded internal timeout | |
| 313 | +| `request_ctx` (ContextVar) | `server/lowlevel/server.py` | Internal context propagation | |
| 314 | +| `StructuredContent` | `server/lowlevel/server.py` | Internal type alias | |
| 315 | +| `UnstructuredContent` | `server/lowlevel/server.py` | Internal type alias | |
| 316 | +| `CombinationContent` | `server/lowlevel/server.py` | Internal type alias | |
| 317 | +| `Settings` | `server/mcpserver/server.py` | Internal config class | |
| 318 | +| `lifespan_wrapper` | `server/mcpserver/server.py` | Internal helper | |
| 319 | +| `remove_request_params` | `client/sse.py` | Internal URL helper | |
| 320 | +| All `MCP_SESSION_ID`, `MCP_PROTOCOL_VERSION`, `LAST_EVENT_ID` constants | transport modules | Internal protocol constants | |
| 321 | +| `SessionMessageOrError`, `StreamWriter`, `StreamReader`, `GetSessionIdCallback` | `client/streamable_http.py` | Internal type aliases | |
| 322 | +| `RequestContext` (dataclass) | `client/streamable_http.py` | Collides with `shared.context.RequestContext`; internal | |
| 323 | + |
| 324 | +--- |
| 325 | + |
| 326 | +## Needs-Decision Items |
| 327 | + |
| 328 | +These require an explicit team call. They are ambiguous and have arguments on both sides. |
| 329 | + |
| 330 | +| Item | Location | Question | |
| 331 | +|---|---|---| |
| 332 | +| `get_default_environment()` | `client/stdio.py` | Do users need to call this? Or just customize `StdioServerParameters.env`? | |
| 333 | +| `DEFAULT_INHERITED_ENV_VARS` | `client/stdio.py` | Same — users might want to reference the default list | |
| 334 | +| `ToolManager`, `ResourceManager`, `PromptManager` | `server/mcpserver/` sub-packages | Currently exported. Do users need these directly, or is `MCPServer.add_tool()` sufficient? | |
| 335 | +| `StreamableHTTPTransport` | `client/streamable_http.py` | Do users need the transport class directly for advanced customization? | |
| 336 | +| `StreamableHTTPServerTransport` | `server/streamable_http.py` | Same question, server side | |
| 337 | +| `MCPServerError` and subclasses | `server/mcpserver/exceptions.py` | Should users be able to `except MCPServerError`? Probably yes — add to Tier 1 | |
| 338 | +| `RequestResponder` | `shared/session.py` | Needed if `ServerSession.incoming_messages` is public. Is it? | |
| 339 | +| `BaseSession` | `shared/session.py` | Should advanced users subclass? Likely no | |
| 340 | + |
| 341 | +--- |
| 342 | + |
| 343 | +## Implementation Phases |
| 344 | + |
| 345 | +### Phase 0: Tooling & Allowlist (do first, blocks everything else) |
| 346 | + |
| 347 | +- Write `scripts/audit_public_api.py` using `griffe` to enumerate the live surface |
| 348 | +- Generate `docs/public_api_allowlist.txt` from the Tier tables above |
| 349 | +- Wire the audit script into CI (can add as a ruff-style pre-commit hook or a dedicated CI step) |
| 350 | +- Resolve the "Needs-Decision" items and update the allowlist |
| 351 | + |
| 352 | +### Phase 1: Module Renames (one large batch) |
| 353 | + |
| 354 | +- Rename all implementation modules as specified in the tables above |
| 355 | +- Update all internal imports across `src/mcp/` |
| 356 | +- Update all `__init__.py` re-exports to point to new `_`-prefixed paths |
| 357 | +- Update all test imports |
| 358 | +- Run the full test suite; fix any breakage |
| 359 | + |
| 360 | +### Phase 2: `__init__.py` Surface Consolidation |
| 361 | + |
| 362 | +- Add missing re-exports (transport functions, auth types, elicitation types, callback protocols) to the appropriate `__init__.py` files |
| 363 | +- Ensure every `__init__.py` has `__all__` |
| 364 | +- Verify stable import paths match the allowlist tables |
| 365 | + |
| 366 | +### Phase 3: Internal Name Cleanup |
| 367 | + |
| 368 | +- Prefix internal constants, type aliases, and helpers with `_` as specified in the "Internal Names" table |
| 369 | +- Resolve the `RequestContext` name collision |
| 370 | +- Run pyright; verify no new `reportPrivateUsage` warnings from tests |
| 371 | + |
| 372 | +### Phase 4: Type Stubs (optional, long-term) |
| 373 | + |
| 374 | +- Generate `.pyi` stubs covering only the public surface |
| 375 | +- Check them into the repo |
| 376 | +- Review stub diffs in PRs as an API review signal |
0 commit comments