Skip to content

Commit 0ed179d

Browse files
authored
Merge pull request #5 from neo4j-contrib/development
Deployment
2 parents 0e41fbc + de49e89 commit 0ed179d

File tree

12 files changed

+1298
-251
lines changed

12 files changed

+1298
-251
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
venv
1+
.venv
22
*/venv
33
*/.venv
44
**/.env

Dockerfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
FROM python:3.12-slim
1+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
22

33
ADD . /app
44
WORKDIR /app
55

6-
RUN pip install -r requirements.txt
6+
# Install dependencies using UV
7+
RUN uv sync --frozen --no-dev
78

8-
CMD ["python", "src/sandbox_api_mcp_server/server.py"]
9+
CMD ["uv", "run", "sandbox-api-mcp-server"]

README.md

Lines changed: 112 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This project provides a Model Context Protocol (MCP) server for interacting with the [Neo4j Sandbox API](https://sandbox.neo4j.com/). It allows language models or other MCP clients to easily launch, list, query, and perform other actions on Neo4j Sandbox instances using a standardized tool interface.
66

7-
The server is built as a [FastAPI](https://fastapi.tiangolo.com/) application and uses the [FastAPI-MCP](https://fastapi-mcp.tadata.com/getting-started/welcome) library to expose its endpoints as MCP tools. Authentication with the Sandbox API is handled via Auth0, and the necessary Auth0 credentials must be configured through environment variables.
7+
The server is built as a [FastAPI](https://fastapi.tiangolo.com/) application and uses [FastMCP](https://gofastmcp.com/) (v2.12.4) to expose its endpoints as MCP tools. FastMCP provides enterprise-grade OAuth 2.1 authentication with Auth0, along with backward compatibility for API key authentication. The necessary Auth0 credentials must be configured through environment variables.
88

99
## Environment Variables
1010

@@ -15,38 +15,44 @@ The server requires the following environment variables to be set for Auth0 auth
1515
* `AUTH0_CLIENT_ID`: The Client ID of your Auth0 Application.
1616
* `AUTH0_CLIENT_SECRET`: The Client Secret of your Auth0 Application.
1717
* `SANDBOX_API_KEY`: Your Neo4j Sandbox API key. This is used by the underlying `neo4j-sandbox-api-client`.
18+
* `PORT` (optional): The port to run the server on. Defaults to `9100` if not set.
1819

1920
You can set these variables directly in your environment or place them in a `.env` file in the project root.
2021

2122
## Running the Server
2223

23-
1. **Install dependencies:**
24-
It's recommended to use a virtual environment.
24+
1. **Install UV (if not already installed):**
2525
```bash
26-
python -m venv .venv
27-
source .venv/bin/activate # On Windows use `.venv\\Scripts\\activate`
28-
pip install -r requirements.txt
29-
# Or using uv
30-
# uv pip install -r requirements.txt
31-
```
26+
# macOS/Linux
27+
curl -LsSf https://astral.sh/uv/install.sh | sh
3228

33-
2. **Set environment variables:**
34-
Ensure the `.env` file is present in the `mcp-server` directory and populated with your Auth0 and Sandbox API credentials as described above.
29+
# Windows
30+
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
31+
```
3532

36-
3. **Run the FastAPI application:**
37-
The server can be started using Uvicorn:
33+
2. **Install dependencies:**
34+
UV will automatically create a virtual environment and install dependencies:
3835
```bash
39-
uvicorn src.sandbox_api_mcp_server.server:run --factory --host 0.0.0.0 --port 9100
36+
uv sync
4037
```
41-
Alternatively, if you have `src` in your `PYTHONPATH` or are in the `mcp-server` directory:
38+
39+
3. **Set environment variables:**
40+
Ensure the `.env` file is present in the project root and populated with your Auth0 and Sandbox API credentials as described above.
41+
42+
4. **Run the FastAPI application:**
43+
The server can be started using UV:
4244
```bash
43-
python src/sandbox_api_mcp_server/server.py
45+
uv run sandbox-api-mcp-server
4446
```
45-
This will typically start the server on `http://0.0.0.0:9100`. The MCP endpoint will be available at `http://0.0.0.0:9100/sse` (as configured in `server.py`).
47+
This will start the server on `http://0.0.0.0:9100`. The MCP server is available at two transport endpoints:
48+
- `http://0.0.0.0:9100/sse` - SSE transport (legacy, backward compatible)
49+
- `http://0.0.0.0:9100/mcp` - Streamable HTTP transport (modern, recommended for production)
4650

4751
## Using with MCP Clients (e.g., Claude Desktop)
4852

49-
To use this MCP server with an MCP client, you need to configure the client to connect to the running FastAPI server. Given the OAuth2 flow used for authentication, **it is highly recommended to use `mcp-remote`** to bridge the connection. `mcp-remote` will handle the browser-based login and token passing to the MCP server.
53+
To use this MCP server with an MCP client, you need to configure the client to connect to the running FastAPI server. This server uses **FastMCP 2.12.4** to expose FastAPI endpoints as MCP tools. Authentication is handled via Auth0 (OAuth2/JWT) and API keys at the FastAPI layer.
54+
55+
**It is recommended to use `mcp-remote`** to bridge the connection, especially if you need to handle OAuth token acquisition. `mcp-remote` can help manage authentication tokens for the HTTP requests to the server.
5056

5157
### Step 1: Install `mcp-remote` (if not already installed)
5258

@@ -57,27 +63,37 @@ npm install -g mcp-remote
5763
5864
### Step 2: Run your FastAPI MCP Server
5965
60-
Ensure your FastAPI MCP server is running locally (e.g., on `http://localhost:9100` with the MCP endpoint at `http://localhost:9100/sse`):
61-
```bash
62-
python src/sandbox_api_mcp_server/server.py
63-
```
64-
Or using uvicorn directly:
66+
Ensure your FastAPI MCP server is running locally (e.g., on `http://localhost:9100`):
6567
```bash
66-
uvicorn src.sandbox_api_mcp_server.server:run --factory --host 0.0.0.0 --port 9100
68+
uv run sandbox-api-mcp-server
6769
```
6870
71+
The server provides two MCP transport endpoints:
72+
- `http://localhost:9100/sse` - SSE transport (backward compatible)
73+
- `http://localhost:9100/mcp` - Streamable HTTP transport (recommended)
74+
6975
7076
### Step 3: Run `mcp-remote`
7177
72-
In a new terminal, start `mcp-remote`, pointing it to your local MCP server's `/sse` endpoint and choosing a local port for `mcp-remote` to listen on (e.g., `8080`):
78+
In a new terminal, start `mcp-remote`, pointing it to your local MCP server. You can choose either transport endpoint:
7379
80+
**Using SSE (legacy, backward compatible):**
7481
```bash
7582
# If mcp-cli is installed globally
7683
mcp-remote http://localhost:9100/sse 8080
7784
7885
# Or using npx
7986
npx -y mcp-remote http://localhost:9100/sse 8080
8087
```
88+
89+
**Using Streamable HTTP (modern, recommended):**
90+
```bash
91+
# If mcp-cli is installed globally
92+
mcp-remote http://localhost:9100/mcp 8080
93+
94+
# Or using npx
95+
npx -y mcp-remote http://localhost:9100/mcp 8080
96+
```
8197
`mcp-remote` will now listen on `localhost:8080` and proxy requests to your actual MCP server, handling the OAuth flow.
8298
8399
### Step 4: Configure Claude Desktop
@@ -90,6 +106,7 @@ npx -y mcp-remote http://localhost:9100/sse 8080
90106
2. **Configure the MCP Server in Claude Desktop:**
91107
Edit `claude_desktop_config.json` to point to the local port where `mcp-remote` is listening (e.g., `8080`).
92108

109+
**Option A: Using SSE transport (backward compatible):**
93110
```json
94111
{
95112
"mcpServers": {
@@ -105,6 +122,23 @@ npx -y mcp-remote http://localhost:9100/sse 8080
105122
}
106123
}
107124
```
125+
126+
**Option B: Using Streamable HTTP transport (recommended):**
127+
```json
128+
{
129+
"mcpServers": {
130+
"neo4j-sandbox-mcp-via-remote": {
131+
"command": "npx",
132+
"args": [
133+
"-y",
134+
"mcp-remote",
135+
"http://localhost:9100/mcp",
136+
"8080"
137+
]
138+
}
139+
}
140+
}
141+
```
108142
**Note:** With `mcp-remote` handling the connection to your actual server and its authentication, the Claude Desktop configuration becomes simpler, primarily needing to know where `mcp-remote` is accessible.
109143

110144
3. **Restart Claude Desktop:**
@@ -244,9 +278,60 @@ The following tools are exposed, derived from the FastAPI application's endpoint
244278

245279
## Development
246280

281+
### Project Structure
282+
247283
* The main FastAPI application logic is in `src/sandbox_api_mcp_server/server.py`.
248284
* API routes (which become MCP tools) are defined in `src/sandbox_api_mcp_server/sandbox/routes.py`.
249285
* Request/response models are primarily in `src/sandbox_api_mcp_server/sandbox/models.py` and `src/sandbox_api_mcp_server/models.py`.
250286
* Authentication logic is in `src/sandbox_api_mcp_server/auth.py`.
251-
* The project uses `uv` for dependency management (see `uv.lock`) and `pip` for installation (`requirements.txt`).
252-
* Consider using `hatch` or `poetry` for more robust dependency management and packaging if distributing this server. (The `pyproject.toml` suggests `hatch` might be intended for future use).
287+
* Auth0 OAuth provider for FastMCP is in `src/sandbox_api_mcp_server/auth_provider.py`.
288+
289+
### Dependency Management
290+
291+
This project uses [UV](https://docs.astral.sh/uv/) for fast, reliable dependency management:
292+
293+
* **Adding dependencies**: `uv add <package-name>`
294+
* **Removing dependencies**: `uv remove <package-name>`
295+
* **Updating dependencies**: `uv lock --upgrade`
296+
* **Running scripts**: `uv run <command>`
297+
298+
All dependencies are defined in `pyproject.toml` and locked in `uv.lock` for reproducible builds.
299+
300+
### FastMCP Configuration
301+
302+
The server includes a `fastmcp.json` configuration file for declarative deployment. You can run the server using:
303+
304+
```bash
305+
fastmcp run fastmcp.json
306+
```
307+
308+
This configuration defines the source, environment, and deployment settings for the FastMCP server.
309+
310+
### Authentication Architecture
311+
312+
The server implements authentication at the **FastAPI layer** via the `verify_auth` dependency, which supports:
313+
314+
1. **OAuth2/JWT Tokens via Auth0**
315+
- Validates JWT tokens issued by Auth0
316+
- Verifies token signature using JWKS public keys
317+
- Checks audience, issuer, and other JWT claims
318+
- MCP clients can use these tokens via standard `Authorization: Bearer <token>` headers
319+
320+
2. **API Key Authentication** (backward compatibility)
321+
- Supports `Authorization: Bearer ApiKey <key>` header format
322+
- Maintained in FastAPI routes via `Depends(verify_auth)`
323+
- Ensures existing API consumers continue to work
324+
325+
**Note on MCP OAuth Support:**
326+
Future versions may implement native MCP OAuth support via `OAuthAuthorizationServerProvider`, which would provide Dynamic Client Registration (DCR) compliance and seamless OAuth flows specifically designed for the MCP protocol. The current implementation leverages FastMCP's ability to convert FastAPI endpoints while maintaining the existing FastAPI authentication system.
327+
328+
### FastMCP Features
329+
330+
This server leverages [FastMCP 2.12.4](https://www.jlowin.dev/blog/fastmcp-2-12) features:
331+
332+
- **FastAPI Integration**: Seamless conversion of FastAPI endpoints to MCP tools via `FastMCP.from_fastapi()`
333+
- **Route Filtering**: Excludes internal endpoints (like `/health`) from MCP tool exposure using `RouteMap` configurations
334+
- **Authentication Compatibility**: Works with existing FastAPI authentication middleware (Auth0 JWT + API keys)
335+
- **Dual Transport Support**: Provides both transport protocols for maximum compatibility
336+
- **SSE** at `/sse` - Legacy transport for backward compatibility with existing clients
337+
- **Streamable HTTP** at `/mcp` - Modern transport recommended for production deployments

fastmcp.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://gofastmcp.com/public/schemas/fastmcp.json/v1.json",
3+
"source": {
4+
"path": "src/sandbox_api_mcp_server/server.py",
5+
"entrypoint": "mcp"
6+
},
7+
"environment": {
8+
"python": ">=3.10",
9+
"dependencies": [
10+
"cryptography~=45.0.0",
11+
"fastapi>=0.115.0",
12+
"fastmcp>=2.12.4",
13+
"httpx>=0.28.1",
14+
"PyJWT>=2.10.1",
15+
"python-dotenv>=1.1.0",
16+
"pydantic>=2.11.4",
17+
"uvicorn>=0.30.0"
18+
]
19+
},
20+
"deployment": {
21+
"transport": "http",
22+
"port": 9100
23+
}
24+
}
25+

pyproject.toml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ packages = ["src/sandbox_api_mcp_server"]
55

66
[project]
77
name = "sandbox-api-mcp-server"
8-
version = "0.1.0"
8+
version = "1.1.0"
99
description = "Sandbox API tool for FastMCP"
1010
readme = "README.md"
1111
requires-python = ">=3.10"
@@ -14,10 +14,14 @@ authors = [
1414
{ name = "Rafal Janicki", email = "[email protected]" }
1515
]
1616
dependencies = [
17-
"fastmcp>=2.5.1",
18-
"python-dotenv>=1.1.0",
19-
"httpx>=0.28.1",
20-
"pydantic>=2.11.4",
17+
"cryptography~=45.0.0",
18+
"fastapi>=0.115.0,<1.0.0",
19+
"fastmcp>=2.12.4",
20+
"httpx>=0.28.1,<1.0.0",
21+
"PyJWT>=2.10.1,<3.0.0",
22+
"python-dotenv>=1.1.0,<2.0.0",
23+
"pydantic>=2.11.4,<3.0.0",
24+
"uvicorn>=0.30.0,<1.0.0",
2125
]
2226
classifiers = [
2327
"Programming Language :: Python :: 3",

requirements.txt

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/sandbox_api_mcp_server/auth.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
import json
33
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
44
from cryptography.hazmat.primitives import serialization
5-
from helpers import get_logger
5+
from .helpers import get_logger
66
from jwt.algorithms import RSAAlgorithm
77
from fastapi import Request, HTTPException, status
88
from typing import Any
9-
from models import Auth0Settings
9+
from .models import Auth0Settings
1010

1111
logger = get_logger(__name__)
1212

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Auth0 authentication configuration for FastMCP.
3+
4+
This module documents the Auth0 authentication setup. The actual authentication
5+
is handled at the FastAPI layer via the verify_auth dependency, which supports:
6+
- OAuth2/JWT tokens from Auth0
7+
- API Key authentication for backward compatibility
8+
9+
Future Enhancement:
10+
Implement OAuthAuthorizationServerProvider for native FastMCP OAuth support.
11+
This would provide DCR-compliant OAuth authentication directly in the MCP protocol.
12+
13+
The provider would need to implement:
14+
- get_client() - Retrieve client information
15+
- register_client() - Handle dynamic client registration
16+
- authorize() - Handle OAuth authorization flow
17+
- exchange_code() - Exchange authorization code for tokens
18+
- refresh_token() - Refresh access tokens
19+
20+
Reference: https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/server/auth/provider.py
21+
"""
22+
23+
from ..models import Auth0Settings
24+
25+
26+
def get_auth0_settings():
27+
"""
28+
Get Auth0 settings for reference.
29+
30+
Authentication is currently handled by FastAPI's verify_auth dependency.
31+
This function is provided for future OAuth provider implementation.
32+
33+
Returns
34+
-------
35+
Auth0Settings
36+
Configured Auth0 settings
37+
"""
38+
return Auth0Settings()
39+

src/sandbox_api_mcp_server/sandbox/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from fastapi.responses import PlainTextResponse
66

77
from .models import StartSandboxBody, StopSandboxBody, ExtendSandboxBody, AuraUploadBody, BackupDownloadUrlBody, FastApiReadCypherQueryBody, FastApiWriteCypherQueryBody
8-
from helpers import get_logger
8+
from ..helpers import get_logger
99
from .service import call_sandbox_api, SandboxApiClient, get_sandbox_client
1010

1111
logger = get_logger(__name__)

src/sandbox_api_mcp_server/sandbox/service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from typing import Annotated, Optional, Dict, Any
66
from fastapi import HTTPException, status, Depends
77

8-
from auth import verify_auth
9-
from helpers import get_logger
8+
from ..auth import verify_auth
9+
from ..helpers import get_logger
1010
from .models import FastApiReadCypherQueryResponse
1111

1212
MAX_RETRIES = 3

0 commit comments

Comments
 (0)