diff --git a/App/Controllers/ServerController.swift b/App/Controllers/ServerController.swift index 8b60b70..f26126b 100644 --- a/App/Controllers/ServerController.swift +++ b/App/Controllers/ServerController.swift @@ -52,6 +52,7 @@ enum ServiceRegistry { CalendarService.shared, CaptureService.shared, ContactsService.shared, + FilesService.shared, LocationService.shared, MapsService.shared, MessageService.shared, @@ -64,6 +65,7 @@ enum ServiceRegistry { calendarEnabled: Binding, captureEnabled: Binding, contactsEnabled: Binding, + filesEnabled: Binding, locationEnabled: Binding, mapsEnabled: Binding, messagesEnabled: Binding, @@ -93,6 +95,13 @@ enum ServiceRegistry { service: ContactsService.shared, binding: contactsEnabled ), + ServiceConfig( + name: "Files", + iconName: "folder.fill", + color: .indigo, + service: FilesService.shared, + binding: filesEnabled + ), ServiceConfig( name: "Location", iconName: "location.fill", @@ -149,6 +158,7 @@ final class ServerController: ObservableObject { @AppStorage("calendarEnabled") private var calendarEnabled = false @AppStorage("captureEnabled") private var captureEnabled = false @AppStorage("contactsEnabled") private var contactsEnabled = false + @AppStorage("finderEnabled") private var finderEnabled = false @AppStorage("locationEnabled") private var locationEnabled = false @AppStorage("mapsEnabled") private var mapsEnabled = true // Default for maps @AppStorage("messagesEnabled") private var messagesEnabled = false @@ -165,6 +175,7 @@ final class ServerController: ObservableObject { calendarEnabled: $calendarEnabled, captureEnabled: $captureEnabled, contactsEnabled: $contactsEnabled, + filesEnabled: $finderEnabled, locationEnabled: $locationEnabled, mapsEnabled: $mapsEnabled, messagesEnabled: $messagesEnabled, @@ -415,6 +426,7 @@ actor MCPConnectionManager { name: Bundle.main.name ?? "iMCP", version: Bundle.main.shortVersionString ?? "unknown", capabilities: MCP.Server.Capabilities( + resources: .init(subscribe: false, listChanged: true), tools: .init(listChanged: true) ) ) @@ -853,12 +865,49 @@ actor ServerNetworkManager { return ListPrompts.Result(prompts: []) } - // Register the resources/list handler + // Register the resources/list handler (empty for now since we use templates) await server.withMethodHandler(ListResources.self) { _ in log.debug("Handling ListResources request for \(connectionID)") return ListResources.Result(resources: []) } + // Register the resources/templates/list handler + await server.withMethodHandler(ListResourceTemplates.self) { [weak self] _ in + guard let self = self else { + return ListResourceTemplates.Result(templates: []) + } + + log.debug("Handling ListResourceTemplates request for \(connectionID)") + + var templates: [MCP.Resource.Template] = [] + if await self.isEnabledState { + for service in await self.services { + let serviceId = String(describing: type(of: service)) + + // Get the binding value in an actor-safe way + if let isServiceEnabled = await self.serviceBindings[serviceId]?.wrappedValue, + isServiceEnabled + { + for template in service.resourceTemplates { + log.debug("Adding resource template: \(template.name)") + templates.append( + .init( + uriTemplate: template.uriTemplate, + name: template.name, + description: template.description, + mimeType: template.mimeType + ) + ) + } + } + } + } + + log.info( + "Returning \(templates.count) available resource templates for \(connectionID)") + return ListResourceTemplates.Result(templates: templates) + } + // Register tools/list handler await server.withMethodHandler(ListTools.self) { [weak self] _ in guard let self = self else { @@ -877,7 +926,6 @@ actor ServerNetworkManager { isServiceEnabled { for tool in service.tools { - log.debug("Adding tool: \(tool.name)") tools.append( .init( name: tool.name, @@ -975,6 +1023,63 @@ actor ServerNetworkManager { isError: true ) } + + // Register resources/read handler + await server.withMethodHandler(ReadResource.self) { [weak self] params in + guard let self = self else { + return ReadResource.Result( + contents: [.text("Server unavailable", uri: params.uri)] + ) + } + + log.notice("Resource read received from \(connectionID): \(params.uri)") + + guard await self.isEnabledState else { + log.notice("Resource read rejected: iMCP is disabled") + return ReadResource.Result( + contents: [ + .text( + "iMCP is currently disabled. Please enable it to read resources.", + uri: params.uri) + ] + ) + } + + for service in await self.services { + let serviceId = String(describing: type(of: service)) + + // Get the binding value in an actor-safe way + if let isServiceEnabled = await self.serviceBindings[serviceId]?.wrappedValue, + isServiceEnabled + { + do { + guard + let content = try await service.read(resource: params.uri) + else { + continue + } + + log.notice("Resource \(params.uri) read successfully for \(connectionID)") + + return ReadResource.Result(contents: [content]) + } catch { + log.error( + "Error reading resource \(params.uri): \(error.localizedDescription)") + return ReadResource.Result( + contents: [.text("Error: \(error)", uri: params.uri)] + ) + } + } + } + + log.error("Resource not found or service not enabled: \(params.uri)") + return ReadResource.Result( + contents: [ + .text( + "Resource not found or service not enabled: \(params.uri)", uri: params.uri) + ] + ) + } } // Update the enabled state and notify clients diff --git a/App/Models/Resource.swift b/App/Models/Resource.swift new file mode 100644 index 0000000..bfd3171 --- /dev/null +++ b/App/Models/Resource.swift @@ -0,0 +1,29 @@ +import Foundation +import MCP +import Ontology + +struct ResourceTemplate: Sendable { + let name: String + let description: String? + let uriTemplate: String + let mimeType: String? + private let reader: @Sendable (String) async throws -> ResourceContent? + + init( + name: String, + description: String? = nil, + uriTemplate: String, + mimeType: String? = nil, + reader: @Sendable @escaping (String) async throws -> ResourceContent? + ) { + self.name = name + self.description = description + self.uriTemplate = uriTemplate + self.mimeType = mimeType + self.reader = reader + } + + func read(uri: String) async throws -> ResourceContent? { + try await reader(uri) + } +} diff --git a/App/Models/Service.swift b/App/Models/Service.swift index 0410211..6354932 100644 --- a/App/Models/Service.swift +++ b/App/Models/Service.swift @@ -1,12 +1,21 @@ +import MCP + +public typealias ResourceContent = MCP.Resource.Content + @preconcurrency protocol Service { - @ToolBuilder var tools: [Tool] { get } - var isActivated: Bool { get async } func activate() async throws + + @ResourceTemplateBuilder var resourceTemplates: [ResourceTemplate] { get } + @ToolBuilder var tools: [Tool] { get } } +// MARK: - Default Implementation + extension Service { + // MARK: - Activation + var isActivated: Bool { get async { return true @@ -15,6 +24,23 @@ extension Service { func activate() async throws {} + // MARK: - Resources + + var resourceTemplates: [ResourceTemplate] { [] } + + func read(resource uri: String) async throws -> ResourceContent? { + for template in resourceTemplates { + if let content = try await template.read(uri: uri) { + return content + } + } + return nil + } + + // MARK: - Tools + + var tools: [Tool] { [] } + func call(tool name: String, with arguments: [String: Value]) async throws -> Value? { for tool in tools where tool.name == name { return try await tool.callAsFunction(arguments) @@ -24,9 +50,18 @@ extension Service { } } +// MARK: - Builders + @resultBuilder struct ToolBuilder { static func buildBlock(_ tools: Tool...) -> [Tool] { tools } } + +@resultBuilder +struct ResourceTemplateBuilder { + static func buildBlock(_ templates: ResourceTemplate...) -> [ResourceTemplate] { + templates + } +} diff --git a/App/Models/Tool.swift b/App/Models/Tool.swift index c0ba0e6..a305c69 100644 --- a/App/Models/Tool.swift +++ b/App/Models/Tool.swift @@ -10,7 +10,7 @@ public struct Tool: Sendable { let annotations: MCP.Tool.Annotations private let implementation: @Sendable ([String: Value]) async throws -> Value - public init( + public init( name: String, description: String, inputSchema: JSONSchema, diff --git a/App/Services/Files.swift b/App/Services/Files.swift new file mode 100644 index 0000000..bbd1b36 --- /dev/null +++ b/App/Services/Files.swift @@ -0,0 +1,222 @@ +import AppKit +import Foundation +import MCP +import OSLog +import Ontology +import UniformTypeIdentifiers + +private let log = Logger.service("files") + +final class FilesService: Service { + static let shared = FilesService() + + var isActivated: Bool { + get async { + hasFullDiskAccess() + } + } + + func activate() async throws { + log.debug("Activating Files service") + if !(await isActivated) { + await promptForFullDiskAccess() + } + } + + var resourceTemplates: [ResourceTemplate] { + ResourceTemplate( + name: "file", + description: "Access file contents and metadata", + uriTemplate: "file://{path}", + mimeType: "application/json" + ) { (uri: String) -> Resource.Content? in + log.debug("Reading file resource template: \(uri)") + + // Parse and validate the file URI + guard let fileURL = URL(string: uri.removingPercentEncoding ?? uri), + fileURL.scheme == "file", + fileURL.isFileURL + else { + throw MCPError.invalidRequest("Invalid file URI: \(uri)") + } + + log.debug("File path: \(fileURL.path)") + + // Check if the file exists + guard try fileURL.checkResourceIsReachable() else { + log.error("File does not exist: \(fileURL.path)") + throw NSError( + domain: NSCocoaErrorDomain, + code: NSFileReadNoSuchFileError, + userInfo: [NSLocalizedDescriptionKey: "File does not exist"] + ) + } + + // Get file attributes and resource values in one go + let resourceValues = try fileURL.resourceValues(forKeys: [ + .isDirectoryKey, .fileSizeKey, .contentModificationDateKey, + ]) + + // Check if it's a directory + if resourceValues.isDirectory == true { + // For directories, create JSON representation with file metadata + let jsonString = try directoryJSON(for: fileURL) + return Resource.Content.text( + jsonString, uri: uri, mimeType: "inode/directory+json") + } + + // Check file size (limit to 100MB) + if let fileSize = resourceValues.fileSize, + fileSize > 100_000_000 + { + throw NSError( + domain: NSCocoaErrorDomain, + code: NSFileReadTooLargeError, + userInfo: [NSLocalizedDescriptionKey: "File too large to read"] + ) + } + + // For regular files, get MIME type efficiently + let mimeType = getMimeType(for: fileURL) + + // Try to read as text first + if mimeType.hasPrefix("text/") + || mimeType == "application/json" + || mimeType == "application/xml" + || mimeType == "application/x-yaml" + || mimeType == "application/toml" + { + if let content = try? String(contentsOf: fileURL, encoding: .utf8) { + return Resource.Content.text(content, uri: uri, mimeType: mimeType) + } + } + + // Fallback to reading as binary + let data = try Data(contentsOf: fileURL) + return Resource.Content.binary(data, uri: uri, mimeType: mimeType) + + } + } + + private func hasFullDiskAccess() -> Bool { + // Try to access a protected directory that requires Full Disk Access + let protectedPath = "/Library/Application Support/com.apple.TCC/" + let fileManager = FileManager.default + + return fileManager.isReadableFile(atPath: protectedPath) + } + + @MainActor + private func promptForFullDiskAccess() async { + let alert = NSAlert() + alert.messageText = "Full Disk Access Required" + alert.informativeText = """ + iMCP needs Full Disk Access to read files throughout your system. + + 1. Click 'Open System Preferences' below + 2. Go to Privacy & Security → Full Disk Access + 3. Drag iMCP from Finder to the Full Disk Access list + """ + alert.addButton(withTitle: "Open System Preferences") + alert.addButton(withTitle: "Cancel") + alert.alertStyle = .warning + + let response = alert.runModal() + if response == .alertFirstButtonReturn { + openSystemPreferences() + revealAppInFinder() + } + } + + private func openSystemPreferences() { + let url = URL( + string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles")! + NSWorkspace.shared.open(url) + } + + private func revealAppInFinder() { + guard let appPath = Bundle.main.bundleURL.path as String? else { + log.error("Could not determine app bundle path") + return + } + + NSWorkspace.shared.selectFile(appPath, inFileViewerRootedAtPath: "") + } +} + +// MARK: - + +private struct FileInfo: Codable { + enum Kind: String, Codable { + case file + case directory + } + + let name: String + let kind: Kind + let size: Int? + let modified: String? +} + +private func directoryJSON(for url: URL) throws -> String { + let contents = try FileManager.default.contentsOfDirectory( + at: url, + includingPropertiesForKeys: [ + .isDirectoryKey, .fileSizeKey, .contentModificationDateKey, + ]) + guard !contents.isEmpty else { + return "[]" + } + + guard contents.count < 1024 else { + throw NSError( + domain: NSCocoaErrorDomain, + code: NSFileReadTooLargeError, + userInfo: [NSLocalizedDescriptionKey: "Directory too large to read"] + ) + } + + let dateFormatter = ISO8601DateFormatter() + + let files = contents.compactMap { fileURL -> FileInfo? in + guard + let resourceValues = try? fileURL.resourceValues(forKeys: [ + .isDirectoryKey, .fileSizeKey, .contentModificationDateKey, + ]) + else { + return nil + } + + let kind: FileInfo.Kind = resourceValues.isDirectory == true ? .directory : .file + let size = resourceValues.fileSize + let modified = resourceValues.contentModificationDate.map { + dateFormatter.string(from: $0) + } + + return FileInfo( + name: fileURL.lastPathComponent, + kind: kind, + size: size, + modified: modified + ) + }.sorted { $0.name < $1.name } + + let encoder = JSONEncoder() + encoder.outputFormatting = [ + .prettyPrinted, .sortedKeys, .withoutEscapingSlashes, + ] + let jsonData = try encoder.encode(files) + return String(data: jsonData, encoding: .utf8) ?? "[]" +} + +// MARK: - + +private func getMimeType(for url: URL) -> String { + if let utType = UTType(filenameExtension: url.pathExtension), + let mimeType = utType.preferredMIMEType + { + return mimeType + } + + return "application/octet-stream" +} diff --git a/iMCP.xcodeproj/project.pbxproj b/iMCP.xcodeproj/project.pbxproj index 1c49ce9..9ad35f7 100644 --- a/iMCP.xcodeproj/project.pbxproj +++ b/iMCP.xcodeproj/project.pbxproj @@ -217,7 +217,7 @@ packageReferences = ( F88358352D64A085000317CD /* XCRemoteSwiftPackageReference "swift-log" */, F873F48B2D712BCF0035CD0A /* XCRemoteSwiftPackageReference "Ontology" */, - F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "madrid" */, + F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "Madrid" */, F825BDBD2D9AD00E0063ADD7 /* XCRemoteSwiftPackageReference "swift-service-lifecycle" */, F8D7C3182DCBD32100A4775F /* XCRemoteSwiftPackageReference "swift-sdk" */, F8D8C48C2DCE0E6800369E5C /* XCRemoteSwiftPackageReference "JSONSchema" */, @@ -385,10 +385,10 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = App/App.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 8; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 9; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = R7AYS6Z734; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -396,13 +396,16 @@ INFOPLIST_KEY_CFBundleDisplayName = iMCP; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_LSUIElement = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "${PRODUCT_NAME} needs access to the camera to take pictures for the MCP server."; - INFOPLIST_KEY_NSContactsUsageDescription = "${PRODUCT_NAME} needs access to provide contact information to the MCP server."; + INFOPLIST_KEY_NSCameraUsageDescription = "iMCP needs access to the camera to take pictures for the MCP server."; + INFOPLIST_KEY_NSContactsUsageDescription = "iMCP needs access to provide contact information to the MCP server."; + INFOPLIST_KEY_NSDesktopFolderUsageDescription = "iMCP needs access to the Desktop folder to provide file resources to the MCP server."; + INFOPLIST_KEY_NSDocumentsFolderUsageDescription = "iMCP needs access to the Documents folder to provide file resources to the MCP server."; + INFOPLIST_KEY_NSDownloadsFolderUsageDescription = "iMCP needs access to the Downloads folder to provide file resources to the MCP server."; INFOPLIST_KEY_NSHumanReadableCopyright = "© 2025 Loopwork Limited. All rights reserved."; - INFOPLIST_KEY_NSLocalNetworkUsageDescription = "${PRODUCT_NAME} uses the local network to connect to the MCP server."; - INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "${PRODUCT_NAME} needs access to provide location information to the MCP server."; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "${PRODUCT_NAME} needs access to provide location information to the MCP server."; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "${PRODUCT_NAME} needs access to the microphone to record audio for the MCP server."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "iMCP uses the local network to connect to the MCP server."; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "iMCP needs access to provide location information to the MCP server."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "iMCP needs access to provide location information to the MCP server."; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "iMCP needs access to the microphone to record audio for the MCP server."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -418,8 +421,9 @@ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 15.1; MARKETING_VERSION = 1.3.0; - PRODUCT_BUNDLE_IDENTIFIER = com.loopwork.iMCP; + PRODUCT_BUNDLE_IDENTIFIER = me.mattt.iMCP; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SUPPORTED_PLATFORMS = macosx; SUPPORTS_MACCATALYST = NO; @@ -437,19 +441,19 @@ AUTOMATION_APPLE_EVENTS = NO; CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 8; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 9; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; - ENABLE_APP_SANDBOX = YES; + ENABLE_APP_SANDBOX = NO; ENABLE_HARDENED_RUNTIME = YES; - ENABLE_INCOMING_NETWORK_CONNECTIONS = NO; - ENABLE_OUTGOING_NETWORK_CONNECTIONS = NO; + ENABLE_INCOMING_NETWORK_CONNECTIONS = YES; + ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; - ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = YES; + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO; ENABLE_RESOURCE_ACCESS_BLUETOOTH = NO; - ENABLE_RESOURCE_ACCESS_CALENDARS = NO; - ENABLE_RESOURCE_ACCESS_CAMERA = YES; + ENABLE_RESOURCE_ACCESS_CALENDARS = YES; + ENABLE_RESOURCE_ACCESS_CAMERA = NO; ENABLE_RESOURCE_ACCESS_CONTACTS = YES; ENABLE_RESOURCE_ACCESS_LOCATION = YES; ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO; @@ -460,13 +464,16 @@ INFOPLIST_KEY_CFBundleDisplayName = iMCP; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; INFOPLIST_KEY_LSUIElement = YES; - INFOPLIST_KEY_NSCameraUsageDescription = "${PRODUCT_NAME} needs access to the camera to take pictures for the MCP server."; - INFOPLIST_KEY_NSContactsUsageDescription = "${PRODUCT_NAME} needs access to provide contact information to the MCP server."; + INFOPLIST_KEY_NSCameraUsageDescription = "iMCP needs access to the camera to take pictures for the MCP server."; + INFOPLIST_KEY_NSContactsUsageDescription = "iMCP needs access to provide contact information to the MCP server."; + INFOPLIST_KEY_NSDesktopFolderUsageDescription = "iMCP needs access to the Desktop folder to provide file resources to the MCP server."; + INFOPLIST_KEY_NSDocumentsFolderUsageDescription = "iMCP needs access to the Documents folder to provide file resources to the MCP server."; + INFOPLIST_KEY_NSDownloadsFolderUsageDescription = "iMCP needs access to the Downloads folder to provide file resources to the MCP server."; INFOPLIST_KEY_NSHumanReadableCopyright = "© 2025 Loopwork Limited. All rights reserved."; - INFOPLIST_KEY_NSLocalNetworkUsageDescription = "${PRODUCT_NAME} uses the local network to connect to the MCP server."; - INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "${PRODUCT_NAME} needs access to provide location information to the MCP server."; - INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "${PRODUCT_NAME} needs access to provide location information to the MCP server."; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "${PRODUCT_NAME} needs access to the microphone to record audio for the MCP server."; + INFOPLIST_KEY_NSLocalNetworkUsageDescription = "iMCP uses the local network to connect to the MCP server."; + INFOPLIST_KEY_NSLocationAlwaysAndWhenInUseUsageDescription = "iMCP needs access to provide location information to the MCP server."; + INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "iMCP needs access to provide location information to the MCP server."; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "iMCP needs access to the microphone to record audio for the MCP server."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -482,10 +489,15 @@ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 15.1; MARKETING_VERSION = 1.3.0; - PRODUCT_BUNDLE_IDENTIFIER = com.loopwork.iMCP; + PRODUCT_BUNDLE_IDENTIFIER = me.mattt.iMCP; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "iMCP Developer Build Provision Profile"; + RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; + RUNTIME_EXCEPTION_ALLOW_JIT = NO; + RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO; + RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO; + RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO; + RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO; SDKROOT = auto; SUPPORTED_PLATFORMS = macosx; SUPPORTS_MACCATALYST = NO; @@ -501,9 +513,9 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = R7AYS6Z734; + DEVELOPMENT_TEAM = ""; MACOSX_DEPLOYMENT_TARGET = 15.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.loopwork.imcp-server"; + PRODUCT_BUNDLE_IDENTIFIER = "me.mattt.imcp-server"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; @@ -518,9 +530,9 @@ CODE_SIGN_STYLE = Manual; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=macosx*]" = R7AYS6Z734; + "DEVELOPMENT_TEAM[sdk=macosx*]" = ""; MACOSX_DEPLOYMENT_TARGET = 15.1; - PRODUCT_BUNDLE_IDENTIFIER = "com.loopwork.imcp-server"; + PRODUCT_BUNDLE_IDENTIFIER = "me.mattt.imcp-server"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; @@ -562,9 +574,9 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "madrid" */ = { + F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "Madrid" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/loopwork-ai/madrid"; + repositoryURL = "https://github.com/mattt/Madrid"; requirement = { kind = upToNextMajorVersion; minimumVersion = 0.1.0; @@ -580,10 +592,10 @@ }; F873F48B2D712BCF0035CD0A /* XCRemoteSwiftPackageReference "Ontology" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/loopwork-ai/Ontology"; + repositoryURL = "https://github.com/mattt/Ontology"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 0.7.0; + minimumVersion = 0.7.1; }; }; F87796FA2E0764AE00328CC6 /* XCRemoteSwiftPackageReference "MenuBarExtraAccess" */ = { @@ -612,7 +624,7 @@ }; F8D8C48C2DCE0E6800369E5C /* XCRemoteSwiftPackageReference "JSONSchema" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/loopwork-ai/JSONSchema.git"; + repositoryURL = "https://github.com/mattt/JSONSchema.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 1.3.0; @@ -623,12 +635,12 @@ /* Begin XCSwiftPackageProductDependency section */ F809556D2D7888920055B911 /* TypedStream */ = { isa = XCSwiftPackageProductDependency; - package = F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "madrid" */; + package = F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "Madrid" */; productName = TypedStream; }; F809556F2D7888920055B911 /* iMessage */ = { isa = XCSwiftPackageProductDependency; - package = F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "madrid" */; + package = F809556C2D7888920055B911 /* XCRemoteSwiftPackageReference "Madrid" */; productName = iMessage; }; F825BDBE2D9AD00E0063ADD7 /* ServiceLifecycle */ = { diff --git a/iMCP.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iMCP.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 74e3e7f..0190a70 100644 --- a/iMCP.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/iMCP.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "0604c8a8c049fb6e696fcd8af68cdc36f2f23f938eec962e31f24cc19d550d45", + "originHash" : "75782284cc1c6049835b3b6bdc5f5c0711069c98bd8295fd2f786ec2dc8b7b86", "pins" : [ { "identity" : "eventsource", @@ -13,7 +13,7 @@ { "identity" : "jsonschema", "kind" : "remoteSourceControl", - "location" : "https://github.com/loopwork-ai/JSONSchema.git", + "location" : "https://github.com/mattt/JSONSchema.git", "state" : { "revision" : "e17c9e1fb6afbad656824d03f996cf8621f9db83", "version" : "1.3.0" @@ -22,10 +22,10 @@ { "identity" : "madrid", "kind" : "remoteSourceControl", - "location" : "https://github.com/loopwork-ai/madrid", + "location" : "https://github.com/mattt/Madrid", "state" : { - "revision" : "9d9fdb20424483fb592e5a026d9d0816cd5c43eb", - "version" : "0.1.0" + "revision" : "d57b7b143d4229670bad0bdd05c539e0f0c8267a", + "version" : "0.1.2" } }, { @@ -40,7 +40,7 @@ { "identity" : "ontology", "kind" : "remoteSourceControl", - "location" : "https://github.com/loopwork-ai/Ontology", + "location" : "https://github.com/mattt/Ontology", "state" : { "revision" : "27969c8ddedff24f90f366fcb1f79aa9ed92e8c5", "version" : "0.7.1"