VibecoderMcSwaggins commited on
Commit
10e234c
·
1 Parent(s): c114650

feat: enhance Gradio UI with MCP server support and new tool interfaces

Browse files

- Updated the Gradio app to include support for the MCP server, enabling integration with Claude Desktop.
- Added new interfaces for PubMed, Clinical Trials, bioRxiv, and a comprehensive search tool, enhancing the research capabilities.
- Modified the demo creation function to reflect these updates and ensure a seamless user experience.

Files modified:
- src/app.py

README.md CHANGED
@@ -30,11 +30,35 @@ uv sync
30
 
31
  ```bash
32
  # Start the Gradio app
33
- uv run python -m src.app
34
  ```
35
 
36
  Open your browser to `http://localhost:7860`.
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  ## Development
39
 
40
  ### Run Tests
@@ -53,13 +77,12 @@ make check
53
 
54
  DeepCritical uses a Vertical Slice Architecture:
55
 
56
- 1. **Search Slice**: Retrieving evidence from PubMed and the Web.
57
  2. **Judge Slice**: Evaluating evidence quality using LLMs.
58
  3. **Orchestrator Slice**: Managing the research loop and UI.
59
 
60
  Built with:
61
  - **PydanticAI**: For robust agent interactions.
62
  - **Gradio**: For the streaming user interface.
63
- - **PubMed**: For biomedical literature.
64
- - **DuckDuckGo**: For general web search.
65
-
 
30
 
31
  ```bash
32
  # Start the Gradio app
33
+ uv run python src/app.py
34
  ```
35
 
36
  Open your browser to `http://localhost:7860`.
37
 
38
+ ### 3. Connect via MCP
39
+
40
+ This application exposes a Model Context Protocol (MCP) server, allowing you to use its search tools directly from Claude Desktop or other MCP clients.
41
+
42
+ **MCP Server URL**: `http://localhost:7860/gradio_api/mcp/`
43
+
44
+ **Claude Desktop Configuration**:
45
+ Add this to your `claude_desktop_config.json`:
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "deepcritical": {
50
+ "url": "http://localhost:7860/gradio_api/mcp/"
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ **Available Tools**:
57
+ - `search_pubmed`: Search peer-reviewed biomedical literature.
58
+ - `search_clinical_trials`: Search ClinicalTrials.gov.
59
+ - `search_biorxiv`: Search bioRxiv/medRxiv preprints.
60
+ - `search_all`: Search all sources simultaneously.
61
+
62
  ## Development
63
 
64
  ### Run Tests
 
77
 
78
  DeepCritical uses a Vertical Slice Architecture:
79
 
80
+ 1. **Search Slice**: Retrieving evidence from PubMed, ClinicalTrials.gov, and bioRxiv.
81
  2. **Judge Slice**: Evaluating evidence quality using LLMs.
82
  3. **Orchestrator Slice**: Managing the research loop and UI.
83
 
84
  Built with:
85
  - **PydanticAI**: For robust agent interactions.
86
  - **Gradio**: For the streaming user interface.
87
+ - **PubMed, ClinicalTrials.gov, bioRxiv**: For biomedical data.
88
+ - **MCP**: For universal tool access.
 
src/app.py CHANGED
@@ -1,4 +1,4 @@
1
- """Gradio UI for DeepCritical agent."""
2
 
3
  import os
4
  from collections.abc import AsyncGenerator
@@ -7,6 +7,12 @@ from typing import Any
7
  import gradio as gr
8
 
9
  from src.agent_factory.judges import JudgeHandler, MockJudgeHandler
 
 
 
 
 
 
10
  from src.orchestrator_factory import create_orchestrator
11
  from src.tools.biorxiv import BioRxivTool
12
  from src.tools.clinicaltrials import ClinicalTrialsTool
@@ -115,10 +121,10 @@ async def research_agent(
115
 
116
  def create_demo() -> Any:
117
  """
118
- Create the Gradio demo interface.
119
 
120
  Returns:
121
- Configured Gradio Blocks interface
122
  """
123
  with gr.Blocks(
124
  title="DeepCritical - Drug Repurposing Research Agent",
@@ -137,9 +143,10 @@ def create_demo() -> Any:
137
  - "What existing medications show promise for Long COVID?"
138
  """)
139
 
 
140
  gr.ChatInterface(
141
  fn=research_agent,
142
- type="messages",
143
  title="",
144
  examples=[
145
  "What drugs could be repurposed for Alzheimer's disease?",
@@ -157,24 +164,74 @@ def create_demo() -> Any:
157
  ],
158
  )
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  gr.Markdown("""
161
  ---
162
  **Note**: This is a research tool and should not be used for medical decisions.
163
  Always consult healthcare professionals for medical advice.
164
 
165
- Built with 🤖 PydanticAI + 🔬 PubMed, ClinicalTrials.gov & bioRxiv
 
 
166
  """)
167
 
168
  return demo
169
 
170
 
171
  def main() -> None:
172
- """Run the Gradio app."""
173
  demo = create_demo()
174
  demo.launch(
175
  server_name="0.0.0.0",
176
  server_port=7860,
177
  share=False,
 
178
  )
179
 
180
 
 
1
+ """Gradio UI for DeepCritical agent with MCP server support."""
2
 
3
  import os
4
  from collections.abc import AsyncGenerator
 
7
  import gradio as gr
8
 
9
  from src.agent_factory.judges import JudgeHandler, MockJudgeHandler
10
+ from src.mcp_tools import (
11
+ search_all_sources,
12
+ search_biorxiv,
13
+ search_clinical_trials,
14
+ search_pubmed,
15
+ )
16
  from src.orchestrator_factory import create_orchestrator
17
  from src.tools.biorxiv import BioRxivTool
18
  from src.tools.clinicaltrials import ClinicalTrialsTool
 
121
 
122
  def create_demo() -> Any:
123
  """
124
+ Create the Gradio demo interface with MCP support.
125
 
126
  Returns:
127
+ Configured Gradio Blocks interface with MCP server enabled
128
  """
129
  with gr.Blocks(
130
  title="DeepCritical - Drug Repurposing Research Agent",
 
143
  - "What existing medications show promise for Long COVID?"
144
  """)
145
 
146
+ # Main chat interface (existing)
147
  gr.ChatInterface(
148
  fn=research_agent,
149
+ type="messages", # type: ignore
150
  title="",
151
  examples=[
152
  "What drugs could be repurposed for Alzheimer's disease?",
 
164
  ],
165
  )
166
 
167
+ # MCP Tool Interfaces (exposed via MCP protocol)
168
+ gr.Markdown("---\n## MCP Tools (Also Available via Claude Desktop)")
169
+
170
+ with gr.Tab("PubMed Search"):
171
+ gr.Interface(
172
+ fn=search_pubmed,
173
+ inputs=[
174
+ gr.Textbox(label="Query", placeholder="metformin alzheimer"),
175
+ gr.Slider(1, 50, value=10, step=1, label="Max Results"),
176
+ ],
177
+ outputs=gr.Markdown(label="Results"),
178
+ api_name="search_pubmed",
179
+ )
180
+
181
+ with gr.Tab("Clinical Trials"):
182
+ gr.Interface(
183
+ fn=search_clinical_trials,
184
+ inputs=[
185
+ gr.Textbox(label="Query", placeholder="diabetes phase 3"),
186
+ gr.Slider(1, 50, value=10, step=1, label="Max Results"),
187
+ ],
188
+ outputs=gr.Markdown(label="Results"),
189
+ api_name="search_clinical_trials",
190
+ )
191
+
192
+ with gr.Tab("Preprints"):
193
+ gr.Interface(
194
+ fn=search_biorxiv,
195
+ inputs=[
196
+ gr.Textbox(label="Query", placeholder="long covid treatment"),
197
+ gr.Slider(1, 50, value=10, step=1, label="Max Results"),
198
+ ],
199
+ outputs=gr.Markdown(label="Results"),
200
+ api_name="search_biorxiv",
201
+ )
202
+
203
+ with gr.Tab("Search All"):
204
+ gr.Interface(
205
+ fn=search_all_sources,
206
+ inputs=[
207
+ gr.Textbox(label="Query", placeholder="metformin cancer"),
208
+ gr.Slider(1, 20, value=5, step=1, label="Max Per Source"),
209
+ ],
210
+ outputs=gr.Markdown(label="Results"),
211
+ api_name="search_all",
212
+ )
213
+
214
  gr.Markdown("""
215
  ---
216
  **Note**: This is a research tool and should not be used for medical decisions.
217
  Always consult healthcare professionals for medical advice.
218
 
219
+ Built with PydanticAI + PubMed, ClinicalTrials.gov & bioRxiv
220
+
221
+ **MCP Server**: Available at `/gradio_api/mcp/` for Claude Desktop integration
222
  """)
223
 
224
  return demo
225
 
226
 
227
  def main() -> None:
228
+ """Run the Gradio app with MCP server enabled."""
229
  demo = create_demo()
230
  demo.launch(
231
  server_name="0.0.0.0",
232
  server_port=7860,
233
  share=False,
234
+ mcp_server=True, # Enable MCP server
235
  )
236
 
237
 
tests/integration/test_mcp_server.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Integration tests for MCP server functionality."""
2
+
3
+ import pytest
4
+
5
+
6
+ class TestMCPServerIntegration:
7
+ """Integration tests for MCP server (requires running app)."""
8
+
9
+ @pytest.mark.integration
10
+ @pytest.mark.asyncio
11
+ async def test_mcp_tools_work_end_to_end(self) -> None:
12
+ """Test that MCP tools execute real searches."""
13
+ from src.mcp_tools import search_pubmed
14
+
15
+ result = await search_pubmed("metformin diabetes", 3)
16
+
17
+ assert isinstance(result, str)
18
+ assert "PubMed Results" in result
19
+ # Should have actual content (not just "no results")
20
+ # Typical queries should return something.
21
+ # The wrapper returns "No PubMed results found" string if empty.
22
+
23
+ if "No PubMed results found" not in result:
24
+ assert len(result) > 10
tests/unit/test_mcp_tools.py CHANGED
@@ -184,17 +184,17 @@ class TestMCPTypeHints:
184
  sig = inspect.signature(search_pubmed)
185
 
186
  # Check parameter hints
187
- assert sig.parameters["query"].annotation == str
188
- assert sig.parameters["max_results"].annotation == int
189
 
190
  # Check return hint
191
- assert sig.return_annotation == str
192
 
193
  def test_search_clinical_trials_type_hints(self) -> None:
194
  """All parameters and return must have type hints."""
195
  import inspect
196
 
197
  sig = inspect.signature(search_clinical_trials)
198
- assert sig.parameters["query"].annotation == str
199
- assert sig.parameters["max_results"].annotation == int
200
- assert sig.return_annotation == str
 
184
  sig = inspect.signature(search_pubmed)
185
 
186
  # Check parameter hints
187
+ assert sig.parameters["query"].annotation is str
188
+ assert sig.parameters["max_results"].annotation is int
189
 
190
  # Check return hint
191
+ assert sig.return_annotation is str
192
 
193
  def test_search_clinical_trials_type_hints(self) -> None:
194
  """All parameters and return must have type hints."""
195
  import inspect
196
 
197
  sig = inspect.signature(search_clinical_trials)
198
+ assert sig.parameters["query"].annotation is str
199
+ assert sig.parameters["max_results"].annotation is int
200
+ assert sig.return_annotation is str