mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
Expand environment variables throughout mcp_settings.json configuration (#384)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
This commit is contained in:
@@ -99,22 +99,33 @@ export function replaceEnvVars(input: string): string
|
||||
export function replaceEnvVars(
|
||||
input: Record<string, any> | string[] | string | undefined,
|
||||
): Record<string, any> | string[] | string {
|
||||
// Handle object input
|
||||
// Handle object input - recursively expand all nested values
|
||||
if (input && typeof input === 'object' && !Array.isArray(input)) {
|
||||
const res: Record<string, string> = {}
|
||||
const res: Record<string, any> = {}
|
||||
for (const [key, value] of Object.entries(input)) {
|
||||
if (typeof value === 'string') {
|
||||
res[key] = expandEnvVars(value)
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
// Recursively handle nested objects and arrays
|
||||
res[key] = replaceEnvVars(value as any)
|
||||
} else {
|
||||
res[key] = String(value)
|
||||
// Preserve non-string, non-object values (numbers, booleans, etc.)
|
||||
res[key] = value
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Handle array input
|
||||
// Handle array input - recursively expand all elements
|
||||
if (Array.isArray(input)) {
|
||||
return input.map((item) => expandEnvVars(item))
|
||||
return input.map((item) => {
|
||||
if (typeof item === 'string') {
|
||||
return expandEnvVars(item)
|
||||
} else if (typeof item === 'object' && item !== null) {
|
||||
return replaceEnvVars(item as any)
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
// Handle string input
|
||||
|
||||
@@ -235,6 +235,7 @@ export const createTransportFromConfig = async (name: string, conf: ServerConfig
|
||||
env['npm_config_registry'] = settings.systemConfig.install.npmRegistry;
|
||||
}
|
||||
|
||||
// Expand environment variables in command
|
||||
transport = new StdioClientTransport({
|
||||
cwd: os.homedir(),
|
||||
command: conf.command,
|
||||
@@ -379,12 +380,16 @@ export const initializeClientsFromSettings = async (
|
||||
try {
|
||||
for (const conf of allServers) {
|
||||
const { name } = conf;
|
||||
|
||||
// Expand environment variables in all configuration values
|
||||
const expandedConf = replaceEnvVars(conf as any) as ServerConfigWithName;
|
||||
|
||||
// Skip disabled servers
|
||||
if (conf.enabled === false) {
|
||||
if (expandedConf.enabled === false) {
|
||||
console.log(`Skipping disabled server: ${name}`);
|
||||
nextServerInfos.push({
|
||||
name,
|
||||
owner: conf.owner,
|
||||
owner: expandedConf.owner,
|
||||
status: 'disconnected',
|
||||
error: null,
|
||||
tools: [],
|
||||
@@ -402,7 +407,7 @@ export const initializeClientsFromSettings = async (
|
||||
if (existingServer && (!serverName || serverName !== name)) {
|
||||
nextServerInfos.push({
|
||||
...existingServer,
|
||||
enabled: conf.enabled === undefined ? true : conf.enabled,
|
||||
enabled: expandedConf.enabled === undefined ? true : expandedConf.enabled,
|
||||
});
|
||||
console.log(`Server '${name}' is already connected.`);
|
||||
continue;
|
||||
@@ -410,15 +415,15 @@ export const initializeClientsFromSettings = async (
|
||||
|
||||
let transport;
|
||||
let openApiClient;
|
||||
if (conf.type === 'openapi') {
|
||||
if (expandedConf.type === 'openapi') {
|
||||
// Handle OpenAPI type servers
|
||||
if (!conf.openapi?.url && !conf.openapi?.schema) {
|
||||
if (!expandedConf.openapi?.url && !expandedConf.openapi?.schema) {
|
||||
console.warn(
|
||||
`Skipping OpenAPI server '${name}': missing OpenAPI specification URL or schema`,
|
||||
);
|
||||
nextServerInfos.push({
|
||||
name,
|
||||
owner: conf.owner,
|
||||
owner: expandedConf.owner,
|
||||
status: 'disconnected',
|
||||
error: 'Missing OpenAPI specification URL or schema',
|
||||
tools: [],
|
||||
@@ -431,20 +436,20 @@ export const initializeClientsFromSettings = async (
|
||||
// Create server info first and keep reference to it
|
||||
const serverInfo: ServerInfo = {
|
||||
name,
|
||||
owner: conf.owner,
|
||||
owner: expandedConf.owner,
|
||||
status: 'connecting',
|
||||
error: null,
|
||||
tools: [],
|
||||
prompts: [],
|
||||
createTime: Date.now(),
|
||||
enabled: conf.enabled === undefined ? true : conf.enabled,
|
||||
config: conf, // Store reference to original config for OpenAPI passthrough headers
|
||||
enabled: expandedConf.enabled === undefined ? true : expandedConf.enabled,
|
||||
config: expandedConf, // Store reference to expanded config for OpenAPI passthrough headers
|
||||
};
|
||||
nextServerInfos.push(serverInfo);
|
||||
|
||||
try {
|
||||
// Create OpenAPI client instance
|
||||
openApiClient = new OpenAPIClient(conf);
|
||||
openApiClient = new OpenAPIClient(expandedConf);
|
||||
|
||||
console.log(`Initializing OpenAPI server: ${name}...`);
|
||||
|
||||
@@ -480,7 +485,7 @@ export const initializeClientsFromSettings = async (
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
transport = await createTransportFromConfig(name, conf);
|
||||
transport = await createTransportFromConfig(name, expandedConf);
|
||||
}
|
||||
|
||||
const client = new Client(
|
||||
@@ -504,7 +509,7 @@ export const initializeClientsFromSettings = async (
|
||||
: undefined;
|
||||
|
||||
// Get request options from server configuration, with fallbacks
|
||||
const serverRequestOptions = conf.options || {};
|
||||
const serverRequestOptions = expandedConf.options || {};
|
||||
const requestOptions = {
|
||||
timeout: serverRequestOptions.timeout || 60000,
|
||||
resetTimeoutOnProgress: serverRequestOptions.resetTimeoutOnProgress || false,
|
||||
@@ -514,7 +519,7 @@ export const initializeClientsFromSettings = async (
|
||||
// Create server info first and keep reference to it
|
||||
const serverInfo: ServerInfo = {
|
||||
name,
|
||||
owner: conf.owner,
|
||||
owner: expandedConf.owner,
|
||||
status: 'connecting',
|
||||
error: null,
|
||||
tools: [],
|
||||
@@ -523,10 +528,10 @@ export const initializeClientsFromSettings = async (
|
||||
transport,
|
||||
options: requestOptions,
|
||||
createTime: Date.now(),
|
||||
config: conf, // Store reference to original config
|
||||
config: expandedConf, // Store reference to expanded config
|
||||
};
|
||||
|
||||
const pendingAuth = conf.oauth?.pendingAuthorization;
|
||||
const pendingAuth = expandedConf.oauth?.pendingAuthorization;
|
||||
if (pendingAuth) {
|
||||
serverInfo.status = 'oauth_required';
|
||||
serverInfo.error = null;
|
||||
@@ -594,7 +599,7 @@ export const initializeClientsFromSettings = async (
|
||||
serverInfo.error = null;
|
||||
|
||||
// Set up keep-alive ping for SSE connections
|
||||
setupKeepAlive(serverInfo, conf);
|
||||
setupKeepAlive(serverInfo, expandedConf);
|
||||
} else {
|
||||
serverInfo.status = 'disconnected';
|
||||
serverInfo.error = `Failed to list data: ${dataError} `;
|
||||
|
||||
Reference in New Issue
Block a user