Skip to content

Commit d896acc

Browse files
committed
clean up and testing with claude
1 parent a233f9c commit d896acc

File tree

3 files changed

+46
-32
lines changed

3 files changed

+46
-32
lines changed

servers/mcp-neo4j-data-modeling/Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Makefile for cypher-guard Python bindings
22

3-
.PHONY: format test clean
3+
.PHONY: format test clean inspector build_local_docker_image
44

55
format:
66
uv run ruff check --select I . --fix
@@ -12,7 +12,9 @@ test:
1212

1313
inspector:
1414
npx @modelcontextprotocol/inspector uv --directory src/mcp_neo4j_data_modeling run mcp-neo4j-data-modeling
15-
15+
16+
build_local_docker_image:
17+
docker build -t mcp-neo4j-data-modeling .
1618

1719
clean:
1820
rm -rf .mypy_cache/

servers/mcp-neo4j-data-modeling/src/mcp_neo4j_data_modeling/data_model.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ class Property(BaseModel):
3333
"A Neo4j Property."
3434

3535
name: str = Field(description="The name of the property. Should be in camelCase.")
36-
type: str = Field(default="STRING", description="The Neo4j type of the property. Should be all caps.")
36+
type: str = Field(
37+
default="STRING",
38+
description="The Neo4j type of the property. Should be all caps.",
39+
)
3740
source: PropertySource | None = Field(
3841
default=None, description="The source of the property, if known."
3942
)
@@ -84,7 +87,9 @@ def to_arrows(self, is_key: bool = False) -> dict[str, Any]:
8487
class Node(BaseModel):
8588
"A Neo4j Node."
8689

87-
label: str = Field(description="The label of the node. Should be in PascalCase.", min_length=1)
90+
label: str = Field(
91+
description="The label of the node. Should be in PascalCase.", min_length=1
92+
)
8893
key_property: Property = Field(description="The key property of the node")
8994
properties: list[Property] = Field(
9095
default_factory=list, description="The properties of the node"
@@ -189,7 +194,8 @@ class Relationship(BaseModel):
189194
"A Neo4j Relationship."
190195

191196
type: str = Field(
192-
description="The type of the relationship. Should be in SCREAMING_SNAKE_CASE.", min_length=1
197+
description="The type of the relationship. Should be in SCREAMING_SNAKE_CASE.",
198+
min_length=1,
193199
)
194200
start_node_label: str = Field(description="The label of the start node")
195201
end_node_label: str = Field(description="The label of the end node")

servers/mcp-neo4j-data-modeling/src/mcp_neo4j_data_modeling/server.py

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44
from typing import Any, Literal
55

66
from mcp.server.fastmcp import FastMCP
7-
from mcp import types as mcp_types
87
from pydantic import ValidationError
98

109
from .data_model import (
1110
DataModel,
1211
Node,
1312
Property,
1413
Relationship,
15-
_generate_relationship_pattern,
1614
)
1715

1816
logger = logging.getLogger("mcp_neo4j_data_modeling")
@@ -25,12 +23,6 @@ def create_mcp_server() -> FastMCP:
2523
"mcp-neo4j-data-modeling", dependencies=["pydantic", "webbrowser"]
2624
)
2725

28-
@mcp.resource("resource://init")
29-
def init() -> DataModel:
30-
"""Create an empty data model."""
31-
logger.info("Creating an empty data model.")
32-
return DataModel(nodes=[], relationships=[])
33-
3426
@mcp.resource("resource://schema/node")
3527
def node_schema() -> dict[str, Any]:
3628
"""Get the schema for a node."""
@@ -56,47 +48,61 @@ def data_model_schema() -> dict[str, Any]:
5648
return DataModel.model_json_schema()
5749

5850
@mcp.tool()
59-
def validate_node(node: Node) -> list[mcp_types.TextContent]:
60-
"Validate a single node. Returns True if the node is valid, otherwise raises a ValueError."
51+
def validate_node(
52+
node: Node, return_validated: bool = False
53+
) -> bool | dict[str, Any]:
54+
"Validate a single node. Returns True if the node is valid, otherwise raises a ValueError. If return_validated is True, returns the validated node."
6155
logger.info("Validating a single node.")
6256
try:
63-
Node.model_validate(node, strict=True)
64-
logger.info(f"Node validated successfully")
65-
return True
57+
validated_node = Node.model_validate(node, strict=True)
58+
logger.info("Node validated successfully")
59+
if return_validated:
60+
return validated_node
61+
else:
62+
return True
6663
except ValidationError as e:
6764
logger.error(f"Validation error: {e}")
6865
raise ValueError(f"Validation error: {e}")
69-
7066

7167
@mcp.tool()
72-
def validate_relationship(relationship: Relationship) -> bool:
73-
"Validate a single relationship. Returns True if the relationship is valid, otherwise raises a ValueError."
68+
def validate_relationship(
69+
relationship: Relationship, return_validated: bool = False
70+
) -> bool | dict[str, Any]:
71+
"Validate a single relationship. Returns True if the relationship is valid, otherwise raises a ValueError. If return_validated is True, returns the validated relationship."
7472
logger.info("Validating a single relationship.")
7573
try:
76-
Relationship.model_validate(relationship, strict=True)
77-
logger.info(f"Relationship validated successfully")
78-
return True
74+
validated_relationship = Relationship.model_validate(
75+
relationship, strict=True
76+
)
77+
logger.info("Relationship validated successfully")
78+
if return_validated:
79+
return validated_relationship
80+
else:
81+
return True
7982
except ValidationError as e:
8083
logger.error(f"Validation error: {e}")
8184
raise ValueError(f"Validation error: {e}")
8285

83-
8486
@mcp.tool()
85-
def validate_data_model(data_model: DataModel) -> bool:
86-
"Validate the entire data model. Returns True if the data model is valid, otherwise raises a ValueError."
87+
def validate_data_model(
88+
data_model: DataModel, return_validated: bool = False
89+
) -> bool | dict[str, Any]:
90+
"Validate the entire data model. Returns True if the data model is valid, otherwise raises a ValueError. If return_validated is True, returns the validated data model."
8791
logger.info("Validating the entire data model.")
8892
try:
8993
DataModel.model_validate(data_model, strict=True)
90-
logger.info(f"Data model validated successfully")
91-
return True
94+
logger.info("Data model validated successfully")
95+
if return_validated:
96+
return data_model
97+
else:
98+
return True
9299
except ValidationError as e:
93100
logger.error(f"Validation error: {e}")
94101
raise ValueError(f"Validation error: {e}")
95-
96102

97103
@mcp.tool()
98104
def visualize_data_model(data_model: DataModel) -> None:
99-
"Open an interactive graph visualization in the default web browser. Warning: May not be useable in Docker environments."
105+
"Open an interactive graph visualization in the default web browser. Validates the data model before opening the visualization. Warning: May not be useable in Docker environments."
100106
logger.info("Validating the data model.")
101107
try:
102108
dm_validated = DataModel.model_validate(data_model, strict=True)
@@ -131,7 +137,7 @@ def load_from_arrows_json(arrows_data_model_dict: dict[str, Any]) -> DataModel:
131137

132138
@mcp.tool()
133139
def export_to_arrows_json(data_model: DataModel) -> str:
134-
"Export the data model to the Arrows web application format. Returns a JSON string."
140+
"Export the data model to the Arrows web application format. Returns a JSON string. This should be presented to the user as an artifact if possible."
135141
logger.info("Exporting the data model to the Arrows web application format.")
136142
return data_model.to_arrows_json_str()
137143

0 commit comments

Comments
 (0)