diff --git a/src/shared/config.ts b/src/shared/config.ts index 7073f58..2e72bae 100644 --- a/src/shared/config.ts +++ b/src/shared/config.ts @@ -144,9 +144,7 @@ export async function getMcpConfigForManifest( // First, add defaults from manifest if (manifest.user_config) { for (const [key, configOption] of Object.entries(manifest.user_config)) { - if (configOption.default !== undefined) { - mergedConfig[key] = configOption.default; - } + mergedConfig[key] = configOption.default ?? ""; } } @@ -159,16 +157,17 @@ export async function getMcpConfigForManifest( for (const [key, value] of Object.entries(mergedConfig)) { // Convert user config to the format expected by variable substitution const userConfigKey = `user_config.${key}`; + const substitutedValue = replaceVariables(value ?? "", variables); - if (Array.isArray(value)) { + if (Array.isArray(substitutedValue)) { // Keep arrays as arrays for proper expansion - variables[userConfigKey] = value.map(String); - } else if (typeof value === "boolean") { + variables[userConfigKey] = substitutedValue.map(String); + } else if (typeof substitutedValue === "boolean") { // Convert booleans to "true"/"false" strings as per spec - variables[userConfigKey] = value ? "true" : "false"; + variables[userConfigKey] = substitutedValue ? "true" : "false"; } else { // Convert other types to strings - variables[userConfigKey] = String(value); + variables[userConfigKey] = String(substitutedValue); } } diff --git a/test/config.test.ts b/test/config.test.ts index 7e29f69..a39304a 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -349,6 +349,73 @@ describe("getMcpConfigForManifest", () => { expect(result?.args).toEqual(["server.js", "--verbose=true"]); }); + + it("should replace system variables inside user config defaults", async () => { + const manifest: McpbManifestAny = { + ...baseManifest, + user_config: { + server_path: { + type: "file", + title: "Server Path", + description: "Path to the server binary", + default: "${HOME}/bin/server", + }, + }, + server: { + type: "binary", + entry_point: "${user_config.server_path}", + mcp_config: { + command: "${user_config.server_path}", + }, + }, + }; + + const result = await getMcpConfigForManifest({ + manifest, + extensionPath: "/ext/path", + systemDirs: { ...mockSystemDirs, HOME: "/home/user" }, + userConfig: {}, + pathSeparator: "/", + logger: mockLogger, + }); + + expect(result?.command).toBe("/home/user/bin/server"); + }); + + it("should substitute missing optional user config values with empty strings", async () => { + const manifest: McpbManifestAny = { + ...baseManifest, + user_config: { + azure_tenant_id: { + type: "string", + title: "Entra Tenant ID", + description: "Optional tenant id", + }, + }, + server: { + type: "node", + entry_point: "server.js", + mcp_config: { + command: "node", + args: ["server.js"], + env: { + AZURE_TENANT_ID: "${user_config.azure_tenant_id}", + }, + }, + }, + }; + + const result = await getMcpConfigForManifest({ + manifest, + extensionPath: "/ext/path", + systemDirs: mockSystemDirs, + userConfig: {}, + pathSeparator: "/", + logger: mockLogger, + }); + + expect(result?.env?.AZURE_TENANT_ID).toBe(""); + }); }); describe("hasRequiredConfigMissing", () => {