Context
Flagged during human review of the SDK v2 migration (#1688): core/mcp/serverList.ts had several pre-existing as unknown as casts that widen a typed config object to Record<string, unknown> (and back) to do key-level manipulation.
They are safe — StoredMCPServer / MCPServerConfig have no index signature, so a single as Record<string, unknown> is a TS2352 error and the unknown step is required — but they read as a code smell. This issue originally tracked removing them in serverList.ts.
Scope (expanded)
While in the area we swept the whole codebase for as unknown as double casts (outside test files) and applied the review bar from AGENTS.md: no unjustified as unknown as. Each site is either removed or carries a comment explaining why it's unavoidable.
Removed (widened via a shared helper, or narrowed to a single cast)
serverList.ts — the original four:
stripInspectorFields: rewritten as clone + delete over INSPECTOR_FIELD_KEYS (all extension keys are optional on StoredMCPServer, so the result is still a subtype of MCPServerConfig — no cast).
- oauth-strip in
extractSecretsFromStored: oauth is optional → plain delete stripped.oauth.
mcpConfigToServerEntries: routed through a documented toRecord(value: object) widening helper.
- Shared helper
toRecord(value: object): Record<string, unknown> promoted to core/json/jsonUtils.ts and reused at:
core/mcp/node/config.ts (the normalizeServerType loop)
core/mcp/remote/node/server.ts (pino log-level dispatch)
clients/web/src/App.tsx (the window global token lookup)
inspectorClient.rawWireRequest — type the frame as JSONRPCRequest and narrow only params with a single structural cast (the SDK's _meta-typed request params is what blocked direct assignment).
toolOutputValidation.ts — SDK outputSchema → JsonSchemaType collapses to a single cast.
test-server-fixtures.ts (×2) — the optional task handle is reachable with a single cast (the field is optional).
Justified in place (genuinely unavoidable)
Private SDK internals and structural bridges that can't be removed without SDK changes, now each carrying a comment: the inspectorClient private-map accesses (_requestHandlers / _responseHandlers / _progressHandlers) and task-result gaps, resourceContext + tokenAuthProvider partial OAuthClientProvider stubs, the ext-apps v1/v2 peer bridge in createAppBridgeFactory, the modern/legacy _requestHandlers bypasses in the test servers, and the happy-dom matchMedia test mock.
Notes
Behavior-neutral throughout — good existing coverage on serverList.ts (79 tests) plus the full npm run ci gate (validate, ≥90 per-file coverage, smoke, Storybook). Net goal met: zero unjustified as unknown as in non-test source.
Ref: review comments 3589589773 / 3589591281 on #1688.
Context
Flagged during human review of the SDK v2 migration (#1688):
core/mcp/serverList.tshad several pre-existingas unknown ascasts that widen a typed config object toRecord<string, unknown>(and back) to do key-level manipulation.They are safe —
StoredMCPServer/MCPServerConfighave no index signature, so a singleas Record<string, unknown>is a TS2352 error and theunknownstep is required — but they read as a code smell. This issue originally tracked removing them inserverList.ts.Scope (expanded)
While in the area we swept the whole codebase for
as unknown asdouble casts (outside test files) and applied the review bar fromAGENTS.md: no unjustifiedas unknown as. Each site is either removed or carries a comment explaining why it's unavoidable.Removed (widened via a shared helper, or narrowed to a single cast)
serverList.ts— the original four:stripInspectorFields: rewritten as clone +deleteoverINSPECTOR_FIELD_KEYS(all extension keys are optional onStoredMCPServer, so the result is still a subtype ofMCPServerConfig— no cast).extractSecretsFromStored:oauthis optional → plaindelete stripped.oauth.mcpConfigToServerEntries: routed through a documentedtoRecord(value: object)widening helper.toRecord(value: object): Record<string, unknown>promoted tocore/json/jsonUtils.tsand reused at:core/mcp/node/config.ts(thenormalizeServerTypeloop)core/mcp/remote/node/server.ts(pino log-level dispatch)clients/web/src/App.tsx(thewindowglobal token lookup)inspectorClient.rawWireRequest— type the frame asJSONRPCRequestand narrow onlyparamswith a single structural cast (the SDK's_meta-typed request params is what blocked direct assignment).toolOutputValidation.ts— SDKoutputSchema→JsonSchemaTypecollapses to a single cast.test-server-fixtures.ts(×2) — the optionaltaskhandle is reachable with a single cast (the field is optional).Justified in place (genuinely unavoidable)
Private SDK internals and structural bridges that can't be removed without SDK changes, now each carrying a comment: the
inspectorClientprivate-map accesses (_requestHandlers/_responseHandlers/_progressHandlers) and task-result gaps,resourceContext+tokenAuthProviderpartialOAuthClientProviderstubs, the ext-apps v1/v2 peer bridge increateAppBridgeFactory, the modern/legacy_requestHandlersbypasses in the test servers, and the happy-dommatchMediatest mock.Notes
Behavior-neutral throughout — good existing coverage on
serverList.ts(79 tests) plus the fullnpm run cigate (validate, ≥90 per-file coverage, smoke, Storybook). Net goal met: zero unjustifiedas unknown asin non-test source.Ref: review comments 3589589773 / 3589591281 on #1688.