-
Notifications
You must be signed in to change notification settings - Fork 4
Log request/response bodies and added version #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,21 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| let method: String | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let path: String | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let remoteAddress: String? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let clientName: String? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func withClientName(_ name: String?) -> RequestLogContext { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| guard let name = name?.trimmingCharacters(in: .whitespacesAndNewlines), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !name.isEmpty else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return self | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return RequestLogContext( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: method, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: path, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remoteAddress: remoteAddress, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clientName: name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private struct State: Sendable { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,6 +43,7 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| private let config: ProxyConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private let sessionManager: any SessionManaging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private let logger: Logger = ProxyLogging.make("http") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private let maxLoggedBodyBytes = 4096 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| init(config: ProxyConfig, sessionManager: any SessionManaging) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.config = config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -107,7 +123,8 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: UUID().uuidString, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: head.method.rawValue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path: path, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remoteAddress: remoteAddressString(for: context.channel) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| remoteAddress: remoteAddressString(for: context.channel), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clientName: nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logRequest(requestLog) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -314,6 +331,10 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let clientName = clientName(from: head.headers, bodyData: bodyData) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let requestLog = requestLog.withClientName(clientName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logRequestBody(bodyData, requestLog: requestLog) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+334
to
+336
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let headerSessionId = HTTPRequestValidator.sessionId(from: head.headers) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let headerSessionExists = headerSessionId.map { sessionManager.hasSession(id: $0) } ?? false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -743,6 +764,7 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponseBody(data, requestLog: requestLog, status: .ok, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponse(requestLog, status: .ok, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -839,6 +861,9 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private func sendJSON(on channel: Channel, buffer: ByteBuffer, keepAlive: Bool, sessionId: String, requestLog: RequestLogContext) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let data = buffer.getData(at: buffer.readerIndex, length: buffer.readableBytes) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponseBody(data, requestLog: requestLog, status: .ok, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponse(requestLog, status: .ok, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MCPResponseEmitter.sendJSON( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -855,6 +880,7 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionId: String?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestLog: RequestLogContext | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponseBody(data, requestLog: requestLog, status: .ok, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponse(requestLog, status: .ok, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var buffer = channel.allocator.buffer(capacity: data.count) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| buffer.writeBytes(data) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -874,6 +900,7 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionId: String?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestLog: RequestLogContext | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponseBody(body, requestLog: requestLog, status: status, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logResponse(requestLog, status: status, sessionId: sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MCPResponseEmitter.sendPlain( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: channel, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1024,6 +1051,9 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let remote = request.remoteAddress { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["remote"] = .string(remote) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let clientName = request.clientName { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["client"] = .string(clientName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.info("HTTP request", metadata: metadata) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1037,12 +1067,115 @@ final class HTTPHandler: ChannelInboundHandler, Sendable { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let remote = request.remoteAddress { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["remote"] = .string(remote) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let clientName = request.clientName { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["client"] = .string(clientName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let sessionId { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["session"] = .string(sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.info("HTTP response", metadata: metadata) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private func logRequestBody(_ data: Data, requestLog: RequestLogContext) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logBody( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "HTTP request body", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: data, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestLog: requestLog, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: nil, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionId: nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private func logResponseBody(_ data: Data, requestLog: RequestLogContext, status: HTTPResponseStatus, sessionId: String?) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logBody( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "HTTP response body", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: data, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestLog: requestLog, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionId: sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private func logResponseBody(_ body: String, requestLog: RequestLogContext, status: HTTPResponseStatus, sessionId: String?) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let data = Data(body.utf8) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logBody( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: "HTTP response body", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: data, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestLog: requestLog, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: status, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionId: sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private func logBody( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| label: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: Data, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestLog: RequestLogContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| status: HTTPResponseStatus?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sessionId: String? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var metadata: Logger.Metadata = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": .string(requestLog.id), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "method": .string(requestLog.method), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "path": .string(requestLog.path), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "bytes": .string("\(data.count)"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let remote = requestLog.remoteAddress { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["remote"] = .string(remote) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let clientName = requestLog.clientName { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["client"] = .string(clientName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let status { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["status"] = .string("\(status.code)") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let sessionId { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["session"] = .string(sessionId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let previewData = data.prefix(maxLoggedBodyBytes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let previewText = String(decoding: previewData, as: UTF8.self) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["preview"] = .string(previewText) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if data.count > maxLoggedBodyBytes { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata["truncated"] = .string("true") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.info(Logger.Message(stringLiteral: label), metadata: metadata) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger.info(Logger.Message(stringLiteral: label), metadata: metadata) | |
| logger.debug(Logger.Message(stringLiteral: label), metadata: metadata) |
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logBody unconditionally logs a 4KB preview of every HTTP request and response body (metadata["preview"]) at info level, along with byte length, path, and optional session/client identifiers. These bodies can easily contain sensitive material (e.g., API keys, access tokens, secrets in payloads, or proprietary code and prompts), so anyone with access to logs or a multi-tenant logging backend could recover that data. Consider restricting full body logging to an opt-in debug mode, redacting or hashing sensitive fields, and/or ensuring logs never contain authentication secrets or other high-sensitivity content.
| let previewData = data.prefix(maxLoggedBodyBytes) | |
| let previewText = String(decoding: previewData, as: UTF8.self) | |
| metadata["preview"] = .string(previewText) | |
| if data.count > maxLoggedBodyBytes { | |
| metadata["truncated"] = .string("true") | |
| } | |
| logger.info(Logger.Message(stringLiteral: label), metadata: metadata) | |
| // Always log high-level request/response information at info level, | |
| // but do not include body content to avoid leaking sensitive data. | |
| logger.info(Logger.Message(stringLiteral: label), metadata: metadata) | |
| // Only log body previews in debug/loopback scenarios to reduce | |
| // the risk of exposing sensitive information in logs. | |
| if isLoopbackDebugEndpointEnabled || logger.logLevel <= .debug { | |
| var debugMetadata = metadata | |
| let previewData = data.prefix(maxLoggedBodyBytes) | |
| let previewText = String(decoding: previewData, as: UTF8.self) | |
| debugMetadata["preview"] = .string(previewText) | |
| if data.count > maxLoggedBodyBytes { | |
| debugMetadata["truncated"] = .string("true") | |
| } | |
| logger.debug(Logger.Message(stringLiteral: label), metadata: debugMetadata) | |
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| public enum Version { | ||
| public static let current = "0.4.1" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ package struct CLICommandLogSink { | |
|
|
||
| package struct CLICommandInvocation { | ||
| package var showHelp = false | ||
| package var showVersion = false | ||
| package var usesRemovedURLHelper = false | ||
| package var hasExplicitURL = false | ||
| package var hasStdioFlag = false | ||
|
|
@@ -93,6 +94,11 @@ package struct XcodeMCPProxyCLICommand { | |
| return 0 | ||
| } | ||
|
|
||
| if invocation.showVersion { | ||
| dependencies.stdout("xcode-mcp-proxy \(Version.current)") | ||
| return 0 | ||
| } | ||
|
Comment on lines
+97
to
+100
|
||
|
|
||
| if invocation.usesRemovedURLHelper { | ||
| logSink.error( | ||
| "url helper mode was removed; configure your HTTP client with a concrete URL (default: http://localhost:8765/mcp)." | ||
|
|
@@ -158,6 +164,9 @@ package struct XcodeMCPProxyCLICommand { | |
| case "-h", "--help": | ||
| invocation.showHelp = true | ||
| index += 1 | ||
| case "--version": | ||
| invocation.showVersion = true | ||
| index += 1 | ||
| case "url" where index == 1: | ||
| invocation.usesRemovedURLHelper = true | ||
| index += 1 | ||
|
|
@@ -259,6 +268,7 @@ package struct XcodeMCPProxyCLICommand { | |
| Options: | ||
| --request-timeout seconds Request timeout (default: 300, 0 disables) | ||
| --url url Explicit upstream URL (default: env/discovery/http://localhost:8765/mcp) | ||
| --version Show version | ||
| -h, --help Show help | ||
|
|
||
| Environment: | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ extension ProxyServer: ProxyServerCommandServer {} | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package struct ProxyServerOptions { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package var forwardedArgs: [String] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package var showHelp: Bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package var showVersion: Bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package var hasListenFlag: Bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package var hasHostFlag: Bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package var hasPortFlag: Bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -18,6 +19,7 @@ package struct ProxyServerOptions { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package init( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| forwardedArgs: [String], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| showHelp: Bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| showHelp: Bool, | |
| showHelp: Bool, | |
| hasListenFlag: Bool, | |
| hasHostFlag: Bool, | |
| hasPortFlag: Bool, | |
| hasXcodePidFlag: Bool, | |
| hasLazyInitFlag: Bool, | |
| forceRestart: Bool, | |
| dryRun: Bool | |
| ) { | |
| self.init( | |
| forwardedArgs: forwardedArgs, | |
| showHelp: showHelp, | |
| showVersion: false, | |
| hasListenFlag: hasListenFlag, | |
| hasHostFlag: hasHostFlag, | |
| hasPortFlag: hasPortFlag, | |
| hasXcodePidFlag: hasXcodePidFlag, | |
| hasLazyInitFlag: hasLazyInitFlag, | |
| forceRestart: forceRestart, | |
| dryRun: dryRun | |
| ) | |
| } | |
| package init( | |
| forwardedArgs: [String], | |
| showHelp: Bool, |
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New --version behavior isn't covered by tests. There are already comprehensive command tests in Tests/XcodeMCPProxyTests/ServerCommandTests.swift; please add coverage asserting --version prints the version string and exits 0 (and that it doesn't attempt to start the server).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The initial "HTTP request" log is emitted before
clientNameis extracted, so it will never include the client name (even when present in theMcp-Client-Nameheader). Consider populatingclientNamefrom headers inhandleRequestbefore callinglogRequest, and only falling back to body parsing for POST requests if needed.