diff --git a/Sources/MCP/Server/Server.swift b/Sources/MCP/Server/Server.swift index 060b51f8..a7630cfd 100644 --- a/Sources/MCP/Server/Server.swift +++ b/Sources/MCP/Server/Server.swift @@ -925,10 +925,6 @@ public actor Server { throw MCPError.internalError("Server was deallocated") } - guard await !self.isInitialized else { - throw MCPError.invalidRequest("Server is already initialized") - } - // Call initialization hook if registered if let hook = initializeHook { try await hook(params.clientInfo, params.capabilities) diff --git a/Tests/MCPTests/ServerTests.swift b/Tests/MCPTests/ServerTests.swift index a7b61667..9d8f71b1 100644 --- a/Tests/MCPTests/ServerTests.swift +++ b/Tests/MCPTests/ServerTests.swift @@ -53,6 +53,53 @@ struct ServerTests { await transport.disconnect() } + @Test("Repeated initialize requests are idempotent") + func testServerHandleRepeatedInitialize() async throws { + let transport = MockTransport() + + let server = Server(name: "TestServer", version: "1.0") + try await server.start(transport: transport) + + // Wait for server to start receiving + try await Task.sleep(for: .milliseconds(10)) + + // First initialize request + try await transport.queue( + request: Initialize.request( + .init( + protocolVersion: Version.latest, + capabilities: .init(), + clientInfo: .init(name: "TestClient", version: "1.0") + ) + )) + + try await Task.sleep(for: .milliseconds(200)) + + // Second initialize request on the same server connection should + // succeed rather than returning a "Server is already initialized" error + try await transport.queue( + request: Initialize.request( + .init( + protocolVersion: Version.latest, + capabilities: .init(), + clientInfo: .init(name: "TestClient", version: "1.0") + ) + )) + + try await Task.sleep(for: .milliseconds(200)) + + let messages = await transport.sentMessages + #expect(messages.count == 2) + for response in messages { + #expect(response.contains("serverInfo")) + #expect(!response.contains("\"error\"")) + #expect(!response.contains("already initialized")) + } + + await server.stop() + await transport.disconnect() + } + @Test("Initialize hook - successful") func testInitializeHookSuccess() async throws { let transport = MockTransport() @@ -101,6 +148,44 @@ struct ServerTests { await transport.disconnect() } + @Test("Initialize hook runs again on repeated initialize") + func testInitializeHookCalledOnRepeatedInitialize() async throws { + let transport = MockTransport() + + actor TestState { + var callCount = 0 + func increment() { callCount += 1 } + func count() -> Int { callCount } + } + + let state = TestState() + let server = Server(name: "TestServer", version: "1.0") + + try await server.start(transport: transport) { _, _ in + await state.increment() + } + + try await Task.sleep(for: .milliseconds(10)) + + for _ in 0..<2 { + try await transport.queue( + request: Initialize.request( + .init( + protocolVersion: Version.latest, + capabilities: .init(), + clientInfo: .init(name: "TestClient", version: "1.0") + ) + )) + try await Task.sleep(for: .milliseconds(200)) + } + + #expect(await state.count() == 2) + #expect(await transport.sentMessages.count == 2) + + await server.stop() + await transport.disconnect() + } + @Test("Initialize hook - rejection") func testInitializeHookRejection() async throws { let transport = MockTransport()