Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,68 @@ def from_arrows(cls, arrows_data_model_dict: dict[str, Any]) -> "DataModel":
}
return cls(nodes=nodes, relationships=relationships, metadata=metadata)

@classmethod
def from_dict(cls, data_model_dict: dict[str, Any]) -> "DataModel":
"Convert a dictionary representation to a DataModel object."
# Convert nodes
nodes = []
for node_data in data_model_dict["nodes"]:
# Create key property
key_prop = Property(
name=node_data["key_property"]["name"],
type=node_data["key_property"]["type"]
)

# Create other properties
properties = []
for prop_data in node_data.get("properties", []):
prop = Property(
name=prop_data["name"],
type=prop_data["type"]
)
properties.append(prop)

# Create node
node = Node(
label=node_data["label"],
key_property=key_prop,
properties=properties
)
nodes.append(node)

# Convert relationships
relationships = []
for rel_data in data_model_dict["relationships"]:
# Create key property if it exists
key_property = None
if "key_property" in rel_data:
key_property = Property(
name=rel_data["key_property"]["name"],
type=rel_data["key_property"]["type"]
)

# Create other properties
properties = []
for prop_data in rel_data.get("properties", []):
prop = Property(
name=prop_data["name"],
type=prop_data["type"]
)
properties.append(prop)

# Create relationship
relationship = Relationship(
type=rel_data["type"],
start_node_label=rel_data["start_node_label"],
end_node_label=rel_data["end_node_label"],
key_property=key_property,
properties=properties
)
relationships.append(relationship)

# Create and return the data model
return cls(nodes=nodes, relationships=relationships)

def to_arrows_dict(self) -> dict[str, Any]:
"Convert the data model to an Arrows Data Model Python dictionary."
node_spacing: int = 200
Expand Down
156 changes: 154 additions & 2 deletions servers/mcp-neo4j-data-modeling/src/mcp_neo4j_data_modeling/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@
Property,
Relationship,
)
from .static import DATA_INGEST_PROCESS
from .static import (
DATA_INGEST_PROCESS,
PATIENT_JOURNEY_MODEL,
SUPPLY_CHAIN_MODEL,
SOFTWARE_DEPENDENCY_MODEL,
OIL_GAS_MONITORING_MODEL,
CUSTOMER_360_MODEL,
FRAUD_AML_MODEL,
HEALTH_INSURANCE_FRAUD_MODEL,
)

logger = logging.getLogger("mcp_neo4j_data_modeling")

Expand Down Expand Up @@ -52,6 +61,62 @@ def neo4j_data_ingest_process() -> str:
logger.info("Getting the process for ingesting data into a Neo4j database.")
return DATA_INGEST_PROCESS

@mcp.resource("resource://examples/patient_journey_model")
def example_patient_journey_model() -> str:
"""Get a real-world Patient Journey healthcare data model in JSON format."""
logger.info("Getting the Patient Journey healthcare data model.")
import json

return json.dumps(PATIENT_JOURNEY_MODEL, indent=2)

@mcp.resource("resource://examples/supply_chain_model")
def example_supply_chain_model() -> str:
"""Get a real-world Supply Chain data model in JSON format."""
logger.info("Getting the Supply Chain data model.")
import json

return json.dumps(SUPPLY_CHAIN_MODEL, indent=2)

@mcp.resource("resource://examples/software_dependency_model")
def example_software_dependency_model() -> str:
"""Get a real-world Software Dependency Graph data model in JSON format."""
logger.info("Getting the Software Dependency Graph data model.")
import json

return json.dumps(SOFTWARE_DEPENDENCY_MODEL, indent=2)

@mcp.resource("resource://examples/oil_gas_monitoring_model")
def example_oil_gas_monitoring_model() -> str:
"""Get a real-world Oil and Gas Equipment Monitoring data model in JSON format."""
logger.info("Getting the Oil and Gas Equipment Monitoring data model.")
import json

return json.dumps(OIL_GAS_MONITORING_MODEL, indent=2)

@mcp.resource("resource://examples/customer_360_model")
def example_customer_360_model() -> str:
"""Get a real-world Customer 360 data model in JSON format."""
logger.info("Getting the Customer 360 data model.")
import json

return json.dumps(CUSTOMER_360_MODEL, indent=2)

@mcp.resource("resource://examples/fraud_aml_model")
def example_fraud_aml_model() -> str:
"""Get a real-world Fraud & AML data model in JSON format."""
logger.info("Getting the Fraud & AML data model.")
import json

return json.dumps(FRAUD_AML_MODEL, indent=2)

@mcp.resource("resource://examples/health_insurance_fraud_model")
def example_health_insurance_fraud_model() -> str:
"""Get a real-world Health Insurance Fraud Detection data model in JSON format."""
logger.info("Getting the Health Insurance Fraud Detection data model.")
import json

return json.dumps(HEALTH_INSURANCE_FRAUD_MODEL, indent=2)

@mcp.tool()
def validate_node(
node: Node, return_validated: bool = False
Expand Down Expand Up @@ -180,6 +245,92 @@ def get_constraints_cypher_queries(data_model: DataModel) -> list[str]:
)
return data_model.get_cypher_constraints_query()

@mcp.tool()
def load_example_data_model(
example_name: str = Field(
...,
description="Name of the example to load: 'patient_journey', 'supply_chain', 'software_dependency', 'oil_gas_monitoring', 'customer_360', 'fraud_aml', or 'health_insurance_fraud'",
),
) -> DataModel:
"""Load an example data model from the available templates. Returns a DataModel object that can be used with validation and export tools."""
logger.info(f"Loading example data model: {example_name}")

example_map = {
"patient_journey": PATIENT_JOURNEY_MODEL,
"supply_chain": SUPPLY_CHAIN_MODEL,
"software_dependency": SOFTWARE_DEPENDENCY_MODEL,
"oil_gas_monitoring": OIL_GAS_MONITORING_MODEL,
"customer_360": CUSTOMER_360_MODEL,
"fraud_aml": FRAUD_AML_MODEL,
"health_insurance_fraud": HEALTH_INSURANCE_FRAUD_MODEL,
}

if example_name not in example_map:
raise ValueError(
f"Unknown example: {example_name}. Available examples: {list(example_map.keys())}"
)

example_data = example_map[example_name]

# Use the new from_dict method to convert the JSON to a DataModel object
return DataModel.from_dict(example_data)

@mcp.tool()
def list_example_data_models() -> dict[str, Any]:
"""List all available example data models with descriptions. Returns a dictionary with example names and their descriptions."""
logger.info("Listing available example data models.")

examples = {
"patient_journey": {
"name": "Patient Journey",
"description": "Healthcare data model for tracking patient encounters, conditions, medications, and care plans",
"nodes": len(PATIENT_JOURNEY_MODEL["nodes"]),
"relationships": len(PATIENT_JOURNEY_MODEL["relationships"]),
},
"supply_chain": {
"name": "Supply Chain",
"description": "Supply chain management data model for tracking products, orders, inventory, and locations",
"nodes": len(SUPPLY_CHAIN_MODEL["nodes"]),
"relationships": len(SUPPLY_CHAIN_MODEL["relationships"]),
},
"software_dependency": {
"name": "Software Dependency Graph",
"description": "Software dependency tracking with security vulnerabilities, commits, and contributor analysis",
"nodes": len(SOFTWARE_DEPENDENCY_MODEL["nodes"]),
"relationships": len(SOFTWARE_DEPENDENCY_MODEL["relationships"]),
},
"oil_gas_monitoring": {
"name": "Oil & Gas Equipment Monitoring",
"description": "Industrial monitoring data model for oil and gas equipment, sensors, alerts, and maintenance",
"nodes": len(OIL_GAS_MONITORING_MODEL["nodes"]),
"relationships": len(OIL_GAS_MONITORING_MODEL["relationships"]),
},
"customer_360": {
"name": "Customer 360",
"description": "Customer relationship management data model for accounts, contacts, orders, tickets, and surveys",
"nodes": len(CUSTOMER_360_MODEL["nodes"]),
"relationships": len(CUSTOMER_360_MODEL["relationships"]),
},
"fraud_aml": {
"name": "Fraud & AML",
"description": "Financial fraud detection and anti-money laundering data model for customers, transactions, alerts, and compliance",
"nodes": len(FRAUD_AML_MODEL["nodes"]),
"relationships": len(FRAUD_AML_MODEL["relationships"]),
},
"health_insurance_fraud": {
"name": "Health Insurance Fraud Detection",
"description": "Healthcare fraud detection data model for tracking investigations, prescriptions, executions, and beneficiary relationships",
"nodes": len(HEALTH_INSURANCE_FRAUD_MODEL["nodes"]),
"relationships": len(HEALTH_INSURANCE_FRAUD_MODEL["relationships"]),
},
}

return {
"available_examples": examples,
"total_examples": len(examples),
"usage": "Use the load_example_data_model tool with any of the example names above to load a specific data model",
}

return mcp


Expand All @@ -203,4 +354,5 @@ async def main(


if __name__ == "__main__":
main()
import asyncio
asyncio.run(main())
Loading