[SEP-2663] refactor!: remove 2025-11 experimental tasks (#2128)
This commit is contained in:
parent
600ba75f34
commit
c8d7401b46
1 changed files with 423 additions and 18280 deletions
|
|
@ -145,7 +145,8 @@ const transport = new StreamableHTTPClientTransport(new URL('http://localhost:30
|
|||
|
||||
Resource Server helpers (`requireBearerAuth`, `mcpAuthMetadataRouter`, `getOAuthProtectedResourceMetadataUrl`, `OAuthTokenVerifier`) are first-class in `@modelcontextprotocol/express`.
|
||||
|
||||
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`.
|
||||
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`.
|
||||
|
||||
Note: `AuthInfo` has moved from `server/auth/types.ts` to the core types and is now re-exported by `@modelcontextprotocol/client` and `@modelcontextprotocol/server`.
|
||||
|
||||
|
|
@ -387,7 +388,11 @@ const AcmeSearch = z.object({
|
|||
params: z.object({ query: z.string(), limit: z.number().int() })
|
||||
});
|
||||
server.setRequestHandler(AcmeSearch, async request => {
|
||||
return { items: [/* ... */] };
|
||||
return {
|
||||
items: [
|
||||
/* ... */
|
||||
]
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -398,7 +403,11 @@ const SearchParams = z.object({ query: z.string(), limit: z.number().int() });
|
|||
const SearchResult = z.object({ items: z.array(z.string()) });
|
||||
|
||||
server.setRequestHandler('acme/search', { params: SearchParams, result: SearchResult }, async (params, ctx) => {
|
||||
return { items: [/* ... */] };
|
||||
return {
|
||||
items: [
|
||||
/* ... */
|
||||
]
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -437,8 +446,8 @@ Common method string replacements:
|
|||
|
||||
### `Protocol.request()`, `ctx.mcpReq.send()`, and `Client.callTool()` no longer require a schema parameter for spec methods
|
||||
|
||||
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
|
||||
like `CallToolResultSchema` or `ElicitResultSchema` when making spec-method requests.
|
||||
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 like `CallToolResultSchema` or `ElicitResultSchema` when making spec-method requests.
|
||||
|
||||
**`client.request()` — Before (v1):**
|
||||
|
||||
|
|
@ -493,7 +502,7 @@ const result = await client.callTool({ name: 'my-tool', arguments: {} }, Compati
|
|||
const result = await client.callTool({ name: 'my-tool', arguments: {} });
|
||||
```
|
||||
|
||||
The return type is now inferred from the method name via `ResultTypeMap`. For example, `client.request({ method: 'tools/call', ... })` returns `Promise<CallToolResult | CreateTaskResult>`.
|
||||
The return type is now inferred from the method name via `ResultTypeMap`. For example, `client.request({ method: 'tools/call', ... })` returns `Promise<CallToolResult>`.
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -518,7 +527,8 @@ import { specTypeSchemas } from '@modelcontextprotocol/client';
|
|||
const result = specTypeSchemas.CallToolResult['~standard'].validate(value);
|
||||
```
|
||||
|
||||
`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 `StandardSchemaV1Sync<In, Out>` — `validate()` returns the result synchronously, so you can access `.issues` / `.value` without `await`. It composes with any Standard-Schema-aware library. The pre-existing `isCallToolResult(value)` guard still works.
|
||||
`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 `StandardSchemaV1Sync<In, Out>` — `validate()` returns the result synchronously,
|
||||
so you can access `.issues` / `.value` without `await`. It composes with any Standard-Schema-aware library. The pre-existing `isCallToolResult(value)` guard still works.
|
||||
|
||||
### Client list methods return empty results for missing capabilities
|
||||
|
||||
|
|
@ -585,23 +595,21 @@ import { JSONRPCErrorResponse, ResourceTemplateReference, isJSONRPCErrorResponse
|
|||
|
||||
The `RequestHandlerExtra` type has been replaced with a structured context type hierarchy using nested groups:
|
||||
|
||||
| v1 | v2 |
|
||||
| ---------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `RequestHandlerExtra` (flat, all fields) | `ServerContext` (server handlers) or `ClientContext` (client handlers) |
|
||||
| `extra` parameter name | `ctx` parameter name |
|
||||
| `extra.signal` | `ctx.mcpReq.signal` |
|
||||
| `extra.requestId` | `ctx.mcpReq.id` |
|
||||
| `extra._meta` | `ctx.mcpReq._meta` |
|
||||
| `extra.sendRequest(...)` | `ctx.mcpReq.send(...)` |
|
||||
| `extra.sendNotification(...)` | `ctx.mcpReq.notify(...)` |
|
||||
| `extra.authInfo` | `ctx.http?.authInfo` |
|
||||
| `extra.requestInfo` | `ctx.http?.req` (standard Web `Request`, only on `ServerContext`) |
|
||||
| `extra.closeSSEStream` | `ctx.http?.closeSSE` (only on `ServerContext`) |
|
||||
| `extra.closeStandaloneSSEStream` | `ctx.http?.closeStandaloneSSE` (only on `ServerContext`) |
|
||||
| `extra.sessionId` | `ctx.sessionId` |
|
||||
| `extra.taskStore` | `ctx.task?.store` |
|
||||
| `extra.taskId` | `ctx.task?.id` |
|
||||
| `extra.taskRequestedTtl` | `ctx.task?.requestedTtl` |
|
||||
| v1 | v2 |
|
||||
| ------------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `RequestHandlerExtra` (flat, all fields) | `ServerContext` (server handlers) or `ClientContext` (client handlers) |
|
||||
| `extra` parameter name | `ctx` parameter name |
|
||||
| `extra.signal` | `ctx.mcpReq.signal` |
|
||||
| `extra.requestId` | `ctx.mcpReq.id` |
|
||||
| `extra._meta` | `ctx.mcpReq._meta` |
|
||||
| `extra.sendRequest(...)` | `ctx.mcpReq.send(...)` |
|
||||
| `extra.sendNotification(...)` | `ctx.mcpReq.notify(...)` |
|
||||
| `extra.authInfo` | `ctx.http?.authInfo` |
|
||||
| `extra.requestInfo` | `ctx.http?.req` (standard Web `Request`, only on `ServerContext`) |
|
||||
| `extra.closeSSEStream` | `ctx.http?.closeSSE` (only on `ServerContext`) |
|
||||
| `extra.closeStandaloneSSEStream` | `ctx.http?.closeStandaloneSSE` (only on `ServerContext`) |
|
||||
| `extra.sessionId` | `ctx.sessionId` |
|
||||
| `extra.taskStore` / `taskId` / `taskRequestedTtl` | _removed — see "Experimental tasks interception removed" below_ |
|
||||
|
||||
**Before (v1):**
|
||||
|
||||
|
|
@ -619,17 +627,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|||
```typescript
|
||||
server.setRequestHandler('tools/call', async (request, ctx) => {
|
||||
const headers = ctx.http?.req?.headers; // standard Web Request object
|
||||
const taskStore = ctx.task?.store;
|
||||
await ctx.mcpReq.notify({ method: 'notifications/progress', params: { progressToken: 'abc', progress: 50, total: 100 } });
|
||||
return { content: [{ type: 'text', text: 'result' }] };
|
||||
});
|
||||
```
|
||||
|
||||
Context fields are organized into 4 groups:
|
||||
Context fields are organized into 3 groups:
|
||||
|
||||
- **`mcpReq`** — request-level concerns: `id`, `method`, `_meta`, `signal`, `send()`, `notify()`, plus server-only `log()`, `elicitInput()`, and `requestSampling()`
|
||||
- **`http?`** — HTTP transport concerns (undefined for stdio): `authInfo`, plus server-only `req`, `closeSSE`, `closeStandaloneSSE`
|
||||
- **`task?`** — task lifecycle: `id`, `store`, `requestedTtl`
|
||||
- **`sessionId?`** — transport session identifier (top-level)
|
||||
|
||||
`BaseContext` is the common base type shared by both `ServerContext` and `ClientContext`. `ServerContext` extends each group with server-specific additions via type intersection.
|
||||
|
||||
|
|
@ -715,22 +722,22 @@ try {
|
|||
|
||||
The new `SdkErrorCode` enum contains string-valued codes for local SDK errors:
|
||||
|
||||
| Code | Description |
|
||||
| ------------------------------------------------- | ------------------------------------------- |
|
||||
| `SdkErrorCode.NotConnected` | Transport is not connected |
|
||||
| `SdkErrorCode.AlreadyConnected` | Transport is already connected |
|
||||
| `SdkErrorCode.NotInitialized` | Protocol is not initialized |
|
||||
| `SdkErrorCode.CapabilityNotSupported` | Required capability is not supported |
|
||||
| `SdkErrorCode.RequestTimeout` | Request timed out waiting for response |
|
||||
| `SdkErrorCode.ConnectionClosed` | Connection was closed |
|
||||
| `SdkErrorCode.SendFailed` | Failed to send message |
|
||||
| Code | Description |
|
||||
| ------------------------------------------------- | ---------------------------------------------- |
|
||||
| `SdkErrorCode.NotConnected` | Transport is not connected |
|
||||
| `SdkErrorCode.AlreadyConnected` | Transport is already connected |
|
||||
| `SdkErrorCode.NotInitialized` | Protocol is not initialized |
|
||||
| `SdkErrorCode.CapabilityNotSupported` | Required capability is not supported |
|
||||
| `SdkErrorCode.RequestTimeout` | Request timed out waiting for response |
|
||||
| `SdkErrorCode.ConnectionClosed` | Connection was closed |
|
||||
| `SdkErrorCode.SendFailed` | Failed to send message |
|
||||
| `SdkErrorCode.InvalidResult` | Response result failed local schema validation |
|
||||
| `SdkErrorCode.ClientHttpNotImplemented` | HTTP POST request failed |
|
||||
| `SdkErrorCode.ClientHttpAuthentication` | Server returned 401 after re-authentication |
|
||||
| `SdkErrorCode.ClientHttpForbidden` | Server returned 403 after trying upscoping |
|
||||
| `SdkErrorCode.ClientHttpUnexpectedContent` | Unexpected content type in HTTP response |
|
||||
| `SdkErrorCode.ClientHttpFailedToOpenStream` | Failed to open SSE stream |
|
||||
| `SdkErrorCode.ClientHttpFailedToTerminateSession` | Failed to terminate session |
|
||||
| `SdkErrorCode.ClientHttpNotImplemented` | HTTP POST request failed |
|
||||
| `SdkErrorCode.ClientHttpAuthentication` | Server returned 401 after re-authentication |
|
||||
| `SdkErrorCode.ClientHttpForbidden` | Server returned 403 after trying upscoping |
|
||||
| `SdkErrorCode.ClientHttpUnexpectedContent` | Unexpected content type in HTTP response |
|
||||
| `SdkErrorCode.ClientHttpFailedToOpenStream` | Failed to open SSE stream |
|
||||
| `SdkErrorCode.ClientHttpFailedToTerminateSession` | Failed to terminate session |
|
||||
|
||||
#### `StreamableHTTPError` removed
|
||||
|
||||
|
|
@ -759,7 +766,7 @@ try {
|
|||
await transport.send(message);
|
||||
} catch (error) {
|
||||
if (error instanceof SdkHttpError) {
|
||||
console.log('HTTP status:', error.status); // number — no cast needed
|
||||
console.log('HTTP status:', error.status); // number — no cast needed
|
||||
console.log('Status text:', error.statusText); // string | undefined
|
||||
switch (error.code) {
|
||||
case SdkErrorCode.ClientHttpAuthentication:
|
||||
|
|
@ -870,46 +877,24 @@ try {
|
|||
}
|
||||
```
|
||||
|
||||
### Experimental: `TaskCreationParams.ttl` no longer accepts `null`
|
||||
### Experimental tasks interception removed
|
||||
|
||||
The `ttl` field in `TaskCreationParams` (used when requesting the server to create a task) no longer accepts `null`. Per the MCP spec, `null` TTL (meaning unlimited lifetime) is only valid in server responses (`Task.ttl`), not in client requests. Clients should omit `ttl` to let
|
||||
the server decide the lifetime.
|
||||
The 2025-11 experimental tasks side-channel woven through `Protocol` has been removed in preparation for the SEP-2663 Tasks Extension. The following are gone with no in-place replacement:
|
||||
|
||||
This also narrows the type of `requestedTtl` in `TaskContext`, `CreateTaskServerContext`, and `TaskServerContext` from `number | null | undefined` to `number | undefined`.
|
||||
- `ProtocolOptions.tasks` (the `{ taskStore, taskMessageQueue }` constructor option)
|
||||
- `protocol.taskManager` getter, `Protocol#_bindTaskManager`
|
||||
- `RequestOptions.task` / `RequestOptions.relatedTask`, `NotificationOptions.relatedTask`
|
||||
- `BaseContext.task` (`ctx.task?.store` / `ctx.task?.id` / `ctx.task?.requestedTtl`)
|
||||
- abstract `assertTaskCapability` / `assertTaskHandlerCapability`
|
||||
- `client.experimental.tasks.*` / `server.experimental.tasks.*` / `mcpServer.experimental.tasks.*` accessors and the `Experimental{Client,Server,McpServer}Tasks` classes
|
||||
- streaming methods (`requestStream`, `callToolStream`, `createMessageStream`, `elicitInputStream`) and the `ResponseMessage` types they yielded (`BaseResponseMessage`, `ErrorMessage`, `AsyncGeneratorValue`)
|
||||
- `mcpServer.experimental.tasks.registerToolTask(...)`, `ToolTaskHandler`, `TaskRequestHandler`, `CreateTaskRequestHandler`
|
||||
- `TaskMessageQueue`, `InMemoryTaskMessageQueue`, `BaseQueuedMessage` and the `Queued*` message types, `CreateTaskServerContext`, `TaskServerContext`, `TaskToolExecution`
|
||||
- `examples/{client,server}/src/simpleTaskInteractive*.ts`
|
||||
|
||||
**Before (v1):**
|
||||
**Also removed:** the storage layer (`TaskStore`, `InMemoryTaskStore`, `CreateTaskOptions`, `isTerminal`) and `TaskCreationParams`. They will return as part of the SEP-2663 server-directed plugin in a follow-up.
|
||||
|
||||
```typescript
|
||||
// Requesting unlimited lifetime by passing null
|
||||
const result = await client.callTool({
|
||||
name: 'long-task',
|
||||
arguments: {},
|
||||
task: { ttl: null }
|
||||
});
|
||||
|
||||
// Handler context had number | null | undefined
|
||||
server.setRequestHandler('tools/call', async (request, ctx) => {
|
||||
const ttl: number | null | undefined = ctx.task?.requestedTtl;
|
||||
});
|
||||
```
|
||||
|
||||
**After (v2):**
|
||||
|
||||
```typescript
|
||||
// Omit ttl to let the server decide (server may return null for unlimited)
|
||||
const result = await client.callTool({
|
||||
name: 'long-task',
|
||||
arguments: {},
|
||||
task: {}
|
||||
});
|
||||
|
||||
// Handler context is now number | undefined
|
||||
server.setRequestHandler('tools/call', async (request, ctx) => {
|
||||
const ttl: number | undefined = ctx.task?.requestedTtl;
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** These task APIs are marked `@experimental` and may change without notice.
|
||||
There is no migration path for the removed surface; it was always `@experimental`. Task support is planned to return as an opt-in extension plugin per SEP-2663.
|
||||
|
||||
## Enhancements
|
||||
|
||||
|
|
@ -949,7 +934,8 @@ const server = new McpServer(
|
|||
);
|
||||
```
|
||||
|
||||
You do not need to install or import validator packages for the default behavior. The client and server packages bundle the validator backend selected by the runtime shim, so a normal `import { McpServer } from '@modelcontextprotocol/server'` does not pull `ajv` or `@cfworker/json-schema` into your bundle until you choose to customize.
|
||||
You do not need to install or import validator packages for the default behavior. The client and server packages bundle the validator backend selected by the runtime shim, so a normal `import { McpServer } from '@modelcontextprotocol/server'` does not pull `ajv` or
|
||||
`@cfworker/json-schema` into your bundle until you choose to customize.
|
||||
|
||||
If you want to customize the **built-in** backend (for example, pre-register schemas by `$id`, register custom AJV formats, or change the `@cfworker/json-schema` draft), import the named class from the explicit subpath and pass an instance through `jsonSchemaValidator`:
|
||||
|
||||
|
|
@ -984,7 +970,8 @@ const server = new McpServer(
|
|||
|
||||
(both subpaths are also available on `@modelcontextprotocol/client/validators/...`)
|
||||
|
||||
If you import from one of these subpaths in your own code, the corresponding peer dep (`ajv` + `ajv-formats`, or `@cfworker/json-schema`) needs to be installed in your `package.json`. The runtime shim continues to vendor a copy for the default code path, so you can use the subpath in some files and rely on the default in others.
|
||||
If you import from one of these subpaths in your own code, the corresponding peer dep (`ajv` + `ajv-formats`, or `@cfworker/json-schema`) needs to be installed in your `package.json`. The runtime shim continues to vendor a copy for the default code path, so you can use the
|
||||
subpath in some files and rely on the default in others.
|
||||
|
||||
To replace validation wholesale rather than customizing the built-in classes, implement the `jsonSchemaValidator` interface and pass your own implementation through the option above.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue