Skip to content

Commit f50d57d

Browse files
edisonb24claude
andcommitted
Add Railway deployment config (org-restricted OAuth)
- railway.json: build from Dockerfile, /health check, restart policy - RAILWAY_DEPLOY.md: step-by-step deploy, Google Cloud Internal OAuth setup, and full env template (disk-backed OAuth state, persistent creds volume, RAILWAY_RUN_UID for non-root volume writes) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 3f19c7d commit f50d57d

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

RAILWAY_DEPLOY.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Deploying this fork to Railway (org-restricted)
2+
3+
Self-host the Google Workspace MCP as a remote server, with sign-in locked to your
4+
`construct.sg` Google Workspace. Docker-based, persistent, HTTPS, auto-redeploy on push.
5+
6+
The MCP endpoint will be: `https://<your-domain>/mcp`
7+
8+
---
9+
10+
## 1. Google Cloud — OAuth (the org restriction lives here)
11+
12+
1. [console.cloud.google.com](https://console.cloud.google.com/) → create/select a project **inside the construct.sg org**.
13+
2. **APIs & Services → OAuth consent screen → User Type = Internal.** ← restricts login to construct.sg. Fill app name + support email.
14+
3. **Enabled APIs & services → Enable APIs** for what you'll use: Gmail, Drive, Calendar, Docs, Sheets, Slides, Forms, Tasks, People. (Custom Search / Chat / Apps Script only if needed — Chat needs extra app config.)
15+
4. **Credentials → Create credentials → OAuth client ID → Web application.**
16+
- Leave redirect URI empty for now (added in step 3 below).
17+
- Save the **Client ID** and **Client secret**.
18+
19+
## 2. Railway — create the service
20+
21+
1. [railway.com](https://railway.com/)**New Project → Deploy from GitHub repo**`edisonb24/google_workspace_mcp`. It builds from the `Dockerfile` (via `railway.json`).
22+
2. **Add a Volume** on the service, mount path **`/data`**.
23+
3. **Variables** → add the ones below (Raw Editor makes this a single paste).
24+
- Fill `GOOGLE_OAUTH_CLIENT_ID` / `GOOGLE_OAUTH_CLIENT_SECRET` from step 1.
25+
- Generate the signing key once:
26+
`python3 -c "import secrets;print(secrets.token_urlsafe(48))"`
27+
- Leave `WORKSPACE_EXTERNAL_URL` / `GOOGLE_OAUTH_REDIRECT_URI` as placeholders for now.
28+
4. **Settings → Networking → Generate Domain.** Copy the `https://<name>.up.railway.app`.
29+
30+
```dotenv
31+
# Google OAuth (from Google Cloud → Credentials → Web application)
32+
GOOGLE_OAUTH_CLIENT_ID=<your-client-id>.apps.googleusercontent.com
33+
GOOGLE_OAUTH_CLIENT_SECRET=<your-client-secret>
34+
35+
# Transport / OAuth 2.1 multi-user mode
36+
MCP_ENABLE_OAUTH21=true
37+
WORKSPACE_MCP_TRANSPORT=streamable-http
38+
WORKSPACE_MCP_HOST=0.0.0.0
39+
# Do NOT set WORKSPACE_MCP_PORT — Railway injects PORT and the server reads it.
40+
41+
# Public URL — set to the real domain AFTER step 4 (no trailing slash)
42+
WORKSPACE_EXTERNAL_URL=https://<name>.up.railway.app
43+
GOOGLE_OAUTH_REDIRECT_URI=https://<name>.up.railway.app/oauth2callback
44+
45+
# Persistence — one volume mounted at /data
46+
WORKSPACE_MCP_CREDENTIALS_DIR=/data/creds
47+
WORKSPACE_MCP_OAUTH_PROXY_STORAGE_BACKEND=disk
48+
WORKSPACE_MCP_OAUTH_PROXY_DISK_DIRECTORY=/data/oauth-proxy
49+
FASTMCP_SERVER_AUTH_GOOGLE_JWT_SIGNING_KEY=<paste-generated-key>
50+
51+
# Let the non-root image write to the root-owned Railway volume
52+
RAILWAY_RUN_UID=0
53+
# Leave OAUTHLIB_INSECURE_TRANSPORT / OAUTH2_ALLOW_INSECURE_TRANSPORT unset in prod.
54+
```
55+
56+
## 3. Wire the domain back
57+
58+
1. Railway → Variables → set the two URL vars to the real domain (no trailing slash):
59+
- `WORKSPACE_EXTERNAL_URL=https://<name>.up.railway.app`
60+
- `GOOGLE_OAUTH_REDIRECT_URI=https://<name>.up.railway.app/oauth2callback`
61+
2. Google Cloud → your OAuth client → **Authorized redirect URIs** → add
62+
`https://<name>.up.railway.app/oauth2callback` → Save.
63+
3. Railway redeploys automatically on the variable change.
64+
65+
## 4. Verify
66+
67+
```bash
68+
curl https://<name>.up.railway.app/health # → 200 OK
69+
curl https://<name>.up.railway.app/.well-known/oauth-protected-resource # → JSON metadata
70+
```
71+
72+
Then add the server to your MCP client as a **streamable-http / remote** server at
73+
`https://<name>.up.railway.app/mcp`, run the OAuth flow, and sign in with your
74+
`construct.sg` account. A non-org Google account should be rejected at the consent screen.
75+
76+
## Updating later
77+
78+
`git fetch upstream && git merge upstream/main && git push` → Railway auto-redeploys.
79+
80+
## Custom domain (optional)
81+
82+
Railway → add domain `mcp.construct.sg` (set the CNAME) → then update
83+
`WORKSPACE_EXTERNAL_URL`, `GOOGLE_OAUTH_REDIRECT_URI`, and the GCP redirect URI to match.

railway.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://railway.com/railway.schema.json",
3+
"build": {
4+
"builder": "DOCKERFILE",
5+
"dockerfilePath": "Dockerfile"
6+
},
7+
"deploy": {
8+
"startCommand": "uv run main.py --transport streamable-http",
9+
"healthcheckPath": "/health",
10+
"healthcheckTimeout": 60,
11+
"restartPolicyType": "ON_FAILURE",
12+
"restartPolicyMaxRetries": 10
13+
}
14+
}

0 commit comments

Comments
 (0)