Skip to main content
Glama
ContentView.swift6.21 kB
import AppKit import MenuBarExtraAccess import SwiftUI struct ContentView: View { @ObservedObject var serverController: ServerController @Binding var isEnabled: Bool @Binding var isMenuPresented: Bool @Environment(\.openSettings) private var openSettings private let aboutWindowController: AboutWindowController private var serviceConfigs: [ServiceConfig] { serverController.computedServiceConfigs } private var serviceBindings: [String: Binding<Bool>] { Dictionary( uniqueKeysWithValues: serviceConfigs.map { ($0.id, $0.binding) }) } init( serverManager: ServerController, isEnabled: Binding<Bool>, isMenuPresented: Binding<Bool> ) { self.serverController = serverManager self._isEnabled = isEnabled self._isMenuPresented = isMenuPresented self.aboutWindowController = AboutWindowController() } var body: some View { VStack(alignment: .leading, spacing: 0) { HStack { Text("Enable MCP Server") .frame(maxWidth: .infinity, alignment: .leading) Toggle("", isOn: $isEnabled) .toggleStyle(.switch) .labelsHidden() } .padding(.top, 2) .padding(.horizontal, 14) .onChange(of: isEnabled, initial: true) { Task { await serverController.setEnabled(isEnabled) } } if isEnabled { VStack(alignment: .leading, spacing: 8) { Divider() Text("Services") .font(.system(size: 13, weight: .semibold)) .foregroundColor(.secondary) .opacity(isEnabled ? 1.0 : 0.4) .padding(.horizontal, 14) ForEach(serviceConfigs) { config in ServiceToggleView(config: config) } } .padding(.top, 8) .padding(.bottom, 4) .padding(.horizontal, 2) .onChange(of: serviceConfigs.map { $0.binding.wrappedValue }, initial: true) { Task { await serverController.updateServiceBindings(serviceBindings) } } .transition(.opacity.combined(with: .move(edge: .top))) .animation(.easeInOut(duration: 0.3), value: isEnabled) } VStack(alignment: .leading, spacing: 2) { Divider() MenuButton("Configure Claude Desktop", isMenuPresented: $isMenuPresented) { ClaudeDesktop.showConfigurationPanel() } MenuButton("Copy server command to clipboard", isMenuPresented: $isMenuPresented) { let command = Bundle.main.bundleURL .appendingPathComponent("Contents/MacOS/imcp-server") .path let pasteboard = NSPasteboard.general pasteboard.clearContents() pasteboard.setString(command, forType: .string) } } .padding(.top, 8) .padding(.bottom, 2) .padding(.horizontal, 2) VStack(alignment: .leading, spacing: 2) { Divider() MenuButton("Settings...", isMenuPresented: $isMenuPresented) { openSettings() } MenuButton("About iMCP", isMenuPresented: $isMenuPresented) { aboutWindowController.showWindow(nil) NSApp.activate(ignoringOtherApps: true) } MenuButton("Quit", isMenuPresented: $isMenuPresented) { NSApplication.shared.terminate(nil) } } .padding(.bottom, 2) .padding(.horizontal, 2) } .padding(.vertical, 6) .background(Material.thick) } } private struct MenuButton: View { @Environment(\.isEnabled) private var isEnabled private let title: String private let action: () -> Void @Binding private var isMenuPresented: Bool @State private var isHighlighted: Bool = false @State private var isPressed: Bool = false init<S>( _ title: S, isMenuPresented: Binding<Bool>, action: @escaping () -> Void ) where S: StringProtocol { self.title = String(title) self._isMenuPresented = isMenuPresented self.action = action } var body: some View { HStack { Text(title) .foregroundColor(.primary.opacity(isEnabled ? 1.0 : 0.4)) .multilineTextAlignment(.leading) .padding(.vertical, 8) .padding(.horizontal, 14) Spacer() } .contentShape(Rectangle()) .allowsHitTesting(isEnabled) .onTapGesture { guard isEnabled else { return } Task { @MainActor in withAnimation(.easeInOut(duration: 0.1)) { isPressed = true } try? await Task.sleep(for: .milliseconds(100)) withAnimation(.easeInOut(duration: 0.1)) { isPressed = false } action() isMenuPresented = false } } .frame(height: 18) .padding(.vertical, 4) .background( RoundedRectangle(cornerRadius: 6) .fill( isPressed ? Color.accentColor : isHighlighted ? Color.accentColor.opacity(0.7) : Color.clear) ) .onHover { state in guard isEnabled else { return } isHighlighted = state } .onChange(of: isEnabled) { _, newValue in if !newValue { isHighlighted = false isPressed = false } } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mattt/iMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server