@@ -334,6 +334,12 @@ const isNonEmptyString = (value: unknown): value is string =>
334334const isErrnoException = ( value : unknown ) : value is NodeJS . ErrnoException =>
335335 isRecord ( value ) && typeof value . code === 'string'
336336
337+ const isHttpImportStep = ( value : unknown ) : value is Record < string , unknown > =>
338+ isRecord ( value ) && value . robot === '/http/import'
339+
340+ const hasHttpImportStep = ( steps : Record < string , unknown > ) : boolean =>
341+ Object . values ( steps ) . some ( ( step ) => isHttpImportStep ( step ) )
342+
337343const getAssemblyIdFromUrl = ( assemblyUrl : string ) : string => {
338344 const match = assemblyUrl . match ( / \/ a s s e m b l i e s \/ ( [ ^ / ? # ] + ) / )
339345 if ( ! match ) {
@@ -526,17 +532,45 @@ export const createTransloaditMcpServer = (
526532 if ( 'error' in liveClient ) return liveClient . error
527533 const { client } = liveClient
528534 const tempCleanups : Array < ( ) => Promise < void > > = [ ]
535+ const warnings : Array < { code : string ; message : string ; hint ?: string ; path ?: string } > = [ ]
536+ let templatePathHint : string | undefined
529537
530538 try {
531539 const fileInputs = files ?? [ ]
540+ const hasUrlInputs = fileInputs . some ( ( file ) => file . kind === 'url' )
532541 let params = parseInstructions ( instructions ) ?? ( { } as CreateAssemblyParams )
533542
534543 if ( builtin_template ) {
535544 const templateId = buildBuiltinTemplateId ( builtin_template . slug , builtin_template . version )
536545 const overrides = builtin_template . overrides
537- params = ( overrides && isRecord ( overrides ) ? overrides : { } ) as CreateAssemblyParams
546+ const baseParams = (
547+ overrides && isRecord ( overrides ) ? { ...overrides } : { }
548+ ) as CreateAssemblyParams
549+
550+ templatePathHint = 'builtin_template.slug'
551+ params = baseParams
538552 params . template_id = templateId
539553 }
554+
555+ if ( hasUrlInputs && params . template_id ) {
556+ const steps = isRecord ( params . steps ) ? { ...params . steps } : { }
557+ if ( ! hasHttpImportStep ( steps ) ) {
558+ const imported = isRecord ( steps . imported )
559+ ? ( steps . imported as Record < string , unknown > )
560+ : { }
561+ steps . imported = {
562+ ...imported ,
563+ robot : '/http/import' ,
564+ }
565+ params . steps = steps
566+ warnings . push ( {
567+ code : 'mcp_imported_step' ,
568+ message :
569+ 'URL inputs with template_id require a step named "imported" that the template uses as input. The MCP server injected /http/import into that step.' ,
570+ path : templatePathHint ?? 'instructions.template_id' ,
571+ } )
572+ }
573+ }
540574 const prep = await prepareInputFiles ( {
541575 inputFiles : fileInputs ,
542576 params,
@@ -634,6 +668,7 @@ export const createTransloaditMcpServer = (
634668 assembly,
635669 upload : uploadSummary ,
636670 next_steps : nextSteps ,
671+ warnings : warnings . length > 0 ? warnings : undefined ,
637672 } )
638673 } finally {
639674 await Promise . all ( tempCleanups . map ( ( cleanup ) => cleanup ( ) ) )
0 commit comments