Google Cloud Run runs containers on demand and scales to zero when nobody is calling. That makes it a good fit for an MCP server that is used in bursts: you get an HTTPS endpoint, standard container logs, and no cluster to manage.
The important detail is that Cloud Run injects a PORT environment variable and expects the process to listen on 0.0.0.0. Generated bundles default to localhost and a fixed port, so each language needs a small adjustment before deployment.
Choose a build path
- Buildpacks: TypeScript and Python deploy from source with no Dockerfile.
- Dockerfile: PHP and Java use a small container definition. This path also works for TypeScript and Python if you need full control.
Install the Google Cloud CLI, authenticate, and select a project before starting.
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
The first source deploy may prompt you to enable Cloud Build and Artifact Registry. Accept the prompts and let the build finish.
TypeScript with buildpacks
Open the generated bundle and make two changes. First, let the entry point read PORT and bind to all interfaces in src/server.ts.
// Before
port: { type: 'string', default: '3000' },
serve({ fetch: app.fetch, port, hostname: '127.0.0.1' }, () => {
// After
port: { type: 'string', default: process.env.PORT ?? '3000' },
serve({ fetch: app.fetch, port, hostname: '0.0.0.0' }, () => {
Second, make npm start launch the HTTP transport, because server.ts defaults to stdio. Keep the build script unchanged; the Node buildpack runs it automatically.
{
"scripts": {
"build": "tsc",
"start": "node dist/server.js --http",
"dev": "tsx watch src/server.ts"
}
}
Deploy from the bundle root. Cloud Run builds the app, runs the build script, and starts it with npm start.
gcloud run deploy my-mcp-server \
--source . \
--region us-central1 \
--allow-unauthenticated
Python with buildpacks
The generated Python server accepts a host and port, so a Procfile is enough. Add it at the bundle root next to requirements.txt.
web: python server.py --transport streamable-http --host 0.0.0.0 --port $PORT
Cloud Run supplies $PORT when it starts the process. Deploy the same way:
gcloud run deploy my-mcp-server \
--source . \
--region us-central1 \
--allow-unauthenticated
PHP with a Dockerfile
The generated PHP bundle is served by PHP's built-in web server, which accepts a bind address and port. Build the dependencies in a Composer stage, then copy the application into a CLI image.
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json ./
RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
FROM php:8.3-cli
WORKDIR /app
COPY . .
COPY --from=vendor /app/vendor ./vendor
RUN mkdir -p sessions
CMD ["sh", "-c", "php -S 0.0.0.0:${PORT:-8080} server.php"]
Add a .dockerignore so local artifacts do not end up in the image, then deploy from the bundle root.
.git
vendor
sessions
dev.log
gcloud run deploy my-mcp-server \
--source . \
--region us-central1 \
--allow-unauthenticated
PHP's built-in server is single-threaded. It is fine for development and low traffic, but use PHP-FPM behind a production web server if you expect concurrent load.
Java with a Dockerfile
The generated Java server embeds Tomcat and binds to all interfaces. Build the classes and copy the dependency set, then run Server with the HTTP transport and the injected port.
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml ./
RUN mvn -q -DskipTests dependency:go-offline
COPY src ./src
RUN mvn -q -DskipTests package dependency:copy-dependencies
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=build /app/target/classes ./classes
COPY --from=build /app/target/dependency ./dependency
CMD ["sh", "-c", "java -cp 'classes:dependency/*' generated.mcp.Server --http --port ${PORT:-8080}"]
gcloud run deploy my-mcp-server \
--source . \
--region us-central1 \
--allow-unauthenticated
Add credentials
Every generated bundle reads upstream credentials from environment variables. Put tokens in Secret Manager instead of passing them on the command line.
printf '%s' "$API_TOKEN" | gcloud secrets create mcp-api-token --data-file=-
gcloud run deploy my-mcp-server \
--source . \
--region us-central1 \
--allow-unauthenticated \
--set-secrets MCP_AUTH_API_TOKEN=mcp-api-token:latest
Use the exact variable names from the bundle README. They are prefixed with MCP_AUTH_ and derived from your security schemes. The Cloud Run service account needs permission to read the secret.
Verify the deployment
After the deploy finishes, Cloud Run prints the service URL. The MCP endpoint is that URL plus /mcp.
https://my-mcp-server-abc123-uc.a.run.app/mcp
Connect an MCP client that supports Streamable HTTP to that address, then call a read-only tool. If the connection fails, inspect the container logs; startup errors and missing environment variables appear there.
gcloud run services logs read my-mcp-server --region us-central1 --limit 50
Operational notes
- Scale to zero. The default minimum instance count is zero, so the first request after idle pays a cold start. Use --min-instances 1 when latency matters more than cost.
- Streaming and timeouts. Streamable HTTP can hold a response open. Cloud Run has a maximum request timeout and a concurrency limit per instance; tune --timeout and --concurrency for your client behavior.
- Session affinity. The PHP bundle stores sessions on the local filesystem, and any in-memory state disappears when an instance stops. Enable session affinity for sticky routing, or move state to an external store. For a quick test, cap the service at one instance.
- Public access. --allow-unauthenticated makes the endpoint reachable by any MCP client, but it also makes it public. Upstream API credentials are protected; the MCP endpoint itself is not. Add authentication or put it behind an identity-aware proxy before sharing it.
- Cost control. Set --max-instances to cap scale-out, especially for long-lived streams.
Next steps
Cloud Run gives you the most language coverage of the options covered so far, at the cost of a container or buildpack adjustment per runtime. Compare it with Cloudflare Workers, Vercel, AWS App Runner, Render, and Railway, or start from From OpenAPI to a Running MCP Server if you still need a bundle. A side-by-side comparison of all deployment options is available in Where to Deploy Your MCP Server.