This project demonstrates how to use the Model Context Protocol (MCP) to connect a Jupyter notebook to the JupyterHealth Exchange server, enabling LLM-powered queries over health data using the NRP-hosted Qwen3 model.
The JHE Notebook connects:
- MCP Client → JupyterHealth Exchange MCP Server (local, port 8001)
- OpenAI Client → NRP Qwen3 LLM (hosted at NRP)
- Integration: LLM uses MCP tools to query health data and respond in natural language
┌─────────────────┐
│ Jupyter Notebook│
│ (This Project) │
└────┬────────┬───┘
│ │
│ └──────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌──────────────┐
│ MCP Client │ │ OpenAI Client│
│ (Python SDK) │ │ (NRP API) │
└────┬────────────┘ └──────┬───────┘
│ │
│ │
▼ ▼
┌─────────────────┐ ┌──────────────┐
│ JHE MCP Server │ │ NRP Qwen3 │
│ localhost:8001 │ │ Model │
└────┬────────────┘ └──────────────┘
│
▼
┌─────────────────┐
│ JupyterHealth │
│ Exchange (JHE) │
│ localhost:8000 │
└─────────────────┘
Important: You need the MCP-enabled version of JupyterHealth Exchange.
Clone the JupyterHealth Exchange repository and checkout the MCP feature branch:
git clone https://github.com/jupyterhealth/jupyterhealth-exchange.git
cd jupyterhealth-exchange
# TODO: Update this when PR is merged - for now use the feature branch
git fetch origin pull/XXX/head:mcp-support # Replace XXX with actual PR number
git checkout mcp-supportFollow the JupyterHealth Exchange setup instructions to:
- Install dependencies
- Configure the database
- Seed test data
Important: The MCP Python SDK requires Python 3.10 or later.
Check your Python version:
python3 --version # Should be 3.10 or higherIf needed, use a specific Python version:
# macOS with Homebrew
brew install python@3.11
# Use specific Python version for venv
python3.11 -m venv .venvJupyterHealth Exchange Server must be running:
cd /path/to/jupyterhealth-exchange
.venv/bin/python manage.py runserverMCP Server must be running:
cd /path/to/jupyterhealth-exchange
./start_mcp_server.shVerify servers are healthy:
curl http://localhost:8000/admin/ # Should return 200
curl http://localhost:8001/health # Should return {"status": "healthy"}You need an API key from the National Research Platform:
- Visit https://portal.nrp.ai/
- Create an account or log in
- Generate an API token
- Note the model name (e.g.,
qwen3)
Important: The MCP Python SDK requires nest-asyncio to work in Jupyter notebooks:
- Jupyter runs an active asyncio event loop
- MCP SDK uses async/await extensively
- nest-asyncio patches the loop to allow nested async operations
This is automatically installed via requirements.txt and applied in the notebook's first cell.
Security Note: All queries go through the MCP server which enforces OAuth authentication and permission filtering. Never query the database directly - the MCP server is the gatekeeper that ensures:
- OAuth authentication with JWT verification
- Permission filtering by study_ids from ID token claims
- SQL injection protection
- Read-only operations only
The MCP server uses OAuth to authenticate with JHE. You should have already completed the OAuth flow (tokens cached at ~/.jhe_mcp/token_cache.json). If not, the notebook will guide you through authentication.
git clone https://github.com/the-commons-project/jhe-notebook.git
cd jhe-notebookImportant: Use Python 3.10+ (e.g., python3.11)
# Use Python 3.11 (or 3.10+)
python3.11 -m venv .venv
# Activate the environment
source .venv/bin/activate # On Windows: .venv\Scripts\activatepip install -r requirements.txtcp .env.example .env
# Edit .env and add your NRP API keyExample .env:
JHE_MCP_URL=http://localhost:8001/sse
NRP_BASE_URL=https://ellm.nrp-nautilus.io/v1
NRP_API_KEY=your_actual_api_key_here
NRP_MODEL=qwen3
jupyter notebookNavigate to notebooks/jhe_mcp_llm_demo.ipynb and open it.
The demonstration notebook shows:
- MCP Connection: Connect to the local JHE MCP server via SSE
- Tool Discovery: List available MCP tools (get_study_count, list_studies, execute_filtered_query, etc.)
- LLM Integration: Use Qwen3 to interpret natural language queries
- Function Calling: LLM decides which MCP tools to call
- Data Retrieval: Execute SQL queries filtered by user permissions
- Natural Language Response: LLM formats results for human consumption
# Simple query
"How many studies are in the system?"
# Complex health data query
"Show me all blood pressure readings for patient 40001 on January 1, 2025,
and calculate the average systolic pressure"
# Time-series aggregation
"Give me hourly averages and standard deviations for patient 40001's
blood pressure data over the first week of January"The JHE MCP Server provides these tools:
- get_study_count: Count studies accessible to authenticated user
- list_studies: List all studies with IDs, names, and organizations
- get_patient_demographics: Get patient demographics for a specific study
- get_study_metadata: Get detailed metadata about a study
- get_patient_observations: Get FHIR observations for a specific patient
- get_database_schema: Get comprehensive database schema with examples
- execute_filtered_query: Execute arbitrary SQL SELECT queries (auto-filtered by permissions)
- All MCP tool calls are authenticated via OAuth
- SQL queries are automatically filtered based on user permissions
- Users can only access studies/patients they have permission to view
- Only SELECT queries allowed (no INSERT/UPDATE/DELETE)
- Dangerous SQL operations are blocked
asyncio.CancelledError: Cancelled via cancel scope...
Solution: Make sure Cell 1 includes the nest_asyncio setup:
import nest_asyncio
nest_asyncio.apply()If you see this error, check that Cell 1 has been run and includes the nest_asyncio code. See NOTEBOOK_UPDATES.md for detailed instructions.
Error: Connection refused to localhost:8001
Solution: Ensure the MCP server is running:
curl http://localhost:8001/healthError: Authentication failed
Solution: Re-authenticate by deleting the token cache:
rm ~/.jhe_mcp/token_cache.jsonThen restart the notebook - it will trigger OAuth flow.
Error: 401 Unauthorized
Solution: Check your .env file has the correct NRP_API_KEY.
"row_count": 0
Solution:
- Verify you're logged in as a user with study access (e.g., mary@example.com)
- Check that test data exists in the database
- Ensure patient IDs are correct (e.g., 40001)
jhe-notebook/
├── README.md # This file
├── requirements.txt # Python dependencies
├── .env.example # Environment variable template
├── .env # Your actual config (gitignored)
├── .gitignore # Git ignore rules
└── notebooks/
└── jhe_mcp_llm_demo.ipynb # Main demonstration notebook
Create new .ipynb files in the notebooks/ directory. They will automatically have access to the same dependencies and configuration.
This is a demonstration project showing MCP integration patterns. Feel free to:
- Add new example notebooks
- Create helper utilities
- Improve error handling
- Add data visualizations
This project will be published to The Commons Project GitHub organization at:
https://github.com/the-commons-project/jhe-notebook
Apache 2.0 (same as other Commons Project repositories)