Skip to content

Commit 22bb453

Browse files
authored
General - create common package for shared code (#167)
* create common package with middleware and config processing, implement in cypher mcp * move tests to common, add pr action, formatting, confirm tests in common * update changelogs, run tests * move format_namespace function to common from cypher mcp * Update README.md
1 parent a3e6aeb commit 22bb453

File tree

25 files changed

+1723
-762
lines changed

25 files changed

+1723
-762
lines changed

.github/workflows/pr-common.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Common Library Tests
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
paths:
7+
- 'servers/common/**'
8+
pull_request:
9+
branches: [ main, master ]
10+
paths:
11+
- 'servers/common/**'
12+
workflow_dispatch: # Allows manual triggering of the workflow
13+
14+
jobs:
15+
test:
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- uses: actions/checkout@v3
20+
21+
- name: Set up Python 3.12
22+
uses: actions/setup-python@v4
23+
with:
24+
python-version: '3.12'
25+
26+
- name: Install UV
27+
run: |
28+
curl -LsSf https://astral.sh/uv/install.sh | sh
29+
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
30+
31+
- name: Install dependencies
32+
run: |
33+
cd servers/common
34+
uv venv
35+
uv pip install -e ".[dev]"
36+
37+
- name: Check format and linting
38+
run: |
39+
cd servers/common
40+
make format-and-lint
41+
42+
- name: Run tests
43+
run: |
44+
cd servers/common
45+
make test-all

servers/common/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## v0.1.0
2+
3+
### Fixed
4+
5+
### Changed
6+
* Move middleware and config processing code from Cypher MCP into Common package
7+
* Move namespace function from Cypher MCP server into Common package
8+
9+
### Added

servers/common/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
format-and-lint:
2+
uv run ruff check --select I . --fix
3+
uv run ruff check --fix .
4+
uv run ruff format .
5+
6+
test-all:
7+
uv run pytest tests/ -v
8+
9+
10+
11+
all: test-all format-and-lint

servers/common/README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Common
2+
3+
Common utilities and middleware for MCP Neo4j servers.
4+
5+
## Installation
6+
7+
You should not have to manually install this package, however if you are receiving import errors you may try reinstalling with the following:
8+
9+
```bash
10+
uv sync --reinstall-package common
11+
```
12+
13+
## Features
14+
15+
This package provides three main categories of utilities:
16+
17+
1. **Middleware** - Pre-configured middleware for CORS and trusted host protection
18+
2. **Config Processing** - Utilities for processing command-line arguments and environment variables
19+
3. **Namespace Formatting** - Utilities for consistent namespace handling in tool naming
20+
21+
## Middleware
22+
23+
The middleware module provides factory functions for creating pre-configured Starlette middleware instances commonly needed by remotely deployed MCP Neo4j servers.
24+
25+
### CORS Middleware
26+
27+
```python
28+
from common.middleware import create_cors_middleware
29+
30+
# Create CORS middleware with allowed origins
31+
cors_middleware = create_cors_middleware(
32+
allow_origins=["https://example.com", "https://app.example.com"]
33+
)
34+
```
35+
36+
The CORS middleware is configured with:
37+
- **Methods**: `GET`, `POST`
38+
- **Headers**: All headers allowed (`*`)
39+
- **Origins**: Configurable list of allowed origins
40+
41+
### Trusted Host Middleware
42+
43+
```python
44+
from common.middleware import create_trusted_host_middleware
45+
46+
# Create trusted host middleware for DNS rebinding protection
47+
trusted_host_middleware = create_trusted_host_middleware(
48+
allowed_hosts=["localhost", "127.0.0.1", "myapp.com"]
49+
)
50+
```
51+
52+
## Config Processing
53+
54+
The `arg_processing` module provides utilities for processing configuration from both command-line arguments and environment variables, with sensible defaults and logging.
55+
56+
### Available Processing Functions
57+
58+
Each function follows the pattern: check command-line args first, then environment variables, then apply defaults with appropriate logging.
59+
60+
```python
61+
from common.utils.arg_processing import (
62+
process_db_url,
63+
process_username,
64+
process_password,
65+
)
66+
import argparse
67+
68+
# Example usage
69+
args = argparse.Namespace() # Your parsed arguments
70+
db_url = process_db_url(args)
71+
username = process_username(args)
72+
password = process_password(args)
73+
```
74+
75+
### Configuration Priority
76+
77+
1. **Command-line arguments**
78+
2. **Environment variables**
79+
3. **Default values**
80+
81+
### Environment Variables
82+
83+
| Function | Environment Variables | Default |
84+
|----------|----------------------|---------|
85+
| `process_db_url` | `NEO4J_URL`, `NEO4J_URI` | `bolt://localhost:7687` |
86+
| `process_username` | `NEO4J_USERNAME` | `neo4j` |
87+
| `process_password` | `NEO4J_PASSWORD` | `password` |
88+
| `process_database` | `NEO4J_DATABASE` | `neo4j` |
89+
| `process_namespace` | `NEO4J_NAMESPACE` | `""` (empty) |
90+
| `process_transport` | `NEO4J_TRANSPORT` | `stdio` |
91+
| `process_server_host` | `NEO4J_MCP_SERVER_HOST` | `127.0.0.1` (ignored if transport is `stdio`) |
92+
| `process_server_port` | `NEO4J_MCP_SERVER_PORT` | `8000` (ignored if transport is `stdio`) |
93+
| `process_server_path` | `NEO4J_MCP_SERVER_PATH` | `/mcp/` (ignored if transport is `stdio`) |
94+
| `process_allow_origins` | `NEO4J_MCP_SERVER_ALLOW_ORIGINS` | `[]` (empty list) |
95+
| `process_allowed_hosts` | `NEO4J_MCP_SERVER_ALLOWED_HOSTS` | `["localhost", "127.0.0.1"]` |
96+
| `process_token_limit` | `NEO4J_RESPONSE_TOKEN_LIMIT` | `None` |
97+
| `process_read_timeout` | `NEO4J_READ_TIMEOUT` | `30` |
98+
99+
## Namespace Formatting
100+
101+
The `namespace` module provides utilities for consistent namespace formatting in tool naming conventions.
102+
103+
### format_namespace Function
104+
105+
```python
106+
from common.utils.namespace import format_namespace
107+
108+
# Ensures namespace ends with hyphen for tool naming
109+
formatted = format_namespace("myapp") # Returns: "myapp-"
110+
formatted = format_namespace("myapp-") # Returns: "myapp-" (no change)
111+
```
112+
113+
This ensures consistent tool naming across MCP Neo4j servers, where namespaced tools follow the pattern `namespace-toolname` and non-namespaced tools use just the tool name.

servers/common/pyproject.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[project]
2+
name = "common"
3+
version = "0.1.0"
4+
description = "Common utilities and middleware for MCP Neo4j servers"
5+
readme = "README.md"
6+
requires-python = ">=3.10"
7+
dependencies = [
8+
"starlette>=0.40.0",
9+
]
10+
11+
[build-system]
12+
requires = ["hatchling"]
13+
build-backend = "hatchling.build"
14+
15+
[tool.uv]
16+
dev-dependencies = [
17+
"pytest>=7.0.0",
18+
"pytest-asyncio>=0.20.3",
19+
"ruff>=0.13.0",
20+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Common utilities and middleware for MCP Neo4j servers.
3+
"""
4+
5+
__version__ = "0.1.0"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
Middleware components for MCP Neo4j servers.
3+
"""
4+
5+
from .cors_middleware import create_cors_middleware
6+
from .trusted_host_middleware import create_trusted_host_middleware
7+
8+
__all__ = ["create_cors_middleware", "create_trusted_host_middleware"]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
CORS middleware configuration for MCP Neo4j servers.
3+
"""
4+
5+
from starlette.middleware import Middleware
6+
from starlette.middleware.cors import CORSMiddleware
7+
8+
9+
def create_cors_middleware(allow_origins: list[str]) -> Middleware:
10+
"""
11+
Create CORS middleware with specified allowed origins.
12+
13+
Parameters
14+
----------
15+
allow_origins : list[str]
16+
List of allowed origins for CORS requests
17+
18+
Returns
19+
-------
20+
Middleware
21+
Configured CORS middleware instance
22+
"""
23+
return Middleware(
24+
CORSMiddleware,
25+
allow_origins=allow_origins,
26+
allow_methods=["GET", "POST"],
27+
allow_headers=["*"],
28+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
Trusted Host middleware configuration for MCP Neo4j servers.
3+
"""
4+
5+
from starlette.middleware import Middleware
6+
from starlette.middleware.trustedhost import TrustedHostMiddleware
7+
8+
9+
def create_trusted_host_middleware(allowed_hosts: list[str]) -> Middleware:
10+
"""
11+
Create TrustedHost middleware with specified allowed hosts.
12+
13+
Parameters
14+
----------
15+
allowed_hosts : list[str]
16+
List of allowed hosts for DNS rebinding protection
17+
18+
Returns
19+
-------
20+
Middleware
21+
Configured TrustedHost middleware instance
22+
"""
23+
return Middleware(TrustedHostMiddleware, allowed_hosts=allowed_hosts)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
Utility functions for MCP Neo4j servers.
3+
"""
4+
5+
from .arg_processing import (
6+
process_allow_origins,
7+
process_allowed_hosts,
8+
process_database,
9+
process_db_url,
10+
process_namespace,
11+
process_password,
12+
process_read_timeout,
13+
process_server_host,
14+
process_server_path,
15+
process_server_port,
16+
process_token_limit,
17+
process_transport,
18+
process_username,
19+
)
20+
from .namespace import format_namespace
21+
22+
__all__ = [
23+
"process_db_url",
24+
"process_username",
25+
"process_password",
26+
"process_database",
27+
"process_namespace",
28+
"process_transport",
29+
"process_server_host",
30+
"process_server_port",
31+
"process_server_path",
32+
"process_allow_origins",
33+
"process_allowed_hosts",
34+
"process_token_limit",
35+
"process_read_timeout",
36+
"format_namespace",
37+
]

0 commit comments

Comments
 (0)