Skip to content

Commit 7193e89

Browse files
author
Lasim
committed
feat(backend): add user-level HTTP config overrides for headers and query params
1 parent be96cc1 commit 7193e89

File tree

1 file changed

+117
-4
lines changed
  • services/backend/src/routes/satellites

1 file changed

+117
-4
lines changed

services/backend/src/routes/satellites/config.ts

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)