fix(skill-mcp): always inherit process.env for MCP servers
- Always merge parent process.env when spawning MCP child processes - Overlay config.env on top if present (for skill-specific overrides) - Fixes issue where skills without explicit env: block started with zero environment variables - Adds 2 tests for env inheritance behavior 🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -106,4 +106,54 @@ describe("SkillMcpManager", () => {
|
|||||||
expect(manager.getConnectedServers()).toEqual([])
|
expect(manager.getConnectedServers()).toEqual([])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("environment variable handling", () => {
|
||||||
|
it("always inherits process.env even when config.env is undefined", async () => {
|
||||||
|
// #given
|
||||||
|
const info: SkillMcpClientInfo = {
|
||||||
|
serverName: "test-server",
|
||||||
|
skillName: "test-skill",
|
||||||
|
sessionID: "session-1",
|
||||||
|
}
|
||||||
|
const configWithoutEnv: ClaudeCodeMcpServer = {
|
||||||
|
command: "node",
|
||||||
|
args: ["-e", "process.exit(0)"],
|
||||||
|
}
|
||||||
|
|
||||||
|
// #when - attempt connection (will fail but exercises env merging code path)
|
||||||
|
// #then - should not throw "undefined" related errors for env
|
||||||
|
try {
|
||||||
|
await manager.getOrCreateClient(info, configWithoutEnv)
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
|
expect(message).not.toContain("env")
|
||||||
|
expect(message).not.toContain("undefined")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("overlays config.env on top of inherited process.env", async () => {
|
||||||
|
// #given
|
||||||
|
const info: SkillMcpClientInfo = {
|
||||||
|
serverName: "test-server",
|
||||||
|
skillName: "test-skill",
|
||||||
|
sessionID: "session-2",
|
||||||
|
}
|
||||||
|
const configWithEnv: ClaudeCodeMcpServer = {
|
||||||
|
command: "node",
|
||||||
|
args: ["-e", "process.exit(0)"],
|
||||||
|
env: {
|
||||||
|
CUSTOM_VAR: "custom_value",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// #when - attempt connection
|
||||||
|
// #then - should not throw, env merging should work
|
||||||
|
try {
|
||||||
|
await manager.getOrCreateClient(info, configWithEnv)
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error)
|
||||||
|
expect(message).toContain("Failed to connect")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -55,18 +55,20 @@ export class SkillMcpManager {
|
|||||||
const command = config.command
|
const command = config.command
|
||||||
const args = config.args || []
|
const args = config.args || []
|
||||||
|
|
||||||
|
// Always inherit parent process environment
|
||||||
const mergedEnv: Record<string, string> = {}
|
const mergedEnv: Record<string, string> = {}
|
||||||
if (config.env) {
|
|
||||||
for (const [key, value] of Object.entries(process.env)) {
|
for (const [key, value] of Object.entries(process.env)) {
|
||||||
if (value !== undefined) mergedEnv[key] = value
|
if (value !== undefined) mergedEnv[key] = value
|
||||||
}
|
}
|
||||||
|
// Overlay with skill-specific env vars if present
|
||||||
|
if (config.env) {
|
||||||
Object.assign(mergedEnv, config.env)
|
Object.assign(mergedEnv, config.env)
|
||||||
}
|
}
|
||||||
|
|
||||||
const transport = new StdioClientTransport({
|
const transport = new StdioClientTransport({
|
||||||
command,
|
command,
|
||||||
args,
|
args,
|
||||||
env: config.env ? mergedEnv : undefined,
|
env: mergedEnv,
|
||||||
})
|
})
|
||||||
|
|
||||||
const client = new Client(
|
const client = new Client(
|
||||||
|
|||||||
Reference in New Issue
Block a user