@@ -646,6 +646,83 @@ export default async function satelliteConfigRoute(server: FastifyInstance) {
646646 }
647647 }
648648
649+ // Fetch and apply user-level HTTP configuration (Tier 3)
650+ // User config overrides team config for headers and URL query params
651+ if ( created_by_user_id ) {
652+ try {
653+ const httpUserConfigs = await db
654+ . select ( )
655+ . from ( mcpUserConfigurations )
656+ . where (
657+ and (
658+ eq ( mcpUserConfigurations . installation_id , installation . id ) ,
659+ eq ( mcpUserConfigurations . user_id , created_by_user_id )
660+ )
661+ )
662+ . limit ( 1 ) ;
663+
664+ const httpUserConfig = httpUserConfigs [ 0 ] ;
665+
666+ // Process user headers (overrides team headers)
667+ if ( httpUserConfig ?. user_headers ) {
668+ try {
669+ const userHeadersSchema = JSON . parse ( server . user_headers_schema || '[]' ) ;
670+ const decryptedUserHeaders = await McpEnvStorage . retrieveUserEnv (
671+ httpUserConfig . user_headers ,
672+ userHeadersSchema ,
673+ { maskSecrets : false } , // Decrypt secrets for satellite
674+ request . log
675+ ) ;
676+ serverConfig . headers = { ...serverConfig . headers , ...decryptedUserHeaders } ;
677+
678+ request . log . debug ( {
679+ serverId : server . id ,
680+ userId : created_by_user_id ,
681+ userHeadersCount : Object . keys ( decryptedUserHeaders ) . length
682+ } , 'Added user headers to HTTP configuration' ) ;
683+ } catch ( error ) {
684+ request . log . warn ( {
685+ serverId : server . id ,
686+ userId : created_by_user_id ,
687+ error : error instanceof Error ? error . message : String ( error )
688+ } , 'Failed to decrypt and parse user_headers' ) ;
689+ }
690+ }
691+
692+ // Process user URL query params (overrides team query params)
693+ if ( httpUserConfig ?. user_url_query_params ) {
694+ try {
695+ const userUrlQueryParamsSchema = JSON . parse ( server . user_url_query_params_schema || '[]' ) ;
696+ const decryptedUserQueryParams = await McpEnvStorage . retrieveUserEnv (
697+ httpUserConfig . user_url_query_params ,
698+ userUrlQueryParamsSchema ,
699+ { maskSecrets : false } , // Decrypt secrets for satellite
700+ request . log
701+ ) ;
702+ finalQueryParams = { ...finalQueryParams , ...decryptedUserQueryParams } ;
703+
704+ request . log . debug ( {
705+ serverId : server . id ,
706+ userId : created_by_user_id ,
707+ userQueryParamsCount : Object . keys ( decryptedUserQueryParams ) . length
708+ } , 'Added user URL query params to HTTP configuration' ) ;
709+ } catch ( error ) {
710+ request . log . warn ( {
711+ serverId : server . id ,
712+ userId : created_by_user_id ,
713+ error : error instanceof Error ? error . message : String ( error )
714+ } , 'Failed to decrypt and parse user_url_query_params' ) ;
715+ }
716+ }
717+ } catch ( error ) {
718+ request . log . warn ( {
719+ serverId : server . id ,
720+ userId : created_by_user_id ,
721+ error : error instanceof Error ? error . message : String ( error )
722+ } , 'Failed to fetch user configuration for HTTP transport' ) ;
723+ }
724+ }
725+
649726 // Apply query params to URL if any exist
650727 if ( finalUrl && Object . keys ( finalQueryParams ) . length > 0 ) {
651728 try {
@@ -667,7 +744,7 @@ export default async function satelliteConfigRoute(server: FastifyInstance) {
667744 const secretQueryParams : string [ ] = [ ] ;
668745 const secretHeaders : string [ ] = [ ] ;
669746
670- // Extract secret query params from schema
747+ // Extract secret query params from team schema
671748 if ( installation . team_url_query_params ) {
672749 try {
673750 const teamUrlQueryParamsSchema = JSON . parse ( server . team_url_query_params_schema || '[]' ) ;
@@ -681,11 +758,29 @@ export default async function satelliteConfigRoute(server: FastifyInstance) {
681758 request . log . debug ( {
682759 serverId : server . id ,
683760 error : error instanceof Error ? error . message : String ( error )
684- } , 'Failed to extract secret query param metadata' ) ;
761+ } , 'Failed to extract secret query param metadata from team schema ' ) ;
685762 }
686763 }
687764
688- // Extract secret headers from schema
765+ // Extract secret query params from user schema
766+ try {
767+ const userUrlQueryParamsSchema = JSON . parse ( server . user_url_query_params_schema || '[]' ) ;
768+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
769+ userUrlQueryParamsSchema . forEach ( ( field : any ) => {
770+ if ( field . type === 'secret' || field . type === 'password' ) {
771+ if ( ! secretQueryParams . includes ( field . name ) ) {
772+ secretQueryParams . push ( field . name ) ;
773+ }
774+ }
775+ } ) ;
776+ } catch ( error ) {
777+ request . log . debug ( {
778+ serverId : server . id ,
779+ error : error instanceof Error ? error . message : String ( error )
780+ } , 'Failed to extract secret query param metadata from user schema' ) ;
781+ }
782+
783+ // Extract secret headers from team schema
689784 if ( installation . team_headers ) {
690785 try {
691786 const teamHeadersSchema = JSON . parse ( server . team_headers_schema || '[]' ) ;
@@ -699,10 +794,28 @@ export default async function satelliteConfigRoute(server: FastifyInstance) {
699794 request . log . debug ( {
700795 serverId : server . id ,
701796 error : error instanceof Error ? error . message : String ( error )
702- } , 'Failed to extract secret header metadata' ) ;
797+ } , 'Failed to extract secret header metadata from team schema ' ) ;
703798 }
704799 }
705800
801+ // Extract secret headers from user schema
802+ try {
803+ const userHeadersSchema = JSON . parse ( server . user_headers_schema || '[]' ) ;
804+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
805+ userHeadersSchema . forEach ( ( field : any ) => {
806+ if ( field . type === 'secret' || field . type === 'password' ) {
807+ if ( ! secretHeaders . includes ( field . name ) ) {
808+ secretHeaders . push ( field . name ) ;
809+ }
810+ }
811+ } ) ;
812+ } catch ( error ) {
813+ request . log . debug ( {
814+ serverId : server . id ,
815+ error : error instanceof Error ? error . message : String ( error )
816+ } , 'Failed to extract secret header metadata from user schema' ) ;
817+ }
818+
706819 // Add secret metadata to config if any secrets found
707820 if ( secretQueryParams . length > 0 || secretHeaders . length > 0 ) {
708821 serverConfig . secret_metadata = {
0 commit comments