Skip to content

Commit 2c75a50

Browse files
committed
Checkpoint, works for mac
1 parent 4ef02e0 commit 2c75a50

File tree

2 files changed

+103
-71
lines changed

2 files changed

+103
-71
lines changed

Tests/SwiftMCPTests/Client/MCPClient.swift

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import Foundation
22
import AnyCodable
33
@testable import SwiftMCP
4-
import Logging
5-
6-
// Configure logging to use stdout
7-
private let logger = Logger(label: "com.cocoanetics.SwiftMCP.MCPClient") { label in
8-
StreamLogHandler.standardOutput(label: label)
9-
}
104

115
/// A client for communicating with an MCP server over HTTP+SSE
126
public actor MCPClient {
@@ -20,21 +14,19 @@ public actor MCPClient {
2014
/// - Parameter endpointURL: The URL of the SSE endpoint (e.g. http://localhost:8080/sse)
2115
public init(endpointURL: URL) {
2216
self.endpointURL = endpointURL
23-
logger.info("Initialized MCPClient with endpoint: \(endpointURL)")
2417
}
2518

2619
/// Connect to the SSE endpoint and establish a connection
2720
/// - Throws: An error if the connection fails
2821
public func connect() async throws {
29-
logger.info("Connecting to SSE endpoint: \(endpointURL)")
3022
var request = URLRequest(url: endpointURL)
3123
request.setValue("text/event-stream", forHTTPHeaderField: "Accept")
24+
request.timeoutInterval = 10 // Add a 10 second timeout for the initial connection
3225

3326
let (stream, response) = try await URLSession.shared.bytes(for: request)
3427
guard let httpResponse = response as? HTTPURLResponse else {
3528
throw MCPError.invalidResponse
3629
}
37-
logger.info("Connected to SSE endpoint, status: \(httpResponse.statusCode)")
3830
self.stream = stream
3931

4032
// Create a single message stream for all messages
@@ -47,17 +39,13 @@ public actor MCPClient {
4739
}
4840

4941
// Wait for the initial message containing the messages endpoint URL
50-
logger.info("Waiting for initial message with endpoint URL")
51-
let initialMessage = try await nextMessage(timeout: 5)
52-
logger.info("Received initial message: \(initialMessage.data)")
42+
let initialMessage = try await nextMessage(timeout: 10) // Increase timeout to 10 seconds
5343

5444
guard let messagesURL = URL(string: initialMessage.data),
5545
messagesURL.path.contains("/messages/") else {
56-
logger.error("Invalid messages endpoint URL: \(initialMessage.data)")
5746
throw MCPError.invalidEndpointURL
5847
}
5948
self.messagesURL = messagesURL
60-
logger.info("Successfully connected with messages URL: \(messagesURL)")
6149
}
6250

6351
deinit {
@@ -72,11 +60,9 @@ public actor MCPClient {
7260
/// - Throws: An error if the request fails or times out
7361
public func send(_ message: JSONRPCMessage, timeout: TimeInterval = 5) async throws -> JSONRPCMessage {
7462
guard let messagesURL = messagesURL else {
75-
logger.error("Not connected to server")
7663
throw MCPError.notConnected
7764
}
7865

79-
logger.info("Sending JSONRPC message to: \(messagesURL)")
8066
var request = URLRequest(url: messagesURL)
8167
request.httpMethod = "POST"
8268
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
@@ -89,14 +75,11 @@ public actor MCPClient {
8975

9076
guard let httpResponse = response as? HTTPURLResponse,
9177
httpResponse.statusCode == 202 else {
92-
logger.error("Invalid response from server")
9378
throw MCPError.invalidResponse
9479
}
9580

96-
logger.info("Request accepted, waiting for SSE response")
9781
// Wait for the SSE response
9882
let responseMessage = try await nextMessage(timeout: timeout)
99-
logger.info("Received SSE response")
10083

10184
let jsonData = responseMessage.data.data(using: .utf8)!
10285
return try JSONDecoder().decode(JSONRPCMessage.self, from: jsonData)
@@ -118,7 +101,6 @@ public actor MCPClient {
118101
/// - timeout: Timeout in seconds
119102
/// - Returns: The next SSE message
120103
private func nextMessage(from stream: AsyncStream<SSEMessage>, timeout: TimeInterval) async throws -> SSEMessage {
121-
logger.info("Waiting for next message with timeout: \(timeout)s")
122104
return try await withThrowingTaskGroup(of: SSEMessage.self) { group in
123105
// Task to read the next message from the stream
124106
group.addTask {
@@ -144,7 +126,6 @@ public actor MCPClient {
144126

145127
private func readMessages(continuation: AsyncStream<SSEMessage>.Continuation) async {
146128
guard let stream = stream else {
147-
logger.error("No stream available, finishing continuation")
148129
continuation.finish()
149130
return
150131
}
@@ -153,12 +134,9 @@ public actor MCPClient {
153134
var currentEvent: String?
154135

155136
for try await line in stream.lines {
156-
logger.info("Received line: \(line)")
157-
158137
if line.hasPrefix("data: ") {
159138
// Handle data lines immediately
160139
let data = String(line.dropFirst(6)) // Remove "data: " prefix
161-
logger.info("Found data line: \(data)")
162140

163141
// Format the complete SSE message
164142
var messageText = ""
@@ -168,10 +146,7 @@ public actor MCPClient {
168146
messageText += "data: \(data)\n"
169147

170148
if let message = SSEMessage(messageText) {
171-
logger.info("Successfully parsed SSE message with data: \(message.data)")
172149
continuation.yield(message)
173-
} else {
174-
logger.error("Failed to parse SSE message: \(messageText)")
175150
}
176151

177152
// Reset for next message
@@ -180,17 +155,12 @@ public actor MCPClient {
180155
// Handle event lines
181156
let event = String(line.dropFirst(7)) // Remove "event: " prefix
182157
currentEvent = event
183-
logger.info("Found event line: \(event)")
184-
} else if !line.isEmpty {
185-
// Skip non-empty lines that aren't SSE format
186-
logger.info("Skipping line: \(line)")
187158
}
188159
}
189160
} catch {
190-
logger.error("Error reading stream: \(error)")
161+
// No logging needed for errors
191162
}
192163

193-
logger.info("Stream ended, finishing continuation")
194164
continuation.finish()
195165
}
196166
}

0 commit comments

Comments
 (0)