Skip to content

Commit 20b249e

Browse files
committed
reorg cypher tests, update changelog, update cli and env args
1 parent 1dbe125 commit 20b249e

File tree

10 files changed

+386
-456
lines changed

10 files changed

+386
-456
lines changed

servers/mcp-neo4j-cypher/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Add .dxt file for Cypher MCP server
1010
* Add .dxt file generation to Cypher MCP Publish GitHub action
1111
* Add error indicator to tool results in the `CallToolResult` object
12+
* Add HTTP transport option
1213

1314
## v0.2.4
1415

servers/mcp-neo4j-cypher/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ Environment variables for HTTP configuration:
8282

8383
```bash
8484
export NEO4J_TRANSPORT=http
85-
export NEO4J_HTTP_HOST=0.0.0.0
86-
export NEO4J_HTTP_PORT=8080
87-
export NEO4J_HTTP_PATH=/api/mcp/
85+
export NEO4J_MCP_SERVER_HOST=0.0.0.0
86+
export NEO4J_MCP_SERVER_PORT=8080
87+
export NEO4J_MCP_SERVER_PATH=/api/mcp/
8888
mcp-neo4j-cypher
8989
```
9090

servers/mcp-neo4j-cypher/src/mcp_neo4j_cypher/__init__.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ def main():
1414
parser.add_argument("--database", default=None, help="Neo4j database name")
1515
parser.add_argument("--transport", default=None, help="Transport type (stdio, sse, http)")
1616
parser.add_argument("--namespace", default=None, help="Tool namespace")
17-
parser.add_argument("--host", default=None, help="HTTP host (default: 127.0.0.1)")
18-
parser.add_argument("--port", type=int, default=None, help="HTTP port (default: 8000)")
19-
parser.add_argument("--path", default=None, help="HTTP path (default: /mcp/)")
17+
parser.add_argument("--server-path", default=None, help="HTTP path (default: /mcp/)")
2018
parser.add_argument("--server-host", default=None, help="Server host")
2119
parser.add_argument("--server-port", default=None, help="Server port")
2220

@@ -29,9 +27,9 @@ def main():
2927
args.database or os.getenv("NEO4J_DATABASE", "neo4j"),
3028
args.transport or os.getenv("NEO4J_TRANSPORT", "stdio"),
3129
args.namespace or os.getenv("NEO4J_NAMESPACE", ""),
32-
args.host or os.getenv("NEO4J_HTTP_HOST", "127.0.0.1"),
33-
args.port or int(os.getenv("NEO4J_HTTP_PORT", "8000")),
34-
args.path or os.getenv("NEO4J_HTTP_PATH", "/mcp/"),
30+
args.server_host or os.getenv("NEO4J_MCP_SERVER_HOST", "127.0.0.1"),
31+
args.server_port or int(os.getenv("NEO4J_MCP_SERVER_PORT", "8000")),
32+
args.server_path or os.getenv("NEO4J_MCP_SERVER_PATH", "/mcp/"),
3533
)
3634
)
3735

servers/mcp-neo4j-cypher/src/mcp_neo4j_cypher/server.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -166,18 +166,19 @@ async def main(
166166
mcp = create_mcp_server(neo4j_driver, database, namespace)
167167

168168
# Run the server with the specified transport
169-
if transport == "http":
170-
logger.info(f"Running Neo4j Cypher MCP Server with HTTP transport on {host}:{port}...")
171-
await mcp.run_http_async(host=host, port=port, path=path)
172-
elif transport == "stdio":
173-
logger.info("Running Neo4j Cypher MCP Server with stdio transport...")
174-
await mcp.run_stdio_async()
175-
elif transport == "sse":
176-
logger.info(f"Running Neo4j Cypher MCP Server with SSE transport on {host}:{port}...")
177-
await mcp.run_sse_async(host=host, port=port, path=path)
178-
else:
179-
logger.error(f"Invalid transport: {transport} | Must be either 'stdio', 'sse', or 'http'")
180-
raise ValueError(f"Invalid transport: {transport} | Must be either 'stdio', 'sse', or 'http'")
169+
match transport:
170+
case "http":
171+
logger.info(f"Running Neo4j Cypher MCP Server with HTTP transport on {host}:{port}...")
172+
await mcp.run_http_async(host=host, port=port, path=path)
173+
case "stdio":
174+
logger.info("Running Neo4j Cypher MCP Server with stdio transport...")
175+
await mcp.run_stdio_async()
176+
case "sse":
177+
logger.info(f"Running Neo4j Cypher MCP Server with SSE transport on {host}:{port}...")
178+
await mcp.run_sse_async(host=host, port=port, path=path)
179+
case _:
180+
logger.error(f"Invalid transport: {transport} | Must be either 'stdio', 'sse', or 'http'")
181+
raise ValueError(f"Invalid transport: {transport} | Must be either 'stdio', 'sse', or 'http'")
181182

182183

183184
if __name__ == "__main__":

servers/mcp-neo4j-cypher/tests/integration/conftest.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import os
2+
import asyncio
3+
import subprocess
24
from typing import Any
35

46
import pytest
57
import pytest_asyncio
68
from neo4j import AsyncGraphDatabase
79
from testcontainers.neo4j import Neo4jContainer
810

11+
912
from mcp_neo4j_cypher.server import create_mcp_server
1013

1114
neo4j = (
@@ -69,3 +72,75 @@ def init_data(setup: Neo4jContainer, clear_data: Any):
6972
def clear_data(setup: Neo4jContainer):
7073
with setup.get_driver().session(database="neo4j") as session:
7174
session.run("MATCH (n) DETACH DELETE n")
75+
76+
77+
78+
@pytest_asyncio.fixture
79+
async def sse_server(setup: Neo4jContainer):
80+
"""Start the MCP server in SSE mode."""
81+
82+
83+
process = await asyncio.create_subprocess_exec(
84+
"uv", "run", "mcp-neo4j-cypher",
85+
"--transport", "sse",
86+
"--server-host", "127.0.0.1",
87+
"--server-port", "8002",
88+
"--db-url", setup.get_connection_url(),
89+
"--username", setup.username,
90+
"--password", setup.password,
91+
"--database", "neo4j",
92+
stdout=subprocess.PIPE,
93+
stderr=subprocess.PIPE,
94+
cwd=os.getcwd()
95+
)
96+
97+
await asyncio.sleep(3)
98+
99+
if process.returncode is not None:
100+
stdout, stderr = await process.communicate()
101+
raise RuntimeError(f"Server failed to start. stdout: {stdout.decode()}, stderr: {stderr.decode()}")
102+
103+
yield process
104+
105+
try:
106+
process.terminate()
107+
await asyncio.wait_for(process.wait(), timeout=5.0)
108+
except asyncio.TimeoutError:
109+
process.kill()
110+
await process.wait()
111+
112+
@pytest_asyncio.fixture
113+
async def http_server(setup: Neo4jContainer):
114+
"""Start the MCP server in HTTP mode."""
115+
116+
# Start server process in HTTP mode using the installed binary
117+
process = await asyncio.create_subprocess_exec(
118+
"uv", "run", "mcp-neo4j-cypher",
119+
"--transport", "http",
120+
"--server-host", "127.0.0.1",
121+
"--server-port", "8001",
122+
"--db-url", setup.get_connection_url(),
123+
"--username", setup.username,
124+
"--password", setup.password,
125+
stdout=subprocess.PIPE,
126+
stderr=subprocess.PIPE,
127+
cwd=os.getcwd()
128+
)
129+
130+
# Wait for server to start
131+
await asyncio.sleep(3)
132+
133+
# Check if process is still running
134+
if process.returncode is not None:
135+
stdout, stderr = await process.communicate()
136+
raise RuntimeError(f"Server failed to start. stdout: {stdout.decode()}, stderr: {stderr.decode()}")
137+
138+
yield process
139+
140+
# Cleanup
141+
try:
142+
process.terminate()
143+
await asyncio.wait_for(process.wait(), timeout=5.0)
144+
except asyncio.TimeoutError:
145+
process.kill()
146+
await process.wait()

0 commit comments

Comments
 (0)