> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mcp-use.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Google Cloud Run

> Deploy an MCP server to Google Cloud Run

Cloud Run can run a `mcp-use` server as a long-lived Node process. The
generated CLI scripts build the default-exported server and own the HTTP
listener.

Cloud Run requires the process to listen on `0.0.0.0` and injects the port in
`PORT`. The configuration below satisfies both parts of that container
contract.

## Create the project

Create the server from the beta template:

```bash theme={null}
npx create-mcp-use-app@beta zoo-cloud-run \
  --template mcp-server \
  --npm \
  --install \
  --no-skills
cd zoo-cloud-run
```

The generated `package.json` includes these scripts:

```json theme={null}
{
  "scripts": {
    "build": "mcp-use build",
    "dev": "mcp-use dev",
    "start": "mcp-use start",
    "deploy": "mcp-use deploy",
    "typecheck": "mcp-use typecheck"
  }
}
```

Replace `index.ts` with this small zoo server:

```typescript theme={null}
import { MCPServer } from "mcp-use";
import { z } from "zod";

const animalSchema = z.object({
  name: z.string(),
  species: z.string(),
  age: z.number().int().nonnegative(),
  enclosure: z.string(),
});

type Animal = z.infer<typeof animalSchema>;

const animals: Animal[] = [
  { name: "Leo", species: "lion", age: 7, enclosure: "Big Cat Plains" },
  { name: "Nala", species: "lion", age: 6, enclosure: "Big Cat Plains" },
  { name: "Chilly", species: "penguin", age: 3, enclosure: "Arctic Exhibit" },
];

const server = new MCPServer({
  name: "zoo-cloud-run",
  version: "1.0.0",
  description: "Look up animals at a fictional zoo",
  host: "0.0.0.0",
});

server.tool(
  {
    name: "get-animals-by-species",
    description: "Return all zoo animals of one species",
    inputSchema: z.object({ species: z.string() }),
    outputSchema: z.object({ animals: z.array(animalSchema) }),
  },
  async ({ species }) => {
    const matches = animals.filter(
      (animal) => animal.species.toLowerCase() === species.toLowerCase(),
    );
    const result = { animals: matches };
    return {
      content: [{ type: "text", text: JSON.stringify(result) }],
      structuredContent: result,
    };
  },
);

server.tool(
  {
    name: "get-animal-details",
    description: "Return one animal by name",
    inputSchema: z.object({ name: z.string() }),
    outputSchema: z.object({ animal: animalSchema.nullable() }),
    view: { name: "animal-card" },
  },
  async ({ name }) => {
    const animal =
      animals.find(
        (candidate) => candidate.name.toLowerCase() === name.toLowerCase(),
      ) ?? null;
    const result = { animal };
    return {
      content: [
        {
          type: "text",
          text:
            animal === null
              ? `No animal named ${name}`
              : JSON.stringify(animal),
        },
      ],
      structuredContent: result,
    };
  },
);

export default server;
```

There is no top-level `listen()` call. `npm run dev` and `npm start` import the
default export and own the listener. At runtime, `mcp-use start` reads Cloud
Run's `PORT` before falling back to the server configuration.

## Optional: add a View

The `mcp-server` template intentionally has no React UI. Install the View
dependencies:

```bash theme={null}
npm install react react-dom
npm install --save-dev @types/react @types/react-dom
```

Create `views/animal-card/view.tsx`:

```tsx theme={null}
import { useToolContext } from "mcp-use/react";

export default function AnimalCard() {
  const view = useToolContext<"get-animal-details">();

  if (view.status === "pending") return <p>Loading animal…</p>;
  if (view.status === "error") return <p>{view.error.message}</p>;
  if (view.toolOutput.animal === null) return <p>Animal not found.</p>;

  const animal = view.toolOutput.animal;
  return (
    <article>
      <h2>{animal.name}</h2>
      <p>
        {animal.species}, age {animal.age}
      </p>
      <p>Enclosure: {animal.enclosure}</p>
    </article>
  );
}
```

The tool's `view.name` matches the directory under `views/`. A view-bound tool
must declare `outputSchema` and return matching `structuredContent`; the View
reads that typed result through `useToolContext`.

Validate the project locally:

```bash theme={null}
npm run typecheck
npm run build
PORT=8080 npm start
```

The local endpoint is `http://localhost:8080/mcp`.

## Add a Dockerfile

Create `Dockerfile`:

```dockerfile theme={null}
FROM node:22-slim

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run typecheck && npm run build

ENV NODE_ENV=production
CMD ["npm", "start"]
```

Create `.dockerignore`:

```text theme={null}
node_modules
.git
.env
.env.*
.mcp-use
```

`npm start` serves `.mcp-use/build/`, binds to the server's `0.0.0.0`
configuration, and uses the injected `PORT`.

## Configure Google Cloud

Select a project and enable the source-deployment services:

```bash theme={null}
gcloud config set project YOUR_PROJECT_ID
gcloud services enable \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com
```

Create a dedicated runtime service account:

```bash theme={null}
gcloud iam service-accounts create zoo-mcp-runtime \
  --display-name="Zoo MCP Cloud Run runtime"
```

Set the values used by every deploy:

```bash theme={null}
PROJECT_ID="$(gcloud config get-value project)"
REGION="us-central1"
RUNTIME_SA="zoo-mcp-runtime@${PROJECT_ID}.iam.gserviceaccount.com"
```

## Deploy a private service

Deploy from source and require an authenticated Cloud Run invoker:

```bash theme={null}
gcloud run deploy zoo-mcp-server \
  --source=. \
  --region="${REGION}" \
  --service-account="${RUNTIME_SA}" \
  --no-allow-unauthenticated
```

For later revisions, run the same command with the same `--region`,
`--service-account`, and `--no-allow-unauthenticated` flags. Keeping the
security-sensitive flags in the repeatable deploy command makes the intended
IAM posture explicit.

Grant your current Google identity permission to invoke the service:

```bash theme={null}
ACCOUNT="$(gcloud config get-value account)"
gcloud run services add-iam-policy-binding zoo-mcp-server \
  --region="${REGION}" \
  --member="user:${ACCOUNT}" \
  --role="roles/run.invoker"
```

## Connect with a materialized ID token

Cloud Run ID tokens expire. Generate the settings file immediately before
starting the client rather than putting shell variable names inside JSON:

```bash theme={null}
SERVICE_URL="$(
  gcloud run services describe zoo-mcp-server \
    --region="${REGION}" \
    --format="value(status.url)"
)"
ID_TOKEN="$(gcloud auth print-identity-token)"
mkdir -p "${HOME}/.gemini"
jq -n \
  --arg url "${SERVICE_URL}/mcp" \
  --arg token "${ID_TOKEN}" \
  '{
    mcpServers: {
      "zoo-cloud-run": {
        httpUrl: $url,
        headers: { Authorization: ("Bearer " + $token) }
      }
    }
  }' > "${HOME}/.gemini/settings.json"
```

`jq` writes the actual URL and token values. If the client later receives
`401 Unauthorized`, generate a fresh token and regenerate the file.

## Verify

Read recent service logs without relying on a fixed log format:

```bash theme={null}
gcloud run services logs read zoo-mcp-server \
  --region="${REGION}" \
  --limit=20
```

For lifecycle or resource removal, use the Cloud Run and Google Cloud project
controls appropriate to your account. This guide intentionally does not run
broad cleanup commands.

## Platform references

* [Deploy Cloud Run services from source](https://cloud.google.com/run/docs/deploying-source-code)
* [Cloud Run container runtime contract](https://cloud.google.com/run/docs/container-contract)
* [Authenticate developers to private services](https://cloud.google.com/run/docs/authenticating/developers)
* [Bind a View to a tool](/v2/typescript/server/tools#bind-a-view)
