Skip to content

Commit 3c6d1a6

Browse files
committed
Update version to 1.1.0, switch from fastapi-mcp to fastmcp, and enhance documentation for transport endpoints and authentication methods
1 parent 0ade000 commit 3c6d1a6

File tree

6 files changed

+748
-201
lines changed

6 files changed

+748
-201
lines changed

README.md

Lines changed: 83 additions & 6 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,6 +15,7 @@ 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

@@ -43,11 +44,15 @@ You can set these variables directly in your environment or place them in a `.en
4344
```bash
4445
uv run sandbox-api-mcp-server
4546
```
46-
This will 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)
4750

4851
## Using with MCP Clients (e.g., Claude Desktop)
4952

50-
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.
5156

5257
### Step 1: Install `mcp-remote` (if not already installed)
5358

@@ -58,23 +63,37 @@ npm install -g mcp-remote
5863
5964
### Step 2: Run your FastAPI MCP Server
6065
61-
Ensure your FastAPI MCP server is running locally (e.g., on `http://localhost:9100` with the MCP endpoint at `http://localhost:9100/sse`):
66+
Ensure your FastAPI MCP server is running locally (e.g., on `http://localhost:9100`):
6267
```bash
6368
uv run sandbox-api-mcp-server
6469
```
6570
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+
6675
6776
### Step 3: Run `mcp-remote`
6877
69-
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:
7079
80+
**Using SSE (legacy, backward compatible):**
7181
```bash
7282
# If mcp-cli is installed globally
7383
mcp-remote http://localhost:9100/sse 8080
7484
7585
# Or using npx
7686
npx -y mcp-remote http://localhost:9100/sse 8080
7787
```
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+
```
7897
`mcp-remote` will now listen on `localhost:8080` and proxy requests to your actual MCP server, handling the OAuth flow.
7998
8099
### Step 4: Configure Claude Desktop
@@ -87,6 +106,7 @@ npx -y mcp-remote http://localhost:9100/sse 8080
87106
2. **Configure the MCP Server in Claude Desktop:**
88107
Edit `claude_desktop_config.json` to point to the local port where `mcp-remote` is listening (e.g., `8080`).
89108

109+
**Option A: Using SSE transport (backward compatible):**
90110
```json
91111
{
92112
"mcpServers": {
@@ -102,6 +122,23 @@ npx -y mcp-remote http://localhost:9100/sse 8080
102122
}
103123
}
104124
```
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+
```
105142
**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.
106143

107144
3. **Restart Claude Desktop:**
@@ -247,6 +284,7 @@ The following tools are exposed, derived from the FastAPI application's endpoint
247284
* API routes (which become MCP tools) are defined in `src/sandbox_api_mcp_server/sandbox/routes.py`.
248285
* Request/response models are primarily in `src/sandbox_api_mcp_server/sandbox/models.py` and `src/sandbox_api_mcp_server/models.py`.
249286
* Authentication logic is in `src/sandbox_api_mcp_server/auth.py`.
287+
* Auth0 OAuth provider for FastMCP is in `src/sandbox_api_mcp_server/auth_provider.py`.
250288

251289
### Dependency Management
252290

@@ -257,4 +295,43 @@ This project uses [UV](https://docs.astral.sh/uv/) for fast, reliable dependency
257295
* **Updating dependencies**: `uv lock --upgrade`
258296
* **Running scripts**: `uv run <command>`
259297

260-
All dependencies are defined in `pyproject.toml` and locked in `uv.lock` for reproducible builds.
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+
"fastmcp>=2.12.4",
11+
"fastapi",
12+
"httpx>=0.28.1",
13+
"PyJWT>=2.10.1",
14+
"python-dotenv>=1.1.0",
15+
"pydantic>=2.11.4",
16+
"uvicorn>=0.30.0",
17+
"cryptography~=45.0.0"
18+
]
19+
},
20+
"deployment": {
21+
"transport": "http",
22+
"port": 9100
23+
}
24+
}
25+

pyproject.toml

Lines changed: 2 additions & 2 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"
@@ -15,7 +15,7 @@ authors = [
1515
]
1616
dependencies = [
1717
"cryptography~=45.0.0",
18-
"fastapi-mcp>=0.3.3",
18+
"fastmcp>=2.12.4",
1919
"httpx>=0.28.1,<1.0.0",
2020
"PyJWT>=2.10.1,<3.0.0",
2121
"python-dotenv>=1.1.0,<2.0.0",
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/server.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
from anyio import get_cancelled_exc_class
55
from contextlib import asynccontextmanager
66
from dotenv import load_dotenv
7-
from fastapi import FastAPI, Depends
7+
from fastapi import FastAPI
88
from fastapi.middleware.cors import CORSMiddleware
9-
from fastapi_mcp import AuthConfig, FastApiMCP
9+
from fastmcp import FastMCP
10+
from fastmcp.server.openapi import RouteMap
1011
from uvicorn._types import ASGI3Application, ASGIReceiveCallable, ASGISendCallable, Scope
1112
from starlette.middleware.base import BaseHTTPMiddleware
12-
from .auth import fetch_jwks_public_key, verify_auth
13+
from .auth import fetch_jwks_public_key
1314
from .models import Auth0Settings
1415
from .sandbox.routes import get_sandbox_api_router
1516
from .helpers import get_logger
@@ -101,28 +102,52 @@ def run():
101102
)
102103
app.add_middleware(ProxyHeadersMiddleware)
103104
app.add_middleware(SecurityHeadersMiddleware)
104-
fastapi_mcp = FastApiMCP(
105-
app,
106-
name="Neo4j Sandbox API MCP Server",
107-
description="Neo4j Sandbox API MCP Server.",
108-
exclude_operations=["health_check"],
109-
auth_config=AuthConfig(
110-
issuer=f"https://{Auth0Settings().auth0_domain}/",
111-
authorize_url=f"https://{Auth0Settings().auth0_domain}/authorize",
112-
oauth_metadata_url=Auth0Settings().auth0_oauth_metadata_url,
113-
audience=Auth0Settings().auth0_audience,
114-
default_scope="read:account-info openid email profile user_metadata",
115-
client_id=Auth0Settings().auth0_client_id,
116-
client_secret=Auth0Settings().auth0_client_secret,
117-
dependencies=[Depends(verify_auth)],
118-
setup_proxies=True,
105+
106+
# Get port from environment or use default
107+
port = int(os.getenv("PORT", 9100))
108+
109+
# Define route maps to exclude health_check endpoint from MCP tools
110+
route_maps = [
111+
# Exclude health endpoint from MCP tools
112+
RouteMap(
113+
methods=["GET"],
114+
pattern=r".*/health$",
115+
mcp_type=None, # Exclude from MCP
119116
),
117+
# Map all other endpoints to tools (default behavior)
118+
]
119+
120+
# Convert FastAPI app to MCP server using from_fastapi
121+
# This will expose FastAPI endpoints as MCP tools
122+
mcp = FastMCP.from_fastapi(
123+
app=app,
124+
name="Neo4j Sandbox API MCP Server",
125+
description="Neo4j Sandbox API MCP Server for managing Neo4j sandboxes.",
126+
route_maps=route_maps,
120127
)
121128

122-
# Mount the MCP server
123-
fastapi_mcp.mount(mount_path="/sse")
129+
# Mount MCP transports - support both for maximum compatibility
130+
131+
# SSE transport for backward compatibility with existing clients
132+
sse_app = mcp.sse_app()
133+
app.mount("/sse", sse_app)
134+
135+
# Streamable HTTP transport for modern clients (recommended for production)
136+
streamable_app = mcp.streamable_http_app()
137+
app.mount("/mcp", streamable_app)
138+
139+
logger.info("MCP server available at multiple transports:")
140+
logger.info(" - /sse (SSE transport - legacy, backward compatible)")
141+
logger.info(" - /mcp (Streamable HTTP transport - modern, recommended)")
142+
143+
# Authentication Note:
144+
# - FastAPI routes use Depends(verify_auth) which handles both:
145+
# * OAuth2/JWT tokens from Auth0
146+
# * API Key authentication (Authorization: Bearer ApiKey <key>)
147+
# - This provides backward compatibility with existing API consumers
148+
# - MCP clients will use the existing FastAPI auth via standard HTTP headers
124149

125-
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", 9100)))
150+
uvicorn.run(app, host="0.0.0.0", port=port)
126151

127152

128153
if __name__ == "__main__":

0 commit comments

Comments
 (0)