GxP MDM MCP Server
by saram-io
README.md
# GxP MDM MCP Server - Cypher Tools for Claude / ChatGPT / Cursor
This MCP server exposes your **Computerized System Inventory as a Knowledge Graph** via Cypher queries. Any Claude/ChatGPT agent can now query GxP master data without hallucinating.
## Architecture
```
Claude / ChatGPT / Cursor
|
| MCP (stdio)
v
mcp_server.py (14 tools)
|
| Cypher queries
v
Neo4j (or MOCK mode: NetworkX + JSON) <- your MDM golden record
```
### Why Cypher?
- **Blast radius** is a graph traversal: `MATCH (start)-[:SENDS_VIA*1..3]->(downstream)` — impossible in SQL
- **Data lineage** for ALCOA+ investigations
- **Regulation ground truth** — agent can only cite clauses returned by Cypher, prevents hallucination
## 14 Tools Exposed
| Tool | Cypher | What it does |
|------|--------|--------------|
| `cypher_query` | Custom | Safe read-only Cypher for exploration |
| `list_all_systems` | `MATCH (s:ComputerizedSystem) RETURN` | Full inventory - auditors ask this first |
| `get_system_details` | Full subgraph | System + functions + e-records + supplier + interfaces |
| `get_blast_radius` | `MATCH (start)-[:SENDS_VIA*1..$depth]->(downstream)` | **Killer app**: What downstream GxP systems are impacted by change? |
| `get_upstream_lineage` | Reverse traversal | Where does data come from? |
| `get_applicable_regulations` | `(s)-[:HAS_FUNCTION]->(f)-[:REGULATED_BY]->(reg)` | Anti-hallucination: only these clauses can be cited |
| `get_system_interfaces` | `(s)-[flow:SENDS_VIA]->(target)` | API/file/manual interfaces with GxP flag |
| `find_validation_gaps` | `WHERE validation_status <> 'Validated'` | Non-validated GxP Direct systems |
| `find_periodic_review_overdue` | `WHERE next_review < date()` | Overdue periodic reviews |
| `find_high_risk_functions` | `WHERE is_gxp_critical AND risk=High` | E-sig, batch release, potency |
| `get_supplier_risk` | `(s)-[:SUPPLIED_BY]->(sup)` | Supplier audit status, SOC2 |
| `get_data_lineage` | `MATCH (s)-[:GENERATES]->(e:ElectronicRecord)` | Lineage for a record type |
| `get_gxp_inventory` | `WHERE gxp_impact='Direct'` | GxP Direct inventory |
| `assess_change_impact` | Composite | **Orchestrates 3 Cypher queries** + generates impact assessment per GAMP 5 / CSA |
## Quick Start (No Neo4j needed - Mock Mode)
```bash
cd gxp_mdm_mcp_server
pip install -r requirements.txt
# Mock mode: uses JSON + NetworkX, no Neo4j required
python scripts/test_tools.py
# Should show:
# - List 4 systems
# - Veeva QMS details with downstream SAP
# - Blast radius: Veeva -> SAP
# - REJECT for audit trail purge
# - Minor for version upgrade
```
Mock mode is perfect for POC and Claude Desktop testing.
## Prod Mode with Neo4j
```bash
# .env - set Neo4j creds
cp .env.example .env
# Edit .env with your Neo4j URI
# Start Neo4j
docker-compose up -d neo4j
# Load sample data + schema
python scripts/load_sample_data.py
# Test with Neo4j
python scripts/test_tools.py
# Start API harness (optional)
uvicorn src.api_server:app --reload --port 8000
# http://localhost:8000/cypher/list_all_systems
# http://localhost:8000/cypher/blast_radius?system_id=SYS-VEEVA-QMS-001
```
## Claude Desktop Config
1. Find your Claude config: `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac) or `%APPDATA%/Claude/claude_desktop_config.json` (Win)
2. Add (use absolute path):
```json
{
"mcpServers": {
"gxp-mdm-cypher": {
"command": "python",
"args": ["/absolute/path/to/gxp_mdm_mcp_server/src/mcp_server.py"],
"env": {
"NEO4J_URI": "",
"NEO4J_USERNAME": "neo4j",
"NEO4J_PASSWORD": "password"
}
}
}
}
```
For mock mode, leave `NEO4J_URI` empty. For Neo4j, set to `bolt://localhost:7687`.
3. Restart Claude Desktop. You should see 14 tools under 🔌.
4. Try prompts:
```
List all GxP Direct systems in my inventory
> calls list_all_systems(gxp_impact="Direct")
What happens if I change Veeva QMS? Show blast radius
> calls get_blast_radius(system_id="SYS-VEEVA-QMS-001")
Assess this change: Enable audit trail purge after 7 years for Veeva QMS
> calls assess_change_impact -> should REJECT per 21CFR11.10(e)
Assess Veeva upgrade from 24R1 to 24R2 with no e-sig change
> calls assess_change_impact -> should be Minor per CSA low risk
```
## Cursor Config
See `config/cursor_config.json.example` - add to `.cursor/mcp.json`
## ChatGPT (with MCP support)
If using ChatGPT custom GPT with MCP, use `config/chatgpt_mcp_config.json` as reference. ChatGPT will call tools via stdio.
## Cypher Queries - Ground Truth
All queries in `src/cypher_tools.py`. Key ones:
**Blast radius (the moat):**
```cypher
MATCH (start:ComputerizedSystem {system_id: $system_id})
MATCH path = (start)-[:SENDS_VIA*1..$depth]->(downstream:ComputerizedSystem)
WHERE downstream.gxp_impact IN ['Direct', 'GxP Relevant']
RETURN downstream.system_id, length(path) as distance
```
**Regulation anti-hallucination:**
```cypher
MATCH (s:ComputerizedSystem {system_id: $system_id})
OPTIONAL MATCH (s)-[:HAS_FUNCTION]->(f)-[:REGULATED_BY]->(reg)
RETURN collect(DISTINCT reg) as regulations
```
Agent must ONLY cite clause_ids returned here.
## From POC to Production
1. Replace `data/*.json` with real Veeva Vault API + ServiceNow CMDB + Okta
2. Add write tools (with approval workflow) for updating validation_status
3. Add vector search tool for regulation RAG (embed GAMP 5 2nd Ed)
4. Add periodic review agent that calls `find_periodic_review_overdue` on schedule
You now own the layer every CSV agent must query.
## Troubleshooting
- `No module named mcp`: `pip install mcp`
- Claude doesn't see tools: Check absolute path in config, restart Claude, check logs `~/Library/Logs/Claude/mcp*.log`
- Neo4j connection fails: Falls back to mock mode automatically - check `NEO4J_URI`
Good luck cornering the market.
---
## NEW: Write Tools with Approval Workflow (GxP Compliant)
### Why approval workflow?
21 CFR Part 11.50 and Annex 11 require:
- 2-person rule (requester != approver)
- Reason for change (ALCOA+ Complete)
- Electronic signature manifestation (who, when, meaning)
- Immutable audit trail (who, when, old/new, reason)
All write tools enforce this.
### Write Tools Added
| Tool | What it does | GxP Control |
|------|--------------|-------------|
| `request_validation_status_change_tool` | Create pending change request | Logs REQUEST audit, requires reason >=10 chars |
| `approve_validation_status_change_tool` | Approve + apply change | Enforces requester != approver, logs e-sig, updates system + next review date |
| `reject_validation_status_change_tool` | Reject with reason | Logs REJECT audit |
| `list_pending_changes_tool` | List pending QA approvals | |
| `get_audit_trail_tool` | Full audit trail with e-signatures | 21CFR11.10(e), Annex 11.7 |
### Example Workflow via Claude
```
You: Change Veeva QMS status to Validated - Change Implemented, reason: IQ/OQ passed for 24R2 upgrade, requested by qa.john
Claude calls:
request_validation_status_change_tool(system_id="SYS-VEEVA-QMS-001", new_status="Validated - Change Implemented", reason="IQ/OQ passed for 24R2 upgrade, evidence in Veeva Vault VP-2024-089", requested_by="qa.john@company.com")
-> Returns request_id CHG-VAL-A1B2C3, status Pending Approval
You: Approve CHG-VAL-A1B2C3 by qa.sarah, comment: Reviewed IQ/OQ, approved
Claude calls:
approve_validation_status_change_tool(request_id="CHG-VAL-A1B2C3", approver="qa.sarah@company.com", approval_comment="Reviewed IQ/OQ")
-> Applies change, updates sample_systems.json, creates audit trail with e-signature
You: Show audit trail for Veeva QMS
Claude calls:
get_audit_trail_tool(system_id="SYS-VEEVA-QMS-001")
-> Returns who, when, old/new, reason, electronic signature manifestation
```
Try via API:
```bash
curl -X POST http://localhost:8000/request-status-change -H "Content-Type: application/json" -d '{"system_id":"SYS-VEEVA-QMS-001","new_status":"Validated - Change Implemented","reason":"IQ/OQ passed for 24R2 upgrade","requested_by":"qa.john@company.com"}'
curl -X POST http://localhost:8000/approve-status-change -d '{"request_id":"CHG-VAL-A1B2C3","approver":"qa.sarah@company.com","approval_comment":"Reviewed"}'
curl http://localhost:8000/audit-trail?system_id=SYS-VEEVA-QMS-001
```
## NEW: Scheduled Periodic Review Agent
### What it does
Automatically:
1. Calls `find_periodic_review_overdue` Cypher daily at 8am (via APScheduler)
2. For each overdue GxP Direct system, generates draft Periodic Review Report per GAMP 5 Section 6.5 and Annex 11.11
3. Saves draft JSON + logs audit trail CREATE_DRAFT
Each draft includes 10 sections auditors expect:
1. System description (from MDM)
2. Changes since last review (from mock Jira)
3. Deviations/incidents
4. Audit trail review (sample, immutable check)
5. User access review (dormant accounts)
6. Backup/restore test
7. Supplier review (SOC2)
8. Data integrity ALCOA+
9. Interfaces - blast radius from MDM graph
10. Conclusion + required actions
### Tools
| Tool | Purpose |
|------|---------|
| `run_periodic_review_scan` | Scan overdue (Cypher) |
| `generate_periodic_review_drafts` | Scan + generate drafts for all overdue |
| `generate_single_periodic_review` | Generate for one system |
| `list_periodic_review_drafts` | List drafts |
### Run Scheduler
```bash
# Run once manually
python -c "from src.periodic_review_agent.agent import agent; print(agent.scan_overdue())"
# Generate drafts
python -c "from src.periodic_review_agent.agent import agent; print(agent.generate_drafts_for_overdue())"
# Run as daemon (daily 8am + every 6h demo)
python src/periodic_review_agent/scheduler.py
```
In prod, replace APScheduler with Airflow DAG or Temporal workflow that calls the MCP tool `run_periodic_review_scan`.
### Via Claude
```
You: Run periodic review scan
Claude calls run_periodic_review_scan() -> finds 2 overdue systems
You: Generate drafts for overdue
Claude calls generate_periodic_review_drafts() -> creates PR-SYS-VEEVA-QMS-001-2025-XXXX reports
You: Show me draft for Veeva QMS
> Returns full 10-section report
```
Drafts saved to `data/periodic_reviews/PR-*.json` - ready to upload to Veeva Vault as ValidationArtifact.
---
This v2 makes you audit-ready: writes are controlled, audit trail is ALCOA+, and periodic reviews are automated — exactly what QA directors pay consultants $200/hr for.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues