Skip to content
This repository was archived by the owner on May 29, 2025. It is now read-only.

Commit 24e83c1

Browse files
Fixing issues for env variables (#17)
* Fixing issues for env variables * PR review * merging user provided env variables * lint * Update MCPClient/Sources/stdioTransport/DataChannel+StdioProcess.swift Co-authored-by: Guillaume Sabran <[email protected]> * Swift format * PR review * package resolutions on demo --------- Co-authored-by: Guillaume Sabran <[email protected]>
1 parent 102b4d7 commit 24e83c1

File tree

3 files changed

+49
-26
lines changed

3 files changed

+49
-26
lines changed

MCPClient/Sources/stdioTransport/DataChannel+StdioProcess.swift

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import Foundation
32
import JSONRPC
43
import MCPInterface
@@ -76,13 +75,15 @@ extension Transport {
7675
let process = Process()
7776
// In MacOS, zsh is the default since macOS Catalina 10.15.7. We can safely assume it is available.
7877
process.launchPath = "/bin/zsh"
78+
7979
if let executable = path(for: executable, env: env) {
80+
// If executable is found directly, use user-provided env or current process env
81+
process.environment = env ?? ProcessInfo.processInfo.environment
8082
let command = "\(executable) \(args.joined(separator: " "))"
8183
process.arguments = ["-c"] + [command]
82-
process.environment = env ?? ProcessInfo.processInfo.environment
8384
} else {
84-
// If we cannot locate the executable, try loading the default environment for zsh, as the current process might not have the correct PATH.
85-
process.environment = try loadZshEnvironment()
85+
// If we cannot locate the executable, try loading the default environment for zsh
86+
process.environment = try loadZshEnvironment(userEnv: env)
8687
let command = "\(executable) \(args.joined(separator: " "))"
8788
process.arguments = ["-c"] + [command]
8889
}
@@ -208,19 +209,41 @@ extension Transport {
208209
return executablePath
209210
}
210211

211-
private static func loadZshEnvironment() throws -> [String: String] {
212-
let process = Process()
213-
process.launchPath = "/bin/zsh"
214-
// Those are loaded for interactive login shell by zsh:
215-
// https://www.freecodecamp.org/news/how-do-zsh-configuration-files-work/
216-
process.arguments = ["-c", "source ~/.zshenv; source ~/.zprofile; source ~/.zshrc; source ~/.zshrc; printenv"]
217-
let env = try getProcessStdout(process: process)
212+
private static func loadZshEnvironment(userEnv: [String: String]? = nil) throws -> [String: String] {
213+
// Load shell environment as base
214+
let shellProcess = Process()
215+
shellProcess.executableURL = URL(fileURLWithPath: "/bin/zsh")
218216

219-
if let path = env?.split(separator: "\n").filter({ $0.starts(with: "PATH=") }).last {
220-
return ["PATH": String(path.dropFirst("PATH=".count))]
217+
// Set process environment - either use userEnv if it exists and isn't empty, or use system environment
218+
if let env = userEnv, !env.isEmpty {
219+
shellProcess.environment = env
221220
} else {
221+
shellProcess.environment = ProcessInfo.processInfo.environment
222+
}
223+
224+
shellProcess.arguments = ["-ilc", "printenv"]
225+
226+
let outputPipe = Pipe()
227+
shellProcess.standardOutput = outputPipe
228+
shellProcess.standardError = Pipe()
229+
230+
try shellProcess.run()
231+
shellProcess.waitUntilExit()
232+
233+
let data = outputPipe.fileHandleForReading.readDataToEndOfFile()
234+
guard let outputString = String(data: data, encoding: .utf8) else {
235+
logger.error("Failed to read environment from shell.")
222236
return ProcessInfo.processInfo.environment
223237
}
238+
239+
// Parse shell environment
240+
return outputString
241+
.split(separator: "\n")
242+
.reduce(into: [String: String]()) { result, line in
243+
let components = line.split(separator: "=", maxSplits: 1)
244+
guard components.count == 2 else { return }
245+
result[String(components[0])] = String(components[1])
246+
}
224247
}
225248

226249
private static func getProcessStdout(process: Process) throws -> String? {

MCPClientChatDemo/MCPClientChat/MCPClientChat.xcodeproj/project.pbxproj

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
7B73E9F52D814ABE00BF0CD1 /* MCPClient in Frameworks */ = {isa = PBXBuildFile; productRef = 7B73E9F42D814ABE00BF0CD1 /* MCPClient */; };
1011
7BDD62DB2D764B3D00E18088 /* SwiftAnthropic in Frameworks */ = {isa = PBXBuildFile; productRef = 7BDD62DA2D764B3D00E18088 /* SwiftAnthropic */; };
11-
7BDD62ED2D764B7C00E18088 /* MCPClient in Frameworks */ = {isa = PBXBuildFile; productRef = 7BDD62EC2D764B7C00E18088 /* MCPClient */; };
1212
7BDD644F2D7811BA00E18088 /* SwiftOpenAI in Frameworks */ = {isa = PBXBuildFile; productRef = 7BDD644E2D7811BA00E18088 /* SwiftOpenAI */; };
1313
/* End PBXBuildFile section */
1414

@@ -58,9 +58,9 @@
5858
isa = PBXFrameworksBuildPhase;
5959
buildActionMask = 2147483647;
6060
files = (
61-
7BDD62ED2D764B7C00E18088 /* MCPClient in Frameworks */,
6261
7BDD644F2D7811BA00E18088 /* SwiftOpenAI in Frameworks */,
6362
7BDD62DB2D764B3D00E18088 /* SwiftAnthropic in Frameworks */,
63+
7B73E9F52D814ABE00BF0CD1 /* MCPClient in Frameworks */,
6464
);
6565
runOnlyForDeploymentPostprocessing = 0;
6666
};
@@ -130,8 +130,8 @@
130130
name = MCPClientChat;
131131
packageProductDependencies = (
132132
7BDD62DA2D764B3D00E18088 /* SwiftAnthropic */,
133-
7BDD62EC2D764B7C00E18088 /* MCPClient */,
134133
7BDD644E2D7811BA00E18088 /* SwiftOpenAI */,
134+
7B73E9F42D814ABE00BF0CD1 /* MCPClient */,
135135
);
136136
productName = MCPClientChat;
137137
productReference = 7BDD62A92D764A4A00E18088 /* MCPClientChat.app */;
@@ -217,8 +217,8 @@
217217
minimizedProjectReferenceProxies = 1;
218218
packageReferences = (
219219
7BDD62D72D764ADA00E18088 /* XCRemoteSwiftPackageReference "SwiftAnthropic" */,
220-
7BDD62D82D764B1800E18088 /* XCLocalSwiftPackageReference "../../../mcp-swift-sdk" */,
221220
7BDD644D2D7811B300E18088 /* XCRemoteSwiftPackageReference "SwiftOpenAI" */,
221+
7B73E9F32D814ABE00BF0CD1 /* XCLocalSwiftPackageReference "../../../mcp-swift-sdk" */,
222222
);
223223
preferredProjectObjectVersion = 77;
224224
productRefGroup = 7BDD62AA2D764A4A00E18088 /* Products */;
@@ -576,7 +576,7 @@
576576
/* End XCConfigurationList section */
577577

578578
/* Begin XCLocalSwiftPackageReference section */
579-
7BDD62D82D764B1800E18088 /* XCLocalSwiftPackageReference "../../../mcp-swift-sdk" */ = {
579+
7B73E9F32D814ABE00BF0CD1 /* XCLocalSwiftPackageReference "../../../mcp-swift-sdk" */ = {
580580
isa = XCLocalSwiftPackageReference;
581581
relativePath = "../../../mcp-swift-sdk";
582582
};
@@ -602,16 +602,15 @@
602602
/* End XCRemoteSwiftPackageReference section */
603603

604604
/* Begin XCSwiftPackageProductDependency section */
605+
7B73E9F42D814ABE00BF0CD1 /* MCPClient */ = {
606+
isa = XCSwiftPackageProductDependency;
607+
productName = MCPClient;
608+
};
605609
7BDD62DA2D764B3D00E18088 /* SwiftAnthropic */ = {
606610
isa = XCSwiftPackageProductDependency;
607611
package = 7BDD62D72D764ADA00E18088 /* XCRemoteSwiftPackageReference "SwiftAnthropic" */;
608612
productName = SwiftAnthropic;
609613
};
610-
7BDD62EC2D764B7C00E18088 /* MCPClient */ = {
611-
isa = XCSwiftPackageProductDependency;
612-
package = 7BDD62D82D764B1800E18088 /* XCLocalSwiftPackageReference "../../../mcp-swift-sdk" */;
613-
productName = MCPClient;
614-
};
615614
7BDD644E2D7811BA00E18088 /* SwiftOpenAI */ = {
616615
isa = XCSwiftPackageProductDependency;
617616
package = 7BDD644D2D7811B300E18088 /* XCRemoteSwiftPackageReference "SwiftOpenAI" */;

MCPClientChatDemo/MCPClientChat/MCPClientChat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)