wolf1997 commited on
Commit
f1c00ca
Β·
verified Β·
1 Parent(s): 41e02f5

Update src/gradio_app/app.py

Browse files
Files changed (1) hide show
  1. src/gradio_app/app.py +454 -454
src/gradio_app/app.py CHANGED
@@ -1,454 +1,454 @@
1
- import gradio as gr
2
- import asyncio
3
- import json
4
- import atexit
5
- import signal
6
- import sys
7
- from src.mcp_agent.agent import MCP_Agent
8
-
9
- class GradioMCPApp:
10
- def __init__(self):
11
- self.agent = None
12
- self.chat_history = []
13
- self._loop = None
14
- self.server_count = 1
15
-
16
- def get_or_create_loop(self):
17
- """Get existing event loop or create a new one"""
18
- if self._loop is None or self._loop.is_closed():
19
- self._loop = asyncio.new_event_loop()
20
- asyncio.set_event_loop(self._loop)
21
- return self._loop
22
-
23
- async def initialize_agent(self, openai_api_key, *server_configs):
24
- """Initialize the MCP Agent with provided configuration"""
25
- try:
26
- # Clean up existing agent first
27
- if self.agent:
28
- await self.disconnect_agent()
29
-
30
- # Build MCP servers configuration from form fields
31
- # server_configs comes as a flat list: [url1, name1, type1, token1, url2, name2, type2, token2, ...]
32
- mcp_servers = []
33
- for i in range(0, len(server_configs), 4):
34
- if i + 3 < len(server_configs):
35
- server_url = server_configs[i]
36
- server_name = server_configs[i + 1]
37
- server_type = server_configs[i + 2]
38
- headers = server_configs[i + 3]
39
-
40
- if server_url and server_url.strip():
41
- server_config = {
42
- 'url': server_url.strip(),
43
- 'name': server_name.strip() if server_name and server_name.strip() else f'server_{i//4 + 1}',
44
- 'type': server_type if server_type else 'http',
45
- 'headers': headers.strip() if headers and headers.strip() else None
46
- }
47
- mcp_servers.append(server_config)
48
-
49
- # Initialize agent
50
- api_keys = {'openai_api_key': openai_api_key}
51
- self.agent = MCP_Agent(api_keys=api_keys, mpc_server_urls=mcp_servers)
52
-
53
- # Connect to MCP servers
54
- await self.agent.connect()
55
-
56
- server_count = len(mcp_servers)
57
- if server_count == 0:
58
- return True, "Agent initialized successfully (no MCP servers configured)!"
59
- else:
60
- return True, f"Agent initialized successfully with {server_count} MCP server(s)!"
61
-
62
- except Exception as e:
63
- return False, f"Error initializing agent: {str(e)}"
64
-
65
- async def chat_with_agent(self, message):
66
- """Handle text chat with the agent"""
67
- if not self.agent:
68
- return self.chat_history, "Please initialize the agent first by providing your OpenAI API key and clicking 'Initialize Agent'."
69
-
70
- if not message or not message.strip():
71
- return self.chat_history, "Please provide a message."
72
-
73
- try:
74
- # Get response from agent
75
- response = await self.agent.chat(message.strip())
76
-
77
- # Update chat history
78
- self.chat_history.append([message.strip(), str(response)])
79
-
80
- return self.chat_history, ""
81
-
82
- except Exception as e:
83
- error_msg = f"Error during chat: {str(e)}"
84
- self.chat_history.append([message, error_msg])
85
- return self.chat_history, error_msg
86
-
87
- async def reset_agent(self):
88
- """Reset the agent's conversation history"""
89
- if self.agent:
90
- self.agent.reset()
91
- self.chat_history = []
92
- return [], "Agent conversation history reset successfully!"
93
- else:
94
- return [], "No agent to reset. Please initialize the agent first."
95
-
96
- async def disconnect_agent(self):
97
- """Disconnect from MCP servers"""
98
- if self.agent:
99
- try:
100
- await self.agent.disconnect()
101
- except Exception as e:
102
- print(f"Error during disconnect: {e}")
103
- finally:
104
- self.agent = None
105
- self.chat_history = []
106
- return [], "Agent disconnected successfully!"
107
-
108
- async def cleanup(self):
109
- """Clean up resources"""
110
- if self.agent:
111
- await self.disconnect_agent()
112
- if self._loop and not self._loop.is_closed():
113
- self._loop.close()
114
-
115
- # Create the app instance
116
- app_instance = GradioMCPApp()
117
-
118
- def run_async_safely(coro, *args):
119
- """Safely run async function with proper error handling"""
120
- loop = app_instance.get_or_create_loop()
121
- try:
122
- return loop.run_until_complete(coro(*args))
123
- except Exception as e:
124
- print(f"Error in async operation: {e}")
125
- return None, f"Error: {str(e)}"
126
-
127
- # Define async wrapper functions for Gradio
128
- def initialize_agent_wrapper(openai_api_key, *server_configs):
129
- success, message = run_async_safely(
130
- app_instance.initialize_agent,
131
- openai_api_key, *server_configs
132
- )
133
- if success is None:
134
- return gr.update(visible=False), gr.update(visible=True), message
135
- return gr.update(visible=success), gr.update(visible=not success), message
136
-
137
- def chat_wrapper(message):
138
- chat_history, error_msg = run_async_safely(app_instance.chat_with_agent, message)
139
- if chat_history is None:
140
- return [], error_msg, ""
141
- return chat_history, error_msg, "" # Clear input
142
-
143
- def reset_wrapper():
144
- chat_history, message = run_async_safely(app_instance.reset_agent)
145
- if chat_history is None:
146
- return [], message
147
- return chat_history, message
148
-
149
- def disconnect_wrapper():
150
- chat_history, message = run_async_safely(app_instance.disconnect_agent)
151
- if chat_history is None:
152
- return [], gr.update(visible=False), gr.update(visible=True), message
153
- return chat_history, gr.update(visible=False), gr.update(visible=True), message
154
-
155
- # Cleanup function for graceful shutdown
156
- def cleanup_on_exit():
157
- """Cleanup function to run on exit"""
158
- try:
159
- loop = app_instance.get_or_create_loop()
160
- if not loop.is_closed():
161
- loop.run_until_complete(app_instance.cleanup())
162
- except Exception as e:
163
- print(f"Error during cleanup: {e}")
164
-
165
- # Register cleanup function
166
- atexit.register(cleanup_on_exit)
167
-
168
- # Handle SIGINT (Ctrl+C) gracefully
169
- def signal_handler(signum, frame):
170
- print("\nReceived interrupt signal. Cleaning up...")
171
- cleanup_on_exit()
172
- sys.exit(0)
173
-
174
- signal.signal(signal.SIGINT, signal_handler)
175
-
176
- # Server management functions
177
- def add_server(current_count):
178
- """Show the next server configuration"""
179
- new_count = min(current_count + 1, 3) # Max 3 servers
180
- return (
181
- new_count,
182
- gr.update(visible=new_count >= 2), # server2_group
183
- gr.update(visible=new_count >= 3), # server3_group
184
- gr.update(interactive=new_count < 3), # add_server_btn
185
- gr.update(interactive=new_count > 1) # remove_server_btn
186
- )
187
-
188
- def remove_server(current_count):
189
- """Hide the last server configuration"""
190
- new_count = max(current_count - 1, 1) # Min 1 server
191
- return (
192
- new_count,
193
- gr.update(visible=new_count >= 2), # server2_group
194
- gr.update(visible=new_count >= 3), # server3_group
195
- gr.update(interactive=new_count < 3), # add_server_btn
196
- gr.update(interactive=new_count > 1) # remove_server_btn
197
- )
198
-
199
-
200
-
201
- # Create the Gradio interface
202
- with gr.Blocks(title="MCP Agent Chat", theme=gr.themes.Soft()) as demo:
203
- with gr.Row():
204
-
205
- gr.Markdown("# MCP Agent Chat Interface")
206
- gr.HTML("") # Balance spacing
207
-
208
-
209
-
210
- with gr.Sidebar():
211
- # Sidebar for configuration
212
- sidebar_column = gr.Column(scale=1, min_width=350)
213
- with sidebar_column:
214
- gr.Markdown("## πŸ”§ Configuration")
215
-
216
- openai_key = gr.Textbox(
217
- label="OpenAI API Key",
218
- type="password",
219
- placeholder="sk-...",
220
- info="Your OpenAI API key for the language model"
221
- )
222
-
223
- gr.Markdown("### MCP Servers Setup")
224
- gr.Markdown("Configure your MCP server connections (leave all URLs empty to run without MCP servers)")
225
-
226
- # Container for dynamic server configurations
227
- servers_container = gr.Column()
228
-
229
- # Initial server configuration
230
- with servers_container:
231
- # Server 1 (always present)
232
- with gr.Tab("Url based servers"):
233
- with gr.Group():
234
- gr.Markdown("#### Server 1")
235
- server1_url = gr.Textbox(
236
- label="Server URL",
237
- placeholder="http://localhost:8000",
238
- info="The URL of your MCP server"
239
- )
240
- server1_name = gr.Textbox(
241
- label="Server Name",
242
- placeholder="server_1",
243
- info="A friendly name for your MCP server"
244
- )
245
- server1_type = gr.Dropdown(
246
- label="Server Type",
247
- choices=["http", "SSE"],
248
- value="http",
249
- info="The type of MCP server connection"
250
- )
251
- server1_headers = gr.Textbox(
252
- label="Headers (Optional)",
253
- type="password",
254
- placeholder="Leave empty if not required",
255
- info="Headers for the MCP server (if required), example: {'Authorization': 'Bearer 1234567890'}"
256
- )
257
-
258
- # Server 2 (optional)
259
- server2_group = gr.Group(visible=False)
260
- with server2_group:
261
- gr.Markdown("#### Server 2")
262
- server2_url = gr.Textbox(
263
- label="Server URL",
264
- placeholder="http://localhost:8001",
265
- info="The URL of your MCP server"
266
- )
267
- server2_name = gr.Textbox(
268
- label="Server Name",
269
- placeholder="server_2",
270
- info="A friendly name for your MCP server"
271
- )
272
- server2_type = gr.Dropdown(
273
- label="Server Type",
274
- choices=["http", "SSE"],
275
- value="http",
276
- info="The type of MCP server connection"
277
- )
278
- server2_headers = gr.Textbox(
279
- label="Headers (Optional)",
280
- type="password",
281
- placeholder="Leave empty if not required",
282
- info="Headers for the MCP server (if required), example: {'Authorization': 'Bearer 1234567890'}"
283
- )
284
-
285
- # Server 3 (optional)
286
- server3_group = gr.Group(visible=False)
287
- with server3_group:
288
- gr.Markdown("#### Server 3")
289
- server3_url = gr.Textbox(
290
- label="Server URL",
291
- placeholder="http://localhost:8002",
292
- info="The URL of your MCP server"
293
- )
294
- server3_name = gr.Textbox(
295
- label="Server Name",
296
- placeholder="server_3",
297
- info="A friendly name for your MCP server"
298
- )
299
- server3_type = gr.Dropdown(
300
- label="Server Type",
301
- choices=["http", "SSE"],
302
- value="http",
303
- info="The type of MCP server connection"
304
- )
305
- server3_headers = gr.Textbox(
306
- label="Headers (Optional)",
307
- type="password",
308
- placeholder="Leave empty if not required",
309
- info="Headers for the MCP server (if required), example: {'Authorization': 'Bearer 1234567890'}"
310
- )
311
- with gr.Tab("Stdio based servers"):
312
- with gr.Group():
313
- gr.Markdown("#### Server 1")
314
- server1_command = gr.Textbox(
315
- label="Command",
316
- placeholder="npx",
317
- info="The command to use to run the MCP server, docker, npm, python, etc."
318
- )
319
- server1_args = gr.Textbox(
320
- label="Arguments",
321
- placeholder="['-y', '@modelcontextprotocol/server-memory']",
322
- info="The arguments to use to run the MCP server"
323
- )
324
- server1_name = gr.Textbox(
325
- label="Server Name",
326
- placeholder="server_1",
327
- info="A friendly name for your MCP server"
328
- )
329
- # Server management buttons
330
- with gr.Row():
331
- add_server_btn = gr.Button("+ Add Server", variant="secondary", size="sm")
332
- remove_server_btn = gr.Button("- Remove Server", variant="secondary", size="sm", interactive=False)
333
-
334
- # Track current server count
335
- server_count_state = gr.State(1)
336
-
337
- init_btn = gr.Button("Initialize Agent", variant="primary", size="lg")
338
- init_status = gr.Textbox(label="Status", interactive=False, max_lines=3)
339
-
340
- # Main chat area
341
- chat_column = gr.Column(scale=2)
342
- with chat_column:
343
- with gr.Row():
344
- gr.Markdown("## πŸ’¬ Chat with your MCP Agent")
345
- config_status = gr.Markdown("", visible=False) # Status when sidebar is collapsed
346
-
347
- chat_interface = gr.Column(visible=False)
348
- with chat_interface:
349
- chatbot = gr.Chatbot(
350
- label="Conversation",
351
- height=500,
352
- show_copy_button=True,
353
- avatar_images=("πŸ‘€", "πŸ€–")
354
- )
355
-
356
- with gr.Row():
357
- msg = gr.Textbox(
358
- label="Message",
359
- placeholder="Type your message here...",
360
- scale=4,
361
- lines=2
362
- )
363
- send_btn = gr.Button("Send", variant="primary", scale=1)
364
-
365
- error_display = gr.Textbox(
366
- label="Error Messages",
367
- visible=False,
368
- interactive=False
369
- )
370
-
371
- with gr.Row():
372
- reset_btn = gr.Button("Reset Conversation", variant="secondary")
373
- disconnect_btn = gr.Button("Disconnect Agent", variant="secondary")
374
-
375
- # Placeholder when agent is not initialized
376
- placeholder = gr.Markdown(
377
- "### πŸ‘‹ Welcome!\n\nPlease configure and initialize your MCP Agent using the sidebar to start chatting.",
378
- visible=True
379
- )
380
-
381
-
382
- # Server management event handlers
383
- add_server_btn.click(
384
- fn=add_server,
385
- inputs=[server_count_state],
386
- outputs=[server_count_state, server2_group, server3_group, add_server_btn, remove_server_btn]
387
- )
388
-
389
- remove_server_btn.click(
390
- fn=remove_server,
391
- inputs=[server_count_state],
392
- outputs=[server_count_state, server2_group, server3_group, add_server_btn, remove_server_btn]
393
- )
394
-
395
- # Event handlers
396
- init_btn.click(
397
- fn=initialize_agent_wrapper,
398
- inputs=[
399
- openai_key,
400
- server1_url, server1_name, server1_type, server1_headers,
401
- server2_url, server2_name, server2_type, server2_headers,
402
- server3_url, server3_name, server3_type, server3_headers,
403
- server1_command, server1_args, server1_name
404
- ],
405
- outputs=[chat_interface, placeholder, init_status]
406
- )
407
-
408
- # Chat functionality
409
- def handle_chat(message):
410
- if not message or not message.strip():
411
- return app_instance.chat_history, "Please provide a message.", ""
412
- return chat_wrapper(message)
413
-
414
- send_btn.click(
415
- fn=handle_chat,
416
- inputs=[msg],
417
- outputs=[chatbot, error_display, msg]
418
- ).then(
419
- lambda error: gr.update(visible=bool(error)),
420
- inputs=[error_display],
421
- outputs=[error_display]
422
- )
423
-
424
- msg.submit(
425
- fn=handle_chat,
426
- inputs=[msg],
427
- outputs=[chatbot, error_display, msg]
428
- ).then(
429
- lambda error: gr.update(visible=bool(error)),
430
- inputs=[error_display],
431
- outputs=[error_display]
432
- )
433
-
434
- reset_btn.click(
435
- fn=reset_wrapper,
436
- outputs=[chatbot, error_display]
437
- ).then(
438
- lambda error: gr.update(visible=bool(error)),
439
- inputs=[error_display],
440
- outputs=[error_display]
441
- )
442
-
443
- disconnect_btn.click(
444
- fn=disconnect_wrapper,
445
- outputs=[chatbot, chat_interface, placeholder, init_status]
446
- )
447
-
448
- if __name__ == "__main__":
449
- demo.launch(
450
- server_name="127.0.0.1",
451
- server_port=7860,
452
- share=False,
453
- show_error=True
454
- )
 
1
+ import gradio as gr
2
+ import asyncio
3
+ import json
4
+ import atexit
5
+ import signal
6
+ import sys
7
+ from mcp_agent.agent import MCP_Agent
8
+
9
+ class GradioMCPApp:
10
+ def __init__(self):
11
+ self.agent = None
12
+ self.chat_history = []
13
+ self._loop = None
14
+ self.server_count = 1
15
+
16
+ def get_or_create_loop(self):
17
+ """Get existing event loop or create a new one"""
18
+ if self._loop is None or self._loop.is_closed():
19
+ self._loop = asyncio.new_event_loop()
20
+ asyncio.set_event_loop(self._loop)
21
+ return self._loop
22
+
23
+ async def initialize_agent(self, openai_api_key, *server_configs):
24
+ """Initialize the MCP Agent with provided configuration"""
25
+ try:
26
+ # Clean up existing agent first
27
+ if self.agent:
28
+ await self.disconnect_agent()
29
+
30
+ # Build MCP servers configuration from form fields
31
+ # server_configs comes as a flat list: [url1, name1, type1, token1, url2, name2, type2, token2, ...]
32
+ mcp_servers = []
33
+ for i in range(0, len(server_configs), 4):
34
+ if i + 3 < len(server_configs):
35
+ server_url = server_configs[i]
36
+ server_name = server_configs[i + 1]
37
+ server_type = server_configs[i + 2]
38
+ headers = server_configs[i + 3]
39
+
40
+ if server_url and server_url.strip():
41
+ server_config = {
42
+ 'url': server_url.strip(),
43
+ 'name': server_name.strip() if server_name and server_name.strip() else f'server_{i//4 + 1}',
44
+ 'type': server_type if server_type else 'http',
45
+ 'headers': headers.strip() if headers and headers.strip() else None
46
+ }
47
+ mcp_servers.append(server_config)
48
+
49
+ # Initialize agent
50
+ api_keys = {'openai_api_key': openai_api_key}
51
+ self.agent = MCP_Agent(api_keys=api_keys, mpc_server_urls=mcp_servers)
52
+
53
+ # Connect to MCP servers
54
+ await self.agent.connect()
55
+
56
+ server_count = len(mcp_servers)
57
+ if server_count == 0:
58
+ return True, "Agent initialized successfully (no MCP servers configured)!"
59
+ else:
60
+ return True, f"Agent initialized successfully with {server_count} MCP server(s)!"
61
+
62
+ except Exception as e:
63
+ return False, f"Error initializing agent: {str(e)}"
64
+
65
+ async def chat_with_agent(self, message):
66
+ """Handle text chat with the agent"""
67
+ if not self.agent:
68
+ return self.chat_history, "Please initialize the agent first by providing your OpenAI API key and clicking 'Initialize Agent'."
69
+
70
+ if not message or not message.strip():
71
+ return self.chat_history, "Please provide a message."
72
+
73
+ try:
74
+ # Get response from agent
75
+ response = await self.agent.chat(message.strip())
76
+
77
+ # Update chat history
78
+ self.chat_history.append([message.strip(), str(response)])
79
+
80
+ return self.chat_history, ""
81
+
82
+ except Exception as e:
83
+ error_msg = f"Error during chat: {str(e)}"
84
+ self.chat_history.append([message, error_msg])
85
+ return self.chat_history, error_msg
86
+
87
+ async def reset_agent(self):
88
+ """Reset the agent's conversation history"""
89
+ if self.agent:
90
+ self.agent.reset()
91
+ self.chat_history = []
92
+ return [], "Agent conversation history reset successfully!"
93
+ else:
94
+ return [], "No agent to reset. Please initialize the agent first."
95
+
96
+ async def disconnect_agent(self):
97
+ """Disconnect from MCP servers"""
98
+ if self.agent:
99
+ try:
100
+ await self.agent.disconnect()
101
+ except Exception as e:
102
+ print(f"Error during disconnect: {e}")
103
+ finally:
104
+ self.agent = None
105
+ self.chat_history = []
106
+ return [], "Agent disconnected successfully!"
107
+
108
+ async def cleanup(self):
109
+ """Clean up resources"""
110
+ if self.agent:
111
+ await self.disconnect_agent()
112
+ if self._loop and not self._loop.is_closed():
113
+ self._loop.close()
114
+
115
+ # Create the app instance
116
+ app_instance = GradioMCPApp()
117
+
118
+ def run_async_safely(coro, *args):
119
+ """Safely run async function with proper error handling"""
120
+ loop = app_instance.get_or_create_loop()
121
+ try:
122
+ return loop.run_until_complete(coro(*args))
123
+ except Exception as e:
124
+ print(f"Error in async operation: {e}")
125
+ return None, f"Error: {str(e)}"
126
+
127
+ # Define async wrapper functions for Gradio
128
+ def initialize_agent_wrapper(openai_api_key, *server_configs):
129
+ success, message = run_async_safely(
130
+ app_instance.initialize_agent,
131
+ openai_api_key, *server_configs
132
+ )
133
+ if success is None:
134
+ return gr.update(visible=False), gr.update(visible=True), message
135
+ return gr.update(visible=success), gr.update(visible=not success), message
136
+
137
+ def chat_wrapper(message):
138
+ chat_history, error_msg = run_async_safely(app_instance.chat_with_agent, message)
139
+ if chat_history is None:
140
+ return [], error_msg, ""
141
+ return chat_history, error_msg, "" # Clear input
142
+
143
+ def reset_wrapper():
144
+ chat_history, message = run_async_safely(app_instance.reset_agent)
145
+ if chat_history is None:
146
+ return [], message
147
+ return chat_history, message
148
+
149
+ def disconnect_wrapper():
150
+ chat_history, message = run_async_safely(app_instance.disconnect_agent)
151
+ if chat_history is None:
152
+ return [], gr.update(visible=False), gr.update(visible=True), message
153
+ return chat_history, gr.update(visible=False), gr.update(visible=True), message
154
+
155
+ # Cleanup function for graceful shutdown
156
+ def cleanup_on_exit():
157
+ """Cleanup function to run on exit"""
158
+ try:
159
+ loop = app_instance.get_or_create_loop()
160
+ if not loop.is_closed():
161
+ loop.run_until_complete(app_instance.cleanup())
162
+ except Exception as e:
163
+ print(f"Error during cleanup: {e}")
164
+
165
+ # Register cleanup function
166
+ atexit.register(cleanup_on_exit)
167
+
168
+ # Handle SIGINT (Ctrl+C) gracefully
169
+ def signal_handler(signum, frame):
170
+ print("\nReceived interrupt signal. Cleaning up...")
171
+ cleanup_on_exit()
172
+ sys.exit(0)
173
+
174
+ signal.signal(signal.SIGINT, signal_handler)
175
+
176
+ # Server management functions
177
+ def add_server(current_count):
178
+ """Show the next server configuration"""
179
+ new_count = min(current_count + 1, 3) # Max 3 servers
180
+ return (
181
+ new_count,
182
+ gr.update(visible=new_count >= 2), # server2_group
183
+ gr.update(visible=new_count >= 3), # server3_group
184
+ gr.update(interactive=new_count < 3), # add_server_btn
185
+ gr.update(interactive=new_count > 1) # remove_server_btn
186
+ )
187
+
188
+ def remove_server(current_count):
189
+ """Hide the last server configuration"""
190
+ new_count = max(current_count - 1, 1) # Min 1 server
191
+ return (
192
+ new_count,
193
+ gr.update(visible=new_count >= 2), # server2_group
194
+ gr.update(visible=new_count >= 3), # server3_group
195
+ gr.update(interactive=new_count < 3), # add_server_btn
196
+ gr.update(interactive=new_count > 1) # remove_server_btn
197
+ )
198
+
199
+
200
+
201
+ # Create the Gradio interface
202
+ with gr.Blocks(title="MCP Agent Chat", theme=gr.themes.Soft()) as demo:
203
+ with gr.Row():
204
+
205
+ gr.Markdown("# MCP Agent Chat Interface")
206
+ gr.HTML("") # Balance spacing
207
+
208
+
209
+
210
+ with gr.Sidebar():
211
+ # Sidebar for configuration
212
+ sidebar_column = gr.Column(scale=1, min_width=350)
213
+ with sidebar_column:
214
+ gr.Markdown("## πŸ”§ Configuration")
215
+
216
+ openai_key = gr.Textbox(
217
+ label="OpenAI API Key",
218
+ type="password",
219
+ placeholder="sk-...",
220
+ info="Your OpenAI API key for the language model"
221
+ )
222
+
223
+ gr.Markdown("### MCP Servers Setup")
224
+ gr.Markdown("Configure your MCP server connections (leave all URLs empty to run without MCP servers)")
225
+
226
+ # Container for dynamic server configurations
227
+ servers_container = gr.Column()
228
+
229
+ # Initial server configuration
230
+ with servers_container:
231
+ # Server 1 (always present)
232
+ with gr.Tab("Url based servers"):
233
+ with gr.Group():
234
+ gr.Markdown("#### Server 1")
235
+ server1_url = gr.Textbox(
236
+ label="Server URL",
237
+ placeholder="http://localhost:8000",
238
+ info="The URL of your MCP server"
239
+ )
240
+ server1_name = gr.Textbox(
241
+ label="Server Name",
242
+ placeholder="server_1",
243
+ info="A friendly name for your MCP server"
244
+ )
245
+ server1_type = gr.Dropdown(
246
+ label="Server Type",
247
+ choices=["http", "SSE"],
248
+ value="http",
249
+ info="The type of MCP server connection"
250
+ )
251
+ server1_headers = gr.Textbox(
252
+ label="Headers (Optional)",
253
+ type="password",
254
+ placeholder="Leave empty if not required",
255
+ info="Headers for the MCP server (if required), example: {'Authorization': 'Bearer 1234567890'}"
256
+ )
257
+
258
+ # Server 2 (optional)
259
+ server2_group = gr.Group(visible=False)
260
+ with server2_group:
261
+ gr.Markdown("#### Server 2")
262
+ server2_url = gr.Textbox(
263
+ label="Server URL",
264
+ placeholder="http://localhost:8001",
265
+ info="The URL of your MCP server"
266
+ )
267
+ server2_name = gr.Textbox(
268
+ label="Server Name",
269
+ placeholder="server_2",
270
+ info="A friendly name for your MCP server"
271
+ )
272
+ server2_type = gr.Dropdown(
273
+ label="Server Type",
274
+ choices=["http", "SSE"],
275
+ value="http",
276
+ info="The type of MCP server connection"
277
+ )
278
+ server2_headers = gr.Textbox(
279
+ label="Headers (Optional)",
280
+ type="password",
281
+ placeholder="Leave empty if not required",
282
+ info="Headers for the MCP server (if required), example: {'Authorization': 'Bearer 1234567890'}"
283
+ )
284
+
285
+ # Server 3 (optional)
286
+ server3_group = gr.Group(visible=False)
287
+ with server3_group:
288
+ gr.Markdown("#### Server 3")
289
+ server3_url = gr.Textbox(
290
+ label="Server URL",
291
+ placeholder="http://localhost:8002",
292
+ info="The URL of your MCP server"
293
+ )
294
+ server3_name = gr.Textbox(
295
+ label="Server Name",
296
+ placeholder="server_3",
297
+ info="A friendly name for your MCP server"
298
+ )
299
+ server3_type = gr.Dropdown(
300
+ label="Server Type",
301
+ choices=["http", "SSE"],
302
+ value="http",
303
+ info="The type of MCP server connection"
304
+ )
305
+ server3_headers = gr.Textbox(
306
+ label="Headers (Optional)",
307
+ type="password",
308
+ placeholder="Leave empty if not required",
309
+ info="Headers for the MCP server (if required), example: {'Authorization': 'Bearer 1234567890'}"
310
+ )
311
+ with gr.Tab("Stdio based servers"):
312
+ with gr.Group():
313
+ gr.Markdown("#### Server 1")
314
+ server1_command = gr.Textbox(
315
+ label="Command",
316
+ placeholder="npx",
317
+ info="The command to use to run the MCP server, docker, npm, python, etc."
318
+ )
319
+ server1_args = gr.Textbox(
320
+ label="Arguments",
321
+ placeholder="['-y', '@modelcontextprotocol/server-memory']",
322
+ info="The arguments to use to run the MCP server"
323
+ )
324
+ server1_name = gr.Textbox(
325
+ label="Server Name",
326
+ placeholder="server_1",
327
+ info="A friendly name for your MCP server"
328
+ )
329
+ # Server management buttons
330
+ with gr.Row():
331
+ add_server_btn = gr.Button("+ Add Server", variant="secondary", size="sm")
332
+ remove_server_btn = gr.Button("- Remove Server", variant="secondary", size="sm", interactive=False)
333
+
334
+ # Track current server count
335
+ server_count_state = gr.State(1)
336
+
337
+ init_btn = gr.Button("Initialize Agent", variant="primary", size="lg")
338
+ init_status = gr.Textbox(label="Status", interactive=False, max_lines=3)
339
+
340
+ # Main chat area
341
+ chat_column = gr.Column(scale=2)
342
+ with chat_column:
343
+ with gr.Row():
344
+ gr.Markdown("## πŸ’¬ Chat with your MCP Agent")
345
+ config_status = gr.Markdown("", visible=False) # Status when sidebar is collapsed
346
+
347
+ chat_interface = gr.Column(visible=False)
348
+ with chat_interface:
349
+ chatbot = gr.Chatbot(
350
+ label="Conversation",
351
+ height=500,
352
+ show_copy_button=True,
353
+ avatar_images=("πŸ‘€", "πŸ€–")
354
+ )
355
+
356
+ with gr.Row():
357
+ msg = gr.Textbox(
358
+ label="Message",
359
+ placeholder="Type your message here...",
360
+ scale=4,
361
+ lines=2
362
+ )
363
+ send_btn = gr.Button("Send", variant="primary", scale=1)
364
+
365
+ error_display = gr.Textbox(
366
+ label="Error Messages",
367
+ visible=False,
368
+ interactive=False
369
+ )
370
+
371
+ with gr.Row():
372
+ reset_btn = gr.Button("Reset Conversation", variant="secondary")
373
+ disconnect_btn = gr.Button("Disconnect Agent", variant="secondary")
374
+
375
+ # Placeholder when agent is not initialized
376
+ placeholder = gr.Markdown(
377
+ "### πŸ‘‹ Welcome!\n\nPlease configure and initialize your MCP Agent using the sidebar to start chatting.",
378
+ visible=True
379
+ )
380
+
381
+
382
+ # Server management event handlers
383
+ add_server_btn.click(
384
+ fn=add_server,
385
+ inputs=[server_count_state],
386
+ outputs=[server_count_state, server2_group, server3_group, add_server_btn, remove_server_btn]
387
+ )
388
+
389
+ remove_server_btn.click(
390
+ fn=remove_server,
391
+ inputs=[server_count_state],
392
+ outputs=[server_count_state, server2_group, server3_group, add_server_btn, remove_server_btn]
393
+ )
394
+
395
+ # Event handlers
396
+ init_btn.click(
397
+ fn=initialize_agent_wrapper,
398
+ inputs=[
399
+ openai_key,
400
+ server1_url, server1_name, server1_type, server1_headers,
401
+ server2_url, server2_name, server2_type, server2_headers,
402
+ server3_url, server3_name, server3_type, server3_headers,
403
+ server1_command, server1_args, server1_name
404
+ ],
405
+ outputs=[chat_interface, placeholder, init_status]
406
+ )
407
+
408
+ # Chat functionality
409
+ def handle_chat(message):
410
+ if not message or not message.strip():
411
+ return app_instance.chat_history, "Please provide a message.", ""
412
+ return chat_wrapper(message)
413
+
414
+ send_btn.click(
415
+ fn=handle_chat,
416
+ inputs=[msg],
417
+ outputs=[chatbot, error_display, msg]
418
+ ).then(
419
+ lambda error: gr.update(visible=bool(error)),
420
+ inputs=[error_display],
421
+ outputs=[error_display]
422
+ )
423
+
424
+ msg.submit(
425
+ fn=handle_chat,
426
+ inputs=[msg],
427
+ outputs=[chatbot, error_display, msg]
428
+ ).then(
429
+ lambda error: gr.update(visible=bool(error)),
430
+ inputs=[error_display],
431
+ outputs=[error_display]
432
+ )
433
+
434
+ reset_btn.click(
435
+ fn=reset_wrapper,
436
+ outputs=[chatbot, error_display]
437
+ ).then(
438
+ lambda error: gr.update(visible=bool(error)),
439
+ inputs=[error_display],
440
+ outputs=[error_display]
441
+ )
442
+
443
+ disconnect_btn.click(
444
+ fn=disconnect_wrapper,
445
+ outputs=[chatbot, chat_interface, placeholder, init_status]
446
+ )
447
+
448
+ if __name__ == "__main__":
449
+ demo.launch(
450
+ server_name="127.0.0.1",
451
+ server_port=7860,
452
+ share=False,
453
+ show_error=True
454
+ )