@@ -8,6 +8,7 @@ import SwiftUI
88struct MCPSettingsView : View {
99 @Bindable var settingsManager : AppSettingsManager
1010 @State private var manager = MCPServerManager . shared
11+ @State private var selectedTool : MCPClientTool = . claudeDesktop
1112
1213 var body : some View {
1314 Form {
@@ -83,30 +84,16 @@ struct MCPSettingsView: View {
8384 // MARK: - Client Configuration
8485
8586 private var clientConfigurationSection : some View {
86- Section ( " Client Configuration " ) {
87- VStack ( alignment: . leading, spacing: 8 ) {
88- Text ( " Add this to your AI tool's MCP configuration: " )
89- . foregroundStyle ( . secondary)
90- . font ( . callout)
91-
92- HStack {
93- Text ( configSnippet)
94- . font ( . system( . caption, design: . monospaced) )
95- . textSelection ( . enabled)
96- . padding ( 8 )
97- . frame ( maxWidth: . infinity, alignment: . leading)
98- . background ( . quaternary)
99- . clipShape ( RoundedRectangle ( cornerRadius: 6 ) )
100-
101- Button {
102- NSPasteboard . general. clearContents ( )
103- NSPasteboard . general. setString ( configSnippet, forType: . string)
104- } label: {
105- Image ( systemName: " doc.on.doc " )
106- }
107- . help ( String ( localized: " Copy to clipboard " ) )
87+ Section ( " Setup " ) {
88+ Picker ( " " , selection: $selectedTool) {
89+ ForEach ( MCPClientTool . allCases) { tool in
90+ Text ( tool. displayName) . tag ( tool)
10891 }
10992 }
93+ . pickerStyle ( . segmented)
94+ . labelsHidden ( )
95+
96+ MCPSetupInstructions ( tool: selectedTool, port: settingsManager. mcp. port)
11097 }
11198 }
11299
@@ -145,16 +132,155 @@ struct MCPSettingsView: View {
145132 }
146133 }
147134
148- private var configSnippet : String {
149- """
150- {
151- " mcpServers " : {
152- " tablepro " : {
153- " url " : " http://127.0.0.1: \( settingsManager. mcp. port) /mcp "
135+ }
136+
137+ // MARK: - Client Tool Enum
138+
139+ private enum MCPClientTool : String , CaseIterable , Identifiable {
140+ case claudeDesktop
141+ case claudeCode
142+ case cursor
143+
144+ var id : String { rawValue }
145+
146+ var displayName : String {
147+ switch self {
148+ case . claudeDesktop: " Claude Desktop "
149+ case . claudeCode: " Claude Code "
150+ case . cursor: " Cursor "
151+ }
152+ }
153+ }
154+
155+ // MARK: - Setup Instructions
156+
157+ private struct MCPSetupInstructions : View {
158+ let tool : MCPClientTool
159+ let port : Int
160+ @State private var copied = false
161+
162+ var body : some View {
163+ VStack ( alignment: . leading, spacing: 12 ) {
164+ ForEach ( Array ( steps. enumerated ( ) ) , id: \. offset) { index, step in
165+ HStack ( alignment: . firstTextBaseline, spacing: 8 ) {
166+ Text ( " \( index + 1 ) . " )
167+ . foregroundStyle ( . secondary)
168+ . monospacedDigit ( )
169+ . frame ( width: 20 , alignment: . trailing)
170+ Text ( step)
171+ . textSelection ( . enabled)
172+ }
154173 }
155- }
174+
175+ if let snippet = configSnippet {
176+ HStack ( alignment: . top) {
177+ Text ( snippet)
178+ . font ( . system( . caption, design: . monospaced) )
179+ . textSelection ( . enabled)
180+ . padding ( 8 )
181+ . frame ( maxWidth: . infinity, alignment: . leading)
182+ . background ( . quaternary)
183+ . clipShape ( RoundedRectangle ( cornerRadius: 6 ) )
184+
185+ Button {
186+ NSPasteboard . general. clearContents ( )
187+ NSPasteboard . general. setString ( snippet, forType: . string)
188+ copied = true
189+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 1.5 ) { copied = false }
190+ } label: {
191+ Image ( systemName: copied ? " checkmark " : " doc.on.doc " )
192+ . contentTransition ( . symbolEffect( . replace) )
193+ }
194+ . help ( String ( localized: " Copy to clipboard " ) )
195+ }
196+ }
197+
198+ if let command {
199+ HStack ( alignment: . top) {
200+ Text ( command)
201+ . font ( . system( . caption, design: . monospaced) )
202+ . textSelection ( . enabled)
203+ . padding ( 8 )
204+ . frame ( maxWidth: . infinity, alignment: . leading)
205+ . background ( . quaternary)
206+ . clipShape ( RoundedRectangle ( cornerRadius: 6 ) )
207+
208+ Button {
209+ NSPasteboard . general. clearContents ( )
210+ NSPasteboard . general. setString ( command, forType: . string)
211+ copied = true
212+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 1.5 ) { copied = false }
213+ } label: {
214+ Image ( systemName: copied ? " checkmark " : " doc.on.doc " )
215+ . contentTransition ( . symbolEffect( . replace) )
216+ }
217+ . help ( String ( localized: " Copy to clipboard " ) )
218+ }
219+ }
220+ }
221+ . font ( . callout)
222+ }
223+
224+ private var url : String {
225+ " http://127.0.0.1: \( port) /mcp "
226+ }
227+
228+ private var steps : [ String ] {
229+ switch tool {
230+ case . claudeDesktop:
231+ [
232+ " Open Claude Desktop, go to Settings > Developer " ,
233+ " Click \" Edit Config \" to open claude_desktop_config.json " ,
234+ " Add the JSON below inside the file and save " ,
235+ " Restart Claude Desktop "
236+ ]
237+ case . claudeCode:
238+ [
239+ " Run the command below in your terminal " ,
240+ ]
241+ case . cursor:
242+ [
243+ " Open Cursor, go to Settings > MCP " ,
244+ " Click \" + Add new global MCP server \" " ,
245+ " Paste the JSON below and save "
246+ ]
247+ }
248+ }
249+
250+ private var configSnippet : String ? {
251+ switch tool {
252+ case . claudeDesktop:
253+ """
254+ {
255+ " mcpServers " : {
256+ " tablepro " : {
257+ " url " : " \( url) "
258+ }
259+ }
260+ }
261+ """
262+ case . claudeCode:
263+ nil
264+ case . cursor:
265+ """
266+ {
267+ " mcpServers " : {
268+ " tablepro " : {
269+ " url " : " \( url) "
270+ }
271+ }
272+ }
273+ """
274+ }
275+ }
276+
277+ private var command : String ? {
278+ switch tool {
279+ case . claudeCode:
280+ " claude mcp add tablepro --transport sse \( url) "
281+ default :
282+ nil
156283 }
157- """
158284 }
159285}
160286
0 commit comments