import Logging import SystemPackage import struct Foundation.Data import struct Foundation.Date import class Foundation.JSONDecoder import class Foundation.JSONEncoder /// Model Context Protocol server public actor Server { /// The server configuration public struct Configuration: Hashable, Codable, Sendable { /// The default configuration. public static let `default` = Configuration(strict: false) /// The strict configuration. public static let strict = Configuration(strict: true) /// When strict mode is enabled, the server: /// - Requires clients to send an initialize request before any other requests /// - Rejects all requests from uninitialized clients with a protocol error /// /// While the MCP specification requires clients to initialize the connection /// before sending other requests, some implementations may not follow this. /// Disabling strict mode allows the server to be more lenient with non-compliant /// clients, though this may lead to undefined behavior. public var strict: Bool } /// Implementation information public struct Info: Hashable, Codable, Sendable { /// The server name public let name: String /// The server version public let version: String public init(name: String, version: String) { self.name = name self.version = version } } /// Server capabilities public struct Capabilities: Hashable, Codable, Sendable { /// Resources capabilities public struct Resources: Hashable, Codable, Sendable { /// Whether the list of resources has changed public var list: Bool? /// Whether the resource can be read public var read: Bool? /// Whether the resource can be subscribed to public var subscribe: Bool? /// Whether the list of resources has changed public var listChanged: Bool? public init( list: Bool? = nil, read: Bool? = nil, subscribe: Bool? = nil, listChanged: Bool? = nil ) { self.list = list self.read = read self.subscribe = subscribe self.listChanged = listChanged } } /// Tools capabilities public struct Tools: Hashable, Codable, Sendable { /// Whether the server notifies clients when tools change public var listChanged: Bool? public init(listChanged: Bool? = nil) { self.listChanged = listChanged } } /// Prompts capabilities public struct Prompts: Hashable, Codable, Sendable { /// Whether the server notifies clients when prompts change public var listChanged: Bool? public init(listChanged: Bool? = nil) { self.listChanged = listChanged } } /// Logging capabilities public struct Logging: Hashable, Codable, Sendable { public init() {} } /// Logging capabilities public var logging: Logging? /// Prompts capabilities public var prompts: Prompts? /// Resources capabilities public var resources: Resources? /// Tools capabilities public var tools: Tools? public init( logging: Logging? = nil, prompts: Prompts? = nil, resources: Resources? = nil, tools: Tools? = nil ) { self.logging = logging self.prompts = prompts self.resources = resources self.tools = tools } } /// Server information private let serverInfo: Server.Info /// The server connection private var connection: (any Transport)? /// The server logger private var logger: Logger? { get async { await connection?.logger } } /// The server name public nonisolated var name: String { serverInfo.name } /// The server version public nonisolated var version: String { serverInfo.version } /// The server capabilities public var capabilities: Capabilities /// The server configuration public var configuration: Configuration /// Request handlers private var methodHandlers: [String: RequestHandlerBox] = [:] /// Notification handlers private var notificationHandlers: [String: [NotificationHandlerBox]] = [:] /// Whether the server is initialized private var isInitialized = false /// The client information private var clientInfo: Client.Info? /// The client capabilities private var clientCapabilities: Client.Capabilities? /// The protocol version private var protocolVersion: String? /// The list of subscriptions private var subscriptions: [String: Set] = [:] /// The task for the message handling loop private var task: Task? public init( name: String, version: String, capabilities: Server.Capabilities = .init(), configuration: Configuration = .default ) { self.serverInfo = Server.Info(name: name, version: version) self.capabilities = capabilities self.configuration = configuration } /// Start the server public func start(transport: any Transport) async throws { self.connection = transport registerDefaultHandlers() try await transport.connect() await logger?.info( "Server started", metadata: ["name": "\(name)", "version": "\(version)"]) // Start message handling loop task = Task { do { let stream = await transport.receive() for try await string in stream { if Task.isCancelled { break } // Check cancellation inside loop var requestID: ID? do { guard let data = string.data(using: .utf8) else { throw Error.parseError("Invalid UTF-8 data") } // Attempt to decode string data as AnyRequest or AnyMessage let decoder = JSONDecoder() if let request = try? decoder.decode(AnyRequest.self, from: data) { try await handleRequest(request) } else if let message = try? decoder.decode(AnyMessage.self, from: data) { try await handleMessage(message) } else { // Try to extract request ID from raw JSON if possible if let json = try? JSONDecoder().decode( [String: Value].self, from: data), let idValue = json["id"] { if let strValue = idValue.stringValue { requestID = .string(strValue) } else if let intValue = idValue.intValue { requestID = .number(intValue) } } throw Error.parseError("Invalid message format") } } catch let error as Errno where error == .resourceTemporarilyUnavailable { // Resource temporarily unavailable, retry after a short delay try? await Task.sleep(nanoseconds: 10_000_000) // 10ms continue } catch { await logger?.error( "Error processing message", metadata: ["error": "\(error)"]) let response = AnyMethod.response( id: requestID ?? .random, error: error as? Error ?? Error.internalError(error.localizedDescription) ) try? await send(response) } } } catch { await logger?.error( "Fatal error in message handling loop", metadata: ["error": "\(error)"]) } } } /// Stop the server public func stop() async { task?.cancel() task = nil if let connection = connection { await connection.disconnect() } connection = nil } // MARK: - Registration /// Register a method handler @discardableResult public func withMethodHandler( _ type: M.Type, handler: @escaping @Sendable (M.Parameters) async throws -> M.Result ) -> Self { methodHandlers[M.name] = TypedRequestHandler { (request: Request) -> Response in let result = try await handler(request.params) return Response(id: request.id, result: result) } return self } /// Register a notification handler @discardableResult public func onNotification( _ type: N.Type, handler: @escaping @Sendable (Message) async throws -> Void ) -> Self { let handlers = notificationHandlers[N.name, default: []] notificationHandlers[N.name] = handlers + [TypedNotificationHandler(handler)] return self } // MARK: - Sending /// Send a response to a client public func send(_ response: Response) async throws { guard let connection = connection else { throw Error.internalError("Server connection not initialized") } let responseData = try JSONEncoder().encode(response) if let responseStr = String(data: responseData, encoding: .utf8) { try await connection.send(responseStr) } } /// Send a notification to connected clients public func notify(_ notification: Message) async throws { guard let connection = connection else { throw Error.internalError("Server connection not initialized") } let notificationData = try JSONEncoder().encode(notification) if let notificationStr = String(data: notificationData, encoding: .utf8) { try await connection.send(notificationStr) } } // MARK: - private func handleRequest(_ request: Request) async throws { await logger?.debug( "Processing request", metadata: [ "method": "\(request.method)", "id": "\(request.id)", ]) if configuration.strict { // The client SHOULD NOT send requests other than pings // before the server has responded to the initialize request. switch request.method { case Initialize.name, Ping.name: break default: try checkInitialized() } } // Find handler for method name guard let handler = methodHandlers[request.method] else { let error = Error.methodNotFound("Unknown method: \(request.method)") let response = AnyMethod.response(id: request.id, error: error) try await send(response) throw error } do { // Handle request and get response let response = try await handler(request) try await send(response) } catch { let mcpError = error as? Error ?? Error.internalError(error.localizedDescription) let response = AnyMethod.response(id: request.id, error: mcpError) try await send(response) throw error } } private func handleMessage(_ message: Message) async throws { await logger?.debug( "Processing notification", metadata: ["method": "\(message.method)"]) if configuration.strict { // Check initialization state unless this is an initialized notification if message.method != InitializedNotification.name { try checkInitialized() } } // Find notification handlers for this method guard let handlers = notificationHandlers[message.method] else { return } // Convert notification parameters to concrete type and call handlers for handler in handlers { do { try await handler(message) } catch { await logger?.error( "Error handling notification", metadata: [ "method": "\(message.method)", "error": "\(error)", ]) } } } private func checkInitialized() throws { guard isInitialized else { throw Error.invalidRequest("Server is not initialized") } } private func registerDefaultHandlers() { // Initialize withMethodHandler(Initialize.self) { [weak self] params in guard let self = self else { throw Error.internalError("Server was deallocated") } guard await !self.isInitialized else { throw Error.invalidRequest("Server is already initialized") } // Validate protocol version guard Version.latest == params.protocolVersion else { throw Error.invalidRequest( "Unsupported protocol version: \(params.protocolVersion)") } await self.setInitialState( clientInfo: params.clientInfo, clientCapabilities: params.capabilities, protocolVersion: params.protocolVersion ) let result = Initialize.Result( protocolVersion: Version.latest, capabilities: await self.capabilities, serverInfo: self.serverInfo, instructions: nil ) // Send initialized notification after a short delay Task { try? await Task.sleep(nanoseconds: 100_000_000) // 100ms try? await self.notify(InitializedNotification.message()) } return result } // Ping withMethodHandler(Ping.self) { _ in return Empty() } } private func setInitialState( clientInfo: Client.Info, clientCapabilities: Client.Capabilities, protocolVersion: String ) async { self.clientInfo = clientInfo self.clientCapabilities = clientCapabilities self.protocolVersion = protocolVersion self.isInitialized = true } }