@@ -18,6 +18,44 @@ export interface CopilotSubblockMetadata {
1818 title ?: string
1919 required ?: boolean
2020 description ?: string
21+ placeholder ?: string
22+ layout ?: string
23+ mode ?: string
24+ hidden ?: boolean
25+ condition ?: any
26+ // Dropdown/combobox options
27+ options ?: { id : string ; label ?: string ; hasIcon ?: boolean } [ ]
28+ // Numeric constraints
29+ min ?: number
30+ max ?: number
31+ step ?: number
32+ integer ?: boolean
33+ // Text input properties
34+ rows ?: number
35+ password ?: boolean
36+ multiSelect ?: boolean
37+ // Code/generation properties
38+ language ?: string
39+ generationType ?: string
40+ // OAuth/credential properties
41+ provider ?: string
42+ serviceId ?: string
43+ requiredScopes ?: string [ ]
44+ // File properties
45+ mimeType ?: string
46+ acceptedTypes ?: string
47+ multiple ?: boolean
48+ maxSize ?: number
49+ // Other properties
50+ connectionDroppable ?: boolean
51+ columns ?: string [ ]
52+ wandConfig ?: any
53+ availableTriggers ?: string [ ]
54+ triggerProvider ?: string
55+ dependsOn ?: string [ ]
56+ canonicalParamId ?: string
57+ defaultValue ?: any
58+ value ?: string // 'function' if it's a function, undefined otherwise
2159}
2260
2361export interface CopilotToolMetadata {
@@ -38,7 +76,8 @@ export interface CopilotBlockMetadata {
3876 name : string
3977 description : string
4078 bestPractices ?: string
41- commonParameters : Record < string , any >
79+ commonParameters : CopilotSubblockMetadata [ ]
80+ inputs ?: Record < string , any >
4281 triggerAllowed ?: boolean
4382 authType ?: 'OAuth' | 'API Key' | 'Bot Token'
4483 tools : CopilotToolMetadata [ ]
@@ -77,15 +116,16 @@ export const getBlocksMetadataServerTool: BaseServerTool<
77116
78117 if ( SPECIAL_BLOCKS_METADATA [ blockId ] ) {
79118 const specialBlock = SPECIAL_BLOCKS_METADATA [ blockId ]
80- const { operationParameters } = splitParametersByOperation (
119+ const { commonParameters , operationParameters } = splitParametersByOperation (
81120 specialBlock . subBlocks || [ ] ,
82121 specialBlock . inputs || { }
83122 )
84123 metadata = {
85124 id : specialBlock . id ,
86125 name : specialBlock . name ,
87126 description : specialBlock . description || '' ,
88- commonParameters : specialBlock . inputs || { } ,
127+ commonParameters : commonParameters ,
128+ inputs : specialBlock . inputs || { } ,
89129 tools : [ ] ,
90130 triggers : [ ] ,
91131 operationParameters,
@@ -127,7 +167,7 @@ export const getBlocksMetadataServerTool: BaseServerTool<
127167 }
128168
129169 const blockInputs = computeBlockLevelInputs ( blockConfig )
130- const { operationParameters } = splitParametersByOperation (
170+ const { commonParameters , operationParameters } = splitParametersByOperation (
131171 Array . isArray ( blockConfig . subBlocks ) ? blockConfig . subBlocks : [ ] ,
132172 blockInputs
133173 )
@@ -159,7 +199,8 @@ export const getBlocksMetadataServerTool: BaseServerTool<
159199 name : blockConfig . name || blockId ,
160200 description : blockConfig . longDescription || blockConfig . description || '' ,
161201 bestPractices : blockConfig . bestPractices ,
162- commonParameters : blockInputs ,
202+ commonParameters : commonParameters ,
203+ inputs : blockInputs ,
163204 triggerAllowed : ! ! blockConfig . triggerAllowed ,
164205 authType : resolveAuthType ( blockConfig . authMode ) ,
165206 tools,
@@ -189,23 +230,94 @@ export const getBlocksMetadataServerTool: BaseServerTool<
189230 } catch { }
190231
191232 if ( metadata ) {
192- result [ blockId ] = metadata as CopilotBlockMetadata
233+ result [ blockId ] = pruneNullishDeep ( metadata ) as CopilotBlockMetadata
193234 }
194235 }
195236
196237 return GetBlocksMetadataResult . parse ( { metadata : result } )
197238 } ,
198239}
199240
200- function simplifySubBlock ( sb : any ) : CopilotSubblockMetadata {
201- const simplified : CopilotSubblockMetadata = {
241+ function processSubBlock ( sb : any ) : CopilotSubblockMetadata {
242+ // Start with required fields
243+ const processed : CopilotSubblockMetadata = {
202244 id : sb . id ,
203245 type : sb . type ,
204246 }
205- if ( sb . title ) simplified . title = sb . title
206- if ( sb . required ) simplified . required = sb . required
207- if ( sb . description ) simplified . description = sb . description
208- return simplified
247+
248+ // Process all optional fields - only add if they exist and are not null/undefined
249+ const optionalFields = {
250+ // Basic properties
251+ title : sb . title ,
252+ required : sb . required ,
253+ description : sb . description ,
254+ placeholder : sb . placeholder ,
255+ layout : sb . layout ,
256+ mode : sb . mode ,
257+ hidden : sb . hidden ,
258+ canonicalParamId : sb . canonicalParamId ,
259+ defaultValue : sb . defaultValue ,
260+
261+ // Numeric constraints
262+ min : sb . min ,
263+ max : sb . max ,
264+ step : sb . step ,
265+ integer : sb . integer ,
266+
267+ // Text input properties
268+ rows : sb . rows ,
269+ password : sb . password ,
270+ multiSelect : sb . multiSelect ,
271+
272+ // Code/generation properties
273+ language : sb . language ,
274+ generationType : sb . generationType ,
275+
276+ // OAuth/credential properties
277+ provider : sb . provider ,
278+ serviceId : sb . serviceId ,
279+ requiredScopes : sb . requiredScopes ,
280+
281+ // File properties
282+ mimeType : sb . mimeType ,
283+ acceptedTypes : sb . acceptedTypes ,
284+ multiple : sb . multiple ,
285+ maxSize : sb . maxSize ,
286+
287+ // Other properties
288+ connectionDroppable : sb . connectionDroppable ,
289+ columns : sb . columns ,
290+ wandConfig : sb . wandConfig ,
291+ availableTriggers : sb . availableTriggers ,
292+ triggerProvider : sb . triggerProvider ,
293+ dependsOn : sb . dependsOn ,
294+ }
295+
296+ // Add non-null optional fields
297+ for ( const [ key , value ] of Object . entries ( optionalFields ) ) {
298+ if ( value !== undefined && value !== null ) {
299+ ; ( processed as any ) [ key ] = value
300+ }
301+ }
302+
303+ // Handle condition normalization
304+ const condition = normalizeCondition ( sb . condition )
305+ if ( condition !== undefined ) {
306+ processed . condition = condition
307+ }
308+
309+ // Handle value field (check if it's a function)
310+ if ( typeof sb . value === 'function' ) {
311+ processed . value = 'function'
312+ }
313+
314+ // Process options with icon detection
315+ const options = resolveSubblockOptions ( sb )
316+ if ( options ) {
317+ processed . options = options
318+ }
319+
320+ return processed
209321}
210322
211323function resolveAuthType (
@@ -218,6 +330,65 @@ function resolveAuthType(
218330 return undefined
219331}
220332
333+ function resolveSubblockOptions (
334+ sb : any
335+ ) : { id : string ; label ?: string ; hasIcon ?: boolean } [ ] | undefined {
336+ try {
337+ // Resolve options if it's a function
338+ const rawOptions = typeof sb . options === 'function' ? sb . options ( ) : sb . options
339+ if ( ! Array . isArray ( rawOptions ) ) return undefined
340+
341+ const normalized = rawOptions
342+ . map ( ( opt : any ) => {
343+ if ( ! opt ) return undefined
344+
345+ // Handle both string and object options
346+ const id = typeof opt === 'object' ? opt . id : opt
347+ if ( id === undefined || id === null ) return undefined
348+
349+ const result : { id : string ; label ?: string ; hasIcon ?: boolean } = {
350+ id : String ( id ) ,
351+ }
352+
353+ // Add label if present
354+ if ( typeof opt === 'object' && typeof opt . label === 'string' ) {
355+ result . label = opt . label
356+ }
357+
358+ // Check for icon presence
359+ if ( typeof opt === 'object' && opt . icon ) {
360+ result . hasIcon = true
361+ }
362+
363+ return result
364+ } )
365+ . filter ( ( o ) : o is { id : string ; label ?: string ; hasIcon ?: boolean } => o !== undefined )
366+
367+ return normalized . length > 0 ? normalized : undefined
368+ } catch {
369+ return undefined
370+ }
371+ }
372+
373+ function pruneNullishDeep < T > ( value : T ) : T {
374+ if ( value === null || value === undefined ) return value
375+ if ( Array . isArray ( value ) ) {
376+ const prunedArray = ( value as unknown [ ] )
377+ . map ( ( v ) => pruneNullishDeep ( v ) )
378+ . filter ( ( v ) => v !== undefined && v !== null )
379+ return prunedArray as unknown as T
380+ }
381+ if ( typeof value === 'object' ) {
382+ const output : Record < string , any > = { }
383+ for ( const [ k , v ] of Object . entries ( value as Record < string , any > ) ) {
384+ const pruned = pruneNullishDeep ( v )
385+ if ( pruned !== undefined && pruned !== null ) output [ k ] = pruned
386+ }
387+ return output as unknown as T
388+ }
389+ return value
390+ }
391+
221392function normalizeCondition ( condition : any ) : any | undefined {
222393 try {
223394 if ( ! condition ) return undefined
@@ -242,14 +413,14 @@ function splitParametersByOperation(
242413
243414 for ( const sb of subBlocks || [ ] ) {
244415 const cond = normalizeCondition ( sb . condition )
245- const simplified = simplifySubBlock ( sb )
416+ const processed = processSubBlock ( sb )
246417
247418 if ( cond && cond . field === 'operation' && ! cond . not && cond . value !== undefined ) {
248419 const values : any [ ] = Array . isArray ( cond . value ) ? cond . value : [ cond . value ]
249420 for ( const v of values ) {
250421 const key = String ( v )
251422 if ( ! operationParameters [ key ] ) operationParameters [ key ] = [ ]
252- operationParameters [ key ] . push ( simplified )
423+ operationParameters [ key ] . push ( processed )
253424 }
254425 } else {
255426 // Override description from blockInputs if available (by id or canonicalParamId)
@@ -258,12 +429,12 @@ function splitParametersByOperation(
258429 for ( const key of candidates ) {
259430 const bi = ( blockInputsForDescriptions as any ) [ key as string ]
260431 if ( bi && typeof bi . description === 'string' ) {
261- simplified . description = bi . description
432+ processed . description = bi . description
262433 break
263434 }
264435 }
265436 }
266- commonParameters . push ( simplified )
437+ commonParameters . push ( processed )
267438 }
268439 }
269440
0 commit comments