From c5d4af4d09ce63422b1955fd3a685f686f8dea2b Mon Sep 17 00:00:00 2001 From: Jefsky Agent Date: Wed, 13 May 2026 23:21:36 +0800 Subject: [PATCH 1/3] fix(server): preserve icons in registerPrompt() registerPrompt() was destructuring only { title, description, argsSchema, _meta } from the config, silently dropping icons. The MCP 2025-11-25 spec explicitly allows icons on PromptDefinition, and the Icon interface already exists. This change: - Adds icons?: Icon[] to all three registerPrompt() overloads - Passes icons through _createRegisteredPrompt() - Stores icons on RegisteredPrompt - Includes icons in the prompts/list response - Adds icons to the update() method Closes modelcontextprotocol/typescript-sdk#2054 --- packages/server/src/server/mcp.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/server/src/server/mcp.ts b/packages/server/src/server/mcp.ts index fb45fd5db..5520dabfc 100644 --- a/packages/server/src/server/mcp.ts +++ b/packages/server/src/server/mcp.ts @@ -8,6 +8,7 @@ import type { CreateTaskResult, CreateTaskServerContext, GetPromptResult, + Icon, Implementation, ListPromptsResult, ListResourcesResult, @@ -533,6 +534,7 @@ export class McpServer { title: prompt.title, description: prompt.description, arguments: prompt.argsSchema ? promptArgumentsFromStandardSchema(prompt.argsSchema) : undefined, + icons: prompt.icons, _meta: prompt._meta }; }) @@ -702,6 +704,7 @@ export class McpServer { title: string | undefined, description: string | undefined, argsSchema: StandardSchemaWithJSON | undefined, + icons: Icon[] | undefined, callback: PromptCallback, _meta: Record | undefined ): RegisteredPrompt { @@ -713,6 +716,7 @@ export class McpServer { title, description, argsSchema, + icons, _meta, handler: createPromptHandler(name, argsSchema, callback), enabled: true, @@ -726,6 +730,7 @@ export class McpServer { } if (updates.title !== undefined) registeredPrompt.title = updates.title; if (updates.description !== undefined) registeredPrompt.description = updates.description; + if (updates.icons !== undefined) registeredPrompt.icons = updates.icons; if (updates._meta !== undefined) registeredPrompt._meta = updates._meta; // Track if we need to regenerate the handler @@ -952,6 +957,7 @@ export class McpServer { title?: string; description?: string; argsSchema?: Args; + icons?: Icon[]; _meta?: Record; }, cb: PromptCallback @@ -963,6 +969,7 @@ export class McpServer { title?: string; description?: string; argsSchema?: Args; + icons?: Icon[]; _meta?: Record; }, cb: LegacyPromptCallback @@ -973,6 +980,7 @@ export class McpServer { title?: string; description?: string; argsSchema?: StandardSchemaWithJSON | ZodRawShape; + icons?: Icon[]; _meta?: Record; }, cb: PromptCallback | LegacyPromptCallback @@ -981,13 +989,14 @@ export class McpServer { throw new Error(`Prompt ${name} is already registered`); } - const { title, description, argsSchema, _meta } = config; + const { title, description, argsSchema, icons, _meta } = config; const registeredPrompt = this._createRegisteredPrompt( name, title, description, normalizeRawShapeSchema(argsSchema), + icons, cb as PromptCallback, _meta ); @@ -1308,6 +1317,7 @@ export type RegisteredPrompt = { title?: string; description?: string; argsSchema?: StandardSchemaWithJSON; + icons?: Icon[]; _meta?: Record; /** @hidden */ handler: PromptHandler; @@ -1319,6 +1329,7 @@ export type RegisteredPrompt = { title?: string; description?: string; argsSchema?: Args; + icons?: Icon[]; _meta?: Record; callback?: PromptCallback; enabled?: boolean; From aa736e354d9063f737002b6a77a5194cec3d40a7 Mon Sep 17 00:00:00 2001 From: Jefsky Agent Date: Wed, 13 May 2026 23:26:07 +0800 Subject: [PATCH 2/3] fix(client): let saveTokens I/O errors propagate in auth() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refreshAuthorization() + saveTokens() were wrapped in a single try/catch. If saveTokens() threw an I/O error (filesystem, file lock, etc.), it was silently discarded by the same catch block that handled AS-side failures. Separating the two: - refreshAuthorization failure → fall through to re-auth (as before) - saveTokens failure → propagates naturally so caller can handle it Also adds a comment clarifying the I/O error case should propagate. Fixes modelcontextprotocol/typescript-sdk#2034 --- packages/client/src/client/auth.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/client/src/client/auth.ts b/packages/client/src/client/auth.ts index 5f55fb7a0..46021c862 100644 --- a/packages/client/src/client/auth.ts +++ b/packages/client/src/client/auth.ts @@ -763,9 +763,10 @@ async function authInternal( // Handle token refresh or new authorization if (tokens?.refresh_token) { + let newTokens; try { // Attempt to refresh the token - const newTokens = await refreshAuthorization(authorizationServerUrl, { + newTokens = await refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken: tokens.refresh_token, @@ -773,18 +774,20 @@ async function authInternal( addClientAuthentication: provider.addClientAuthentication, fetchFn }); - - await provider.saveTokens(newTokens); - return 'AUTHORIZED'; } catch (error) { // If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry. if (!(error instanceof OAuthError) || error.code === OAuthErrorCode.ServerError) { - // Could not refresh OAuth tokens + // Could not refresh OAuth tokens — fall through to re-auth } else { // Refresh failed for another reason, re-throw throw error; } } + + if (newTokens) { + await provider.saveTokens(newTokens); // Let I/O errors propagate + return 'AUTHORIZED'; + } } const state = provider.state ? await provider.state() : undefined; From 27c68919795c6d6ab58da794aec523528e1c55e1 Mon Sep 17 00:00:00 2001 From: Jefsky Agent Date: Wed, 13 May 2026 23:44:56 +0800 Subject: [PATCH 3/3] docs(core): clarify resetTimeoutOnProgress requires onprogress The resetTimeoutOnProgress option only works when a progress token is included in the request, which requires onprogress to be set. Without onprogress, no token is sent so the server never sends progress notifications and the timeout reset never triggers. Documents the behavior clearly so users don't expect it to work when progress is consumed via lower-level transport features without onprogress being set. Fixes modelcontextprotocol/typescript-sdk#2076 --- packages/core/src/shared/protocol.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/shared/protocol.ts b/packages/core/src/shared/protocol.ts index 361bd6fc7..462ee4515 100644 --- a/packages/core/src/shared/protocol.ts +++ b/packages/core/src/shared/protocol.ts @@ -124,7 +124,11 @@ export type RequestOptions = { /** * If `true`, receiving a progress notification will reset the request timeout. - * This is useful for long-running operations that send periodic progress updates. + * Note: a progress token is only included in the request when `onprogress` is + * explicitly provided (to avoid unnecessary server round-trips). Therefore + * `resetTimeoutOnProgress` only takes effect when `onprogress` is also set. + * If progress is consumed via lower-level transport features, provide a no-op + * `onprogress` callback to enable timeout reset on progress. * Default: `false` */ resetTimeoutOnProgress?: boolean;