diff --git a/mcp_server/DEPLOY_RAILWAY.md b/mcp_server/DEPLOY_RAILWAY.md new file mode 100644 index 00000000..dde63011 --- /dev/null +++ b/mcp_server/DEPLOY_RAILWAY.md @@ -0,0 +1,56 @@ +# Deploying Graphiti MCP Server on Railway + +This guide captures the minimum configuration needed to deploy the Graphiti MCP server on Railway without encountering the `cd: mcp_server: No such file or directory` error. + +## Root cause + +Railway's "Root Directory" setting was already pointed at `mcp_server/`. The start command attempted to run `cd mcp_server && ...`, so the service tried to change into `mcp_server/mcp_server/` at runtime and failed, resulting in a 502 error. + +## Recommended configuration (Root Directory = `mcp_server`) + +1. In Railway → **Settings → Root Directory**, set the value to `mcp_server`. +2. Set the **Start Command** to: + + ```bash + bash start.sh + ``` + +Railway will execute the launcher inside the `mcp_server` directory, respect the `$PORT` value it injects, and the server will bind to `0.0.0.0:$PORT`. + +## Alternative configuration (Root Directory = repo root `/`) + +If you prefer to keep the root directory at the repository root, update the start command instead: + +```bash +cd mcp_server && bash start.sh +``` + +This mirrors the local workflow while continuing to honor the `$PORT` value supplied by Railway. + +## Local verification + +Install dependencies with `uv sync`, then run: + +```bash +cd mcp_server +PORT=8080 bash start.sh +``` + +You should see the log line: + +``` +Graphiti MCP Server listening on 0.0.0.0:8080 (transport=sse) +``` + +## Remote validation with MCP Inspector + +After deploying to Railway, validate the endpoint with [MCP Inspector](https://github.com/modelcontextprotocol/inspector): + +```bash +npx @modelcontextprotocol/inspector \ + --transport sse \ + --url https://.railway.app/sse \ + --headers "Authorization: Bearer " +``` + +Replace `` with your Railway subdomain and include the authorization header only if your deployment requires it. diff --git a/mcp_server/graphiti_mcp_server.py b/mcp_server/graphiti_mcp_server.py index b652b7b9..44fd00e5 100644 --- a/mcp_server/graphiti_mcp_server.py +++ b/mcp_server/graphiti_mcp_server.py @@ -1209,6 +1209,12 @@ async def initialize_server() -> MCPConfig: args = parser.parse_args() + host = getattr(args, 'host', os.environ.get('MCP_SERVER_HOST', '0.0.0.0')) + transport = getattr(args, 'transport', 'sse') + port_arg = getattr(args, 'port', None) + default_port = port_arg if port_arg is not None else 8080 + port = int(os.environ.get('PORT', default_port)) + # Build configuration from CLI arguments and environment variables config = GraphitiConfig.from_cli_and_env(args) @@ -1227,15 +1233,17 @@ async def initialize_server() -> MCPConfig: # Initialize Graphiti await initialize_graphiti() - if args.host: - logger.info(f'Setting MCP server host to: {args.host}') + if host: + logger.info(f'Setting MCP server host to: {host}') # Set MCP server host from CLI or env - mcp.settings.host = args.host - - if args.port: - logger.info(f'Setting MCP server port to: {args.port}') + mcp.settings.host = host + + if port: + logger.info(f'Setting MCP server port to: {port}') # Set MCP server port from CLI or env - mcp.settings.port = args.port + mcp.settings.port = port + + print(f'Graphiti MCP Server listening on {host}:{port} (transport={transport})') # Return MCP configuration return MCPConfig.from_cli(args) diff --git a/mcp_server/start.sh b/mcp_server/start.sh new file mode 100755 index 00000000..88d2b29f --- /dev/null +++ b/mcp_server/start.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")" +: "${PORT:=8080}" +PYTHON_BIN="${VIRTUAL_ENV:+$VIRTUAL_ENV/bin/python}" +if [[ -z "${PYTHON_BIN}" && -x "./.venv/bin/python" ]]; then + PYTHON_BIN="$(pwd)/.venv/bin/python" +fi +PYTHON_BIN="${PYTHON_BIN:-python}" +exec "$PYTHON_BIN" graphiti_mcp_server.py --transport sse --host 0.0.0.0 --port "$PORT"