Skip to content

Commit d7a5c96

Browse files
refactor(specTypeSchema): switch isSpecType/specTypeSchema to string-arg function form
Replaces the Record-of-predicates exports with two generic functions: isSpecType('TypeName', value) and specTypeSchema('TypeName'). Drops the register() helper, the per-key closure creation (161 closures at module init), the SchemaRecord/GuardRecord types, and both Object.freeze(...) as unknown as casts. The single remaining cast is a narrow Record<SpecTypeName, z.ZodType> at construction so indexing by SpecTypeName is non-undefined under noUncheckedIndexedAccess. Trade-off: arr.filter needs an arrow wrapper (mixed.filter(v => isSpecType('ContentBlock', v))). TypeScript's inferred type predicates still narrow the result to ContentBlock[].
1 parent dbd3555 commit d7a5c96

7 files changed

Lines changed: 125 additions & 121 deletions

File tree

.changeset/spec-type-schema.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
'@modelcontextprotocol/server': minor
44
---
55

6-
Export `isSpecType` and `specTypeSchemas` records for runtime validation of any MCP spec type by name. `isSpecType.ContentBlock(value)` is a type predicate; `specTypeSchemas.ContentBlock` is a `StandardSchemaV1<ContentBlock>` validator. Guards are standalone functions, so `arr.filter(isSpecType.ContentBlock)` works. Also export the `StandardSchemaV1`, `SpecTypeName`, and `SpecTypes` types.
6+
Export `isSpecType` and `specTypeSchema` for runtime validation of any MCP spec type by name. `isSpecType('ContentBlock', value)` is a type predicate; `specTypeSchema('ContentBlock')` returns a `StandardSchemaV1<ContentBlock>` validator. Also export the `StandardSchemaV1`,
7+
`SpecTypeName`, and `SpecTypes` types.

docs/migration-SKILL.md

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,21 @@ Replace all `@modelcontextprotocol/sdk/...` imports using this table.
5151
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
5252
| `@modelcontextprotocol/sdk/server/mcp.js` | `@modelcontextprotocol/server` |
5353
| `@modelcontextprotocol/sdk/server/index.js` | `@modelcontextprotocol/server` |
54-
| `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server/stdio` |
54+
| `@modelcontextprotocol/sdk/server/stdio.js` | `@modelcontextprotocol/server/stdio` |
5555
| `@modelcontextprotocol/sdk/server/streamableHttp.js` | `@modelcontextprotocol/node` (class renamed to `NodeStreamableHTTPServerTransport`) OR `@modelcontextprotocol/server` (web-standard `WebStandardStreamableHTTPServerTransport` for Cloudflare Workers, Deno, etc.) |
5656
| `@modelcontextprotocol/sdk/server/sse.js` | REMOVED (migrate to Streamable HTTP) |
5757
| `@modelcontextprotocol/sdk/server/auth/*` | RS helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `OAuthTokenVerifier`) → `@modelcontextprotocol/express`; AS helpers removed (use external IdP/OAuth library) |
5858
| `@modelcontextprotocol/sdk/server/middleware.js` | `@modelcontextprotocol/express` (signature changed, see section 8) |
5959

6060
### Types / shared imports
6161

62-
| v1 import path | v2 package |
63-
| ------------------------------------------------- | ---------------------------------------------------------------- |
64-
| `@modelcontextprotocol/sdk/types.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
65-
| `@modelcontextprotocol/sdk/shared/protocol.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
66-
| `@modelcontextprotocol/sdk/shared/transport.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
67-
| `@modelcontextprotocol/sdk/shared/uriTemplate.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
68-
| `@modelcontextprotocol/sdk/shared/auth.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
62+
| v1 import path | v2 package |
63+
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
64+
| `@modelcontextprotocol/sdk/types.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
65+
| `@modelcontextprotocol/sdk/shared/protocol.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
66+
| `@modelcontextprotocol/sdk/shared/transport.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
67+
| `@modelcontextprotocol/sdk/shared/uriTemplate.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
68+
| `@modelcontextprotocol/sdk/shared/auth.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` |
6969
| `@modelcontextprotocol/sdk/shared/stdio.js` | `@modelcontextprotocol/client` or `@modelcontextprotocol/server` (`ReadBuffer`, `serializeMessage`, `deserializeMessage` are in the root barrel; the `./stdio` subpath only has the transport class) |
7070

7171
Notes:
@@ -99,7 +99,7 @@ Notes:
9999
| `WebSocketClientTransport` | REMOVED (use `StreamableHTTPClientTransport` or `StdioClientTransport`) |
100100

101101
All other **type** symbols from `@modelcontextprotocol/sdk/types.js` retain their original names. **Zod schemas** (e.g., `CallToolResultSchema`, `ListToolsResultSchema`) are no longer part of the public API — they are internal to the SDK. For runtime validation, use
102-
`isSpecType.TypeName(value)` (e.g., `isSpecType.CallToolResult(v)`) or `specTypeSchemas.TypeName` for the `StandardSchemaV1` validator object. The keys are typed as `SpecTypeName`, a literal union of all spec type names.
102+
`isSpecType('TypeName', value)` (e.g., `isSpecType('CallToolResult', v)`) or `specTypeSchema('TypeName')` for the `StandardSchemaV1` validator object. The first argument is typed as `SpecTypeName`, a literal union of all spec type names.
103103

104104
### Error class changes
105105

@@ -323,7 +323,8 @@ new URL(ctx.http?.req?.url).searchParams.get('debug')
323323

324324
### Server-side auth
325325

326-
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`, `ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) are removed from the core SDK; use an external IdP/OAuth library. See `examples/server/src/` for demos.
326+
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`. Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`,
327+
`ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) are removed from the core SDK; use an external IdP/OAuth library. See `examples/server/src/` for demos.
327328

328329
### Host header validation (Express)
329330

@@ -462,14 +463,14 @@ For **custom (non-spec)** methods, keep the result-schema argument — see §9.
462463

463464
Remove unused schema imports: `CallToolResultSchema`, `CompatibilityCallToolResultSchema`, `ElicitResultSchema`, `CreateMessageResultSchema`, etc., when they were only used in `request()`/`send()`/`callTool()` calls.
464465

465-
If a `*Schema` constant was used for **runtime validation** (not just as a `request()` argument), replace with `isSpecType` / `specTypeSchemas`:
466+
If a `*Schema` constant was used for **runtime validation** (not just as a `request()` argument), replace with `isSpecType` / `specTypeSchema`:
466467

467-
| v1 pattern | v2 replacement |
468-
| -------------------------------------------------- | -------------------------------------------------------------------------------------- |
469-
| `CallToolResultSchema.safeParse(value).success` | `isSpecType.CallToolResult(value)` |
470-
| `<TypeName>Schema.safeParse(value).success` | `isSpecType.<TypeName>(value)` |
471-
| `<TypeName>Schema.parse(value)` | `await specTypeSchemas.<TypeName>['~standard'].validate(value)` (returns a `Result`, not the value) |
472-
| Passing `<TypeName>Schema` as a validator argument | `specTypeSchemas.<TypeName>` (a `StandardSchemaV1<In, Out>`) |
468+
| v1 pattern | v2 replacement |
469+
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
470+
| `CallToolResultSchema.safeParse(value).success` | `isSpecType('CallToolResult', value)` |
471+
| `<TypeName>Schema.safeParse(value).success` | `isSpecType('<TypeName>', value)` |
472+
| `<TypeName>Schema.parse(value)` | `await specTypeSchema('<TypeName>')['~standard'].validate(value)` (returns a `Result`, not the value) |
473+
| Passing `<TypeName>Schema` as a validator argument | `specTypeSchema('<TypeName>')` (a `StandardSchemaV1<In, Out>`) |
473474

474475
`isCallToolResult(value)` still works, but `isSpecType` covers every spec type by name.
475476

docs/migration.md

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ const transport = new StreamableHTTPClientTransport(new URL('http://localhost:30
137137

138138
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are now first-class in `@modelcontextprotocol/express`.
139139

140-
Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`, `ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) have been removed from the core SDK; new code should use a dedicated IdP/OAuth library. See the [examples](../examples/server/src/) for a working demo with `better-auth`.
140+
Authorization Server helpers (`mcpAuthRouter`, `OAuthServerProvider`, `ProxyOAuthServerProvider`, `authenticateClient`, `allowedMethods`, etc.) have been removed from the core SDK; new code should use a dedicated IdP/OAuth library. See the [examples](../examples/server/src/) for
141+
a working demo with `better-auth`.
141142

142143
Note: `AuthInfo` has moved from `server/auth/types.ts` to the core types and is now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`.
143144

@@ -379,7 +380,11 @@ const AcmeSearch = z.object({
379380
params: z.object({ query: z.string(), limit: z.number().int() })
380381
});
381382
server.setRequestHandler(AcmeSearch, async request => {
382-
return { items: [/* ... */] };
383+
return {
384+
items: [
385+
/* ... */
386+
]
387+
};
383388
});
384389
```
385390

@@ -390,7 +395,11 @@ const SearchParams = z.object({ query: z.string(), limit: z.number().int() });
390395
const SearchResult = z.object({ items: z.array(z.string()) });
391396

392397
server.setRequestHandler('acme/search', { params: SearchParams, result: SearchResult }, async (params, ctx) => {
393-
return { items: [/* ... */] };
398+
return {
399+
items: [
400+
/* ... */
401+
]
402+
};
394403
});
395404
```
396405

@@ -429,8 +438,8 @@ Common method string replacements:
429438

430439
### `Protocol.request()`, `ctx.mcpReq.send()`, and `Client.callTool()` no longer require a schema parameter for spec methods
431440

432-
For **spec** methods, the public `Protocol.request()`, `BaseContext.mcpReq.send()`, and `Client.callTool()` methods no longer require a Zod result schema argument. The SDK now resolves the correct result schema internally based on the method name. This means you no longer need to import result schemas
433-
like `CallToolResultSchema` or `ElicitResultSchema` when making spec-method requests.
441+
For **spec** methods, the public `Protocol.request()`, `BaseContext.mcpReq.send()`, and `Client.callTool()` methods no longer require a Zod result schema argument. The SDK now resolves the correct result schema internally based on the method name. This means you no longer need to
442+
import result schemas like `CallToolResultSchema` or `ElicitResultSchema` when making spec-method requests.
434443

435444
**`client.request()` — Before (v1):**
436445

@@ -489,7 +498,7 @@ The return type is now inferred from the method name via `ResultTypeMap`. For ex
489498

490499
For **custom (non-spec)** methods, keep the result-schema argument — see [Sending custom-method requests](#sending-custom-method-requests). Only drop the schema when calling a spec method.
491500

492-
If you were using `CallToolResultSchema` (or any `*Schema` constant) for **runtime validation** (not just in `request()`/`callTool()` calls), use `isSpecType` or `specTypeSchemas`:
501+
If you were using `CallToolResultSchema` (or any `*Schema` constant) for **runtime validation** (not just in `request()`/`callTool()` calls), use `isSpecType` or `specTypeSchema`:
493502

494503
```typescript
495504
// v1: runtime validation with Zod schema
@@ -498,19 +507,20 @@ if (CallToolResultSchema.safeParse(value).success) {
498507
/* ... */
499508
}
500509

501-
// v2: keyed type predicate
510+
// v2: type predicate by name
502511
import { isSpecType } from '@modelcontextprotocol/client';
503-
if (isSpecType.CallToolResult(value)) {
512+
if (isSpecType('CallToolResult', value)) {
504513
/* ... */
505514
}
506-
const blocks = mixed.filter(isSpecType.ContentBlock);
515+
const blocks = mixed.filter(v => isSpecType('ContentBlock', v));
507516

508517
// v2: or get the StandardSchemaV1 validator object directly
509-
import { specTypeSchemas } from '@modelcontextprotocol/client';
510-
const result = await specTypeSchemas.CallToolResult['~standard'].validate(value);
518+
import { specTypeSchema } from '@modelcontextprotocol/client';
519+
const result = await specTypeSchema('CallToolResult')['~standard'].validate(value);
511520
```
512521

513-
`isSpecType` and `specTypeSchemas` are keyed by `SpecTypeName` — a literal union of every named type in the MCP spec — so you get autocomplete and a compile error on typos. `specTypeSchemas.X` is a `StandardSchemaV1<In, Out>`, which composes with any Standard-Schema-aware library. The pre-existing `isCallToolResult(value)` guard still works.
522+
The first argument to `isSpecType` and `specTypeSchema` is a `SpecTypeName` — a literal union of every named type in the MCP spec — so you get autocomplete and a compile error on typos. `specTypeSchema(name)` returns a `StandardSchemaV1<In, Out>`, which composes with any
523+
Standard-Schema-aware library. The pre-existing `isCallToolResult(value)` guard still works.
514524

515525
### Client list methods return empty results for missing capabilities
516526

@@ -706,22 +716,22 @@ try {
706716

707717
The new `SdkErrorCode` enum contains string-valued codes for local SDK errors:
708718

709-
| Code | Description |
710-
| ------------------------------------------------- | ------------------------------------------- |
711-
| `SdkErrorCode.NotConnected` | Transport is not connected |
712-
| `SdkErrorCode.AlreadyConnected` | Transport is already connected |
713-
| `SdkErrorCode.NotInitialized` | Protocol is not initialized |
714-
| `SdkErrorCode.CapabilityNotSupported` | Required capability is not supported |
715-
| `SdkErrorCode.RequestTimeout` | Request timed out waiting for response |
716-
| `SdkErrorCode.ConnectionClosed` | Connection was closed |
717-
| `SdkErrorCode.SendFailed` | Failed to send message |
719+
| Code | Description |
720+
| ------------------------------------------------- | ---------------------------------------------- |
721+
| `SdkErrorCode.NotConnected` | Transport is not connected |
722+
| `SdkErrorCode.AlreadyConnected` | Transport is already connected |
723+
| `SdkErrorCode.NotInitialized` | Protocol is not initialized |
724+
| `SdkErrorCode.CapabilityNotSupported` | Required capability is not supported |
725+
| `SdkErrorCode.RequestTimeout` | Request timed out waiting for response |
726+
| `SdkErrorCode.ConnectionClosed` | Connection was closed |
727+
| `SdkErrorCode.SendFailed` | Failed to send message |
718728
| `SdkErrorCode.InvalidResult` | Response result failed local schema validation |
719-
| `SdkErrorCode.ClientHttpNotImplemented` | HTTP POST request failed |
720-
| `SdkErrorCode.ClientHttpAuthentication` | Server returned 401 after re-authentication |
721-
| `SdkErrorCode.ClientHttpForbidden` | Server returned 403 after trying upscoping |
722-
| `SdkErrorCode.ClientHttpUnexpectedContent` | Unexpected content type in HTTP response |
723-
| `SdkErrorCode.ClientHttpFailedToOpenStream` | Failed to open SSE stream |
724-
| `SdkErrorCode.ClientHttpFailedToTerminateSession` | Failed to terminate session |
729+
| `SdkErrorCode.ClientHttpNotImplemented` | HTTP POST request failed |
730+
| `SdkErrorCode.ClientHttpAuthentication` | Server returned 401 after re-authentication |
731+
| `SdkErrorCode.ClientHttpForbidden` | Server returned 403 after trying upscoping |
732+
| `SdkErrorCode.ClientHttpUnexpectedContent` | Unexpected content type in HTTP response |
733+
| `SdkErrorCode.ClientHttpFailedToOpenStream` | Failed to open SSE stream |
734+
| `SdkErrorCode.ClientHttpFailedToTerminateSession` | Failed to terminate session |
725735

726736
#### `StreamableHTTPError` removed
727737

packages/core/src/exports/public/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export { InMemoryTaskMessageQueue, InMemoryTaskStore } from '../../experimental/
139139

140140
// Validator types and classes
141141
export type { SpecTypeName, SpecTypes } from '../../types/specTypeSchema.js';
142-
export { isSpecType, specTypeSchemas } from '../../types/specTypeSchema.js';
142+
export { isSpecType, specTypeSchema } from '../../types/specTypeSchema.js';
143143
export type { StandardSchemaV1, StandardSchemaWithJSON } from '../../util/standardSchema.js';
144144
export { AjvJsonSchemaValidator } from '../../validators/ajvProvider.js';
145145
export type { CfWorkerSchemaDraft } from '../../validators/cfWorkerProvider.js';

0 commit comments

Comments
 (0)