11import {
22 RESOURCE_MIME_TYPE ,
3- registerAppResource ,
3+ RESOURCE_URI_META_KEY ,
44 registerAppTool ,
55} from "@modelcontextprotocol/ext-apps/server" ;
66import type {
77 McpServer ,
8+ RegisteredResource ,
89 RegisteredTool ,
910 ToolCallback ,
1011} from "@modelcontextprotocol/sdk/server/mcp.js" ;
@@ -47,10 +48,39 @@ export interface SessionToolRegistrar {
4748 callback : ToolCallback < InputArgs > ,
4849 ui ?: ToolUiConfig < InputArgs >
4950 ) : RegisteredTool ;
50- setUiEnabled ( enabled : boolean ) : void ;
51+ setUiEnabled ( enabled : boolean , options ?: SetUiEnabledOptions ) : void ;
5152}
5253
53- export function createSessionToolRegistrar ( server : McpServer ) : SessionToolRegistrar {
54+ type SetUiEnabledOptions = {
55+ notify ?: boolean ;
56+ } ;
57+
58+ type MutableRegisteredTool = RegisteredTool & {
59+ _meta ?: Record < string , unknown > ;
60+ enabled : boolean ;
61+ handler : ToolCallback < any > ;
62+ } ;
63+
64+ type MutableRegisteredResource = RegisteredResource & {
65+ enabled : boolean ;
66+ } ;
67+
68+ type UiAwareRegistration = {
69+ plainCallback : ToolCallback < any > ;
70+ plainMeta : Record < string , unknown > | undefined ;
71+ resource : MutableRegisteredResource ;
72+ tool : MutableRegisteredTool ;
73+ uiCallback : ToolCallback < any > ;
74+ uiMeta : Record < string , unknown > ;
75+ } ;
76+
77+ export function createSessionToolRegistrar (
78+ server : McpServer ,
79+ uiEnabled : boolean
80+ ) : SessionToolRegistrar {
81+ const uiAwareRegistrations : UiAwareRegistration [ ] = [ ] ;
82+ let currentUiEnabled = uiEnabled ;
83+
5484 return {
5585 registerTool <
5686 OutputArgs extends ZodRawShapeCompat | AnySchema ,
@@ -65,26 +95,26 @@ export function createSessionToolRegistrar(server: McpServer): SessionToolRegist
6595 return server . registerTool ( name , config , callback ) ;
6696 }
6797
68- const tool = registerAppTool (
69- server ,
70- name ,
71- {
72- ... config ,
73- _meta : {
74- ... ( config . _meta ?? { } ) ,
75- ui : {
76- resourceUri : ui . resourceUri ,
77- } ,
78- } ,
79- } as any ,
80- ( ui . callback ?? callback ) as any
81- ) ;
98+ const plainMeta = stripToolUiMeta ( config . _meta ) ;
99+ const uiMeta = createToolUiMeta ( config . _meta , ui . resourceUri ) ;
100+ const uiCallback = ( ui . callback ?? callback ) as ToolCallback < any > ;
101+ const tool = currentUiEnabled
102+ ? registerAppTool (
103+ server ,
104+ name ,
105+ {
106+ ... config ,
107+ _meta : uiMeta ,
108+ } as any ,
109+ uiCallback as any
110+ )
111+ : server . registerTool ( name , { ... config , _meta : plainMeta } , callback ) ;
82112
83- registerAppResource (
84- server ,
113+ const resource = server . registerResource (
85114 ui . resourceName ?? `${ config . title ?? name } UI` ,
86115 ui . resourceUri ,
87116 {
117+ mimeType : RESOURCE_MIME_TYPE ,
88118 description :
89119 ui . resourceDescription ?? `${ config . title ?? name } interactive UI` ,
90120 _meta : ui . resourceMeta ,
@@ -101,12 +131,98 @@ export function createSessionToolRegistrar(server: McpServer): SessionToolRegist
101131 } )
102132 ) ;
103133
134+ if ( ! currentUiEnabled ) {
135+ resource . disable ( ) ;
136+ }
137+
138+ uiAwareRegistrations . push ( {
139+ plainCallback : callback as ToolCallback < any > ,
140+ plainMeta,
141+ resource : resource as MutableRegisteredResource ,
142+ tool : tool as MutableRegisteredTool ,
143+ uiCallback,
144+ uiMeta,
145+ } ) ;
146+
104147 return tool ;
105148 } ,
106- setUiEnabled ( ) {
107- // The hosted example server always advertises its app tool and relies on
108- // the host to ignore UI metadata when unsupported. Keep the method for
109- // compatibility with existing call sites.
149+ setUiEnabled ( enabled , options ) {
150+ if ( currentUiEnabled === enabled ) {
151+ return ;
152+ }
153+
154+ currentUiEnabled = enabled ;
155+ const notify = options ?. notify ?? true ;
156+
157+ for ( const registration of uiAwareRegistrations ) {
158+ applyUiEnabledState ( registration , enabled , notify ) ;
159+ }
160+ } ,
161+ } ;
162+ }
163+
164+ function applyUiEnabledState (
165+ registration : UiAwareRegistration ,
166+ enabled : boolean ,
167+ notify : boolean
168+ ) : void {
169+ if ( notify ) {
170+ registration . tool . update ( {
171+ _meta : enabled ? registration . uiMeta : ( registration . plainMeta ?? { } ) ,
172+ callback : enabled
173+ ? registration . uiCallback
174+ : registration . plainCallback ,
175+ } as any ) ;
176+
177+ if ( enabled ) {
178+ registration . resource . enable ( ) ;
179+ } else {
180+ registration . resource . disable ( ) ;
181+ }
182+
183+ return ;
184+ }
185+
186+ registration . tool . _meta = enabled
187+ ? registration . uiMeta
188+ : ( registration . plainMeta ?? { } ) ;
189+ registration . tool . handler = enabled
190+ ? registration . uiCallback
191+ : registration . plainCallback ;
192+ registration . resource . enabled = enabled ;
193+ }
194+
195+ function createToolUiMeta (
196+ meta : Record < string , unknown > | undefined ,
197+ resourceUri : string
198+ ) : Record < string , unknown > {
199+ const uiMeta =
200+ meta ?. ui && typeof meta . ui === "object" && ! Array . isArray ( meta . ui )
201+ ? ( meta . ui as Record < string , unknown > )
202+ : undefined ;
203+
204+ return {
205+ ...( stripToolUiMeta ( meta ) ?? { } ) ,
206+ ui : {
207+ ...( uiMeta ?? { } ) ,
208+ resourceUri,
110209 } ,
210+ [ RESOURCE_URI_META_KEY ] : resourceUri ,
111211 } ;
112212}
213+
214+ function stripToolUiMeta (
215+ meta : Record < string , unknown > | undefined
216+ ) : Record < string , unknown > | undefined {
217+ if ( ! meta ) {
218+ return undefined ;
219+ }
220+
221+ const {
222+ [ RESOURCE_URI_META_KEY ] : _resourceUri ,
223+ ui : _ui ,
224+ ...plainMeta
225+ } = meta ;
226+
227+ return Object . keys ( plainMeta ) . length > 0 ? plainMeta : undefined ;
228+ }
0 commit comments