google-cloud-support-mcp
An MCP server for Google Cloud Support. Read and act on support cases across every project and organization you have access to, without leaving the conversation.
This is not an official Google product. It is an independent project, not affiliated with, endorsed by, or supported by Google. It talks to the public Cloud Support API using your own credentials. Google Cloud and Google are trademarks of Google LLC.
What it covers
Fourteen tools over the Cloud Support API v2 — cases, comments, attachments, and problem classifications:
| Group | Tools |
|---|---|
| Cases (read) | search_cases, get_case |
| Cases (write) | create_case, update_case, close_case, escalate_case |
| Classifications | search_case_classifications |
| Comments | list_comments, get_comment, add_comment |
| Attachments | list_attachments, get_attachment, download_attachment, upload_attachment |
Plus a case://{parent_type}/{parent_id}/{case_id} resource template and two
prompts (triage_open_cases, draft_case_from_incident).
Not covered, deliberately:
SupportEventSubscriptionService(7 RPCs) is out of scope — push delivery and subscription CRUD alike. Seespecs/001-gcp-support-mcp/spec.mdClarifications, 2026-09-01.v2beta, includingFeedService.ShowFeed. The one thing v2beta looked like it offered — cross-organization search — turned out not to exist: its query grammar'sORcombines values within a field, never scope identifiers.
Prerequisites
-
Python 3.14+ and uv
-
A paid support plan (Standard, Enhanced, or Premium) on the target project or organization. The Cloud Support API is unavailable without one, and that failure is reported separately from a permissions failure.
-
IAM:
roles/cloudsupport.techSupportViewerto read,roles/cloudsupport.techSupportEditorto write. Organization-level scopes also needresourcemanager.organizations.get. -
API enabled and credentials resolved:
gcloud services enable cloudsupport.googleapis.com --project=PROJECT_ID gcloud auth application-default login gcloud auth application-default set-quota-project PROJECT_ID
Install and run
Nothing to clone or install — uvx fetches the published package and runs it:
uvx google-cloud-support-mcp
Register it with an MCP client:
{
"mcpServers": {
"google-cloud-support": {
"command": "uvx",
"args": ["google-cloud-support-mcp"],
"env": {
"GOOGLE_CLOUD_SUPPORT_MCP_PARENTS": "[\"projects/my-project\",\"organizations/123456789\"]",
"GOOGLE_CLOUD_SUPPORT_MCP_QUOTA_PROJECT": "my-project"
}
}
}
}
Pin a release with uvx google-cloud-support-mcp@0.1.0 if you would rather not track
the latest.
From a clone
uv sync
uv run google-cloud-support-mcp # stdio
uv run fastmcp inspect # tool surface, without starting a session
fastmcp.json points the FastMCP CLI at server.py:create_server, so inspect,
run and dev all work with no arguments.
{
"mcpServers": {
"google-cloud-support": {
"command": "uv",
"args": ["run", "--directory", "/path/to/google-cloud-support-mcp", "google-cloud-support-mcp"],
"env": {
"GOOGLE_CLOUD_SUPPORT_MCP_PARENTS": "[\"projects/my-project\",\"organizations/123456789\"]",
"GOOGLE_CLOUD_SUPPORT_MCP_QUOTA_PROJECT": "my-project"
}
}
}
}
Configuration
All variables take the GOOGLE_CLOUD_SUPPORT_MCP_ prefix.
| Variable | Default | What it does |
|---|---|---|
PARENTS |
[] |
Scopes to query, as a JSON list of projects/{id} / organizations/{id}. Validated at start-up. |
QUOTA_PROJECT |
— | Billing/quota project. Organization-level calls with user credentials generally need this. |
ENABLE_WRITES |
true |
false leaves only the eight read tools. |
ENABLE_ESCALATION |
false |
Escalation is hidden unless you turn it on. See below. |
DOWNLOAD_DIR |
system temp | Where download_attachment writes files. |
MAX_UPLOAD_BYTES |
10000000 |
Client-side check before an upload is offered. |
DEFAULT_PAGE_SIZE |
20 |
Kept small so list results do not swamp the conversation. |
FANOUT_CONCURRENCY |
5 |
Parallel scopes per query; lower it if you hit rate limits. |
Two things worth knowing
Nothing reaches Google without your confirmation. Every outward-facing action
shows you the exact payload and waits. Declining sends nothing. The approved
payload is sealed by the framework between rounds, so what executes is
byte-for-byte what you were shown — a confirm: true parameter could not do
this, because the model would be the one filling it in.
Escalation is off by default. It is delivered to Google's support
organisation and a person acts on it. Set GOOGLE_CLOUD_SUPPORT_MCP_ENABLE_ESCALATION=true
to expose the tool; it still requires a written justification and your
confirmation.
Cross-organization queries
search_cases queries every configured scope in parallel and merges the results.
A scope that fails does not fail the call — it appears in scopes_failed with
what to do about it, so one organization's permission error never hides another
organization's cases. Pagination uses a composite cursor that carries a token per
scope; changing parents mid-pagination is rejected rather than silently skewing
the results.
Development
uv run pytest # unit + contract; integration is deselected
uv run ruff check . && uv run ruff format --check .
Releasing
Two workflows live in .github/workflows/. ci.yml lints and tests every push
and pull request. release.yml fires only on a tag you push yourself —
nothing publishes from an ordinary push to main.
git tag v0.1.1
git push origin v0.1.1
That is the whole release. The version is derived from the tag — pyproject.toml
declares dynamic = ["version"] and hatchling reads git, so there is no version
string anywhere to bump or to drift out of sync. A build off a tag is 0.1.1; a
build one commit later is 0.1.1.post1.dev0+abc1234, which PyPI refuses, so an
accidental release from the wrong commit cannot reach the index.
The pipeline runs lint and tests, builds the sdist and wheel, asserts the built
version really is the tag (a checkout that cannot see the tag would quietly
produce 0.0.0), checks the wheel actually exposes the google-cloud-support-mcp
console script (otherwise uvx would break for everyone), publishes to PyPI, and
creates a GitHub release with the artifacts attached.
When a release fails
Every step fails loudly. Most of them also stop the steps after them — a failed PyPI upload means no GitHub release is created, so a tag never points at something that was not published.
Changelog generation is the one exception, and it is deferred rather than swallowed. git-cliff crashes instead of degrading when the GitHub API is unreachable, and by that point PyPI has already published; stopping there would strand the package with no release pointing at it. So the release is created with GitHub's generic notes and the job then fails on purpose. The run goes red, an annotation says exactly what was substituted, and nothing is lost. Edit the release body by hand, or fix the cause and re-run the job.
Fix the cause, then re-run the same tag — there is no need to burn a version number:
- Actions tab → the failed run → "Re-run failed jobs" picks up from the job that failed and reuses the artifacts already built.
- Actions tab → Release → "Run workflow" takes an existing tag as input and runs the whole pipeline against it. Use this when the run is too old to re-run, or when you fixed something outside the repository — a missing PyPI publisher, say.
Retrying an upload is safe. uv publish --check-url checks the index first, so a
run that uploaded the sdist and then died before the wheel will skip the sdist
rather than fail on a duplicate. Re-running the release job likewise refreshes
the attached files instead of erroring on them.
The one thing a retry cannot fix is a version already published to PyPI with different contents — PyPI does not allow overwriting a file. That needs a new tag.
One-time setup before the first tag. PyPI publishing uses Trusted Publishing, so no API token is stored anywhere — but it will not work until you register the publisher. On https://pypi.org/manage/account/publishing/, add a pending publisher with:
| Field | Value |
|---|---|
| PyPI project name | google-cloud-support-mcp |
| Owner | your GitHub user or org |
| Repository name | this repository |
| Workflow name | release.yml |
| Environment name | pypi |
The environment name matters: the pypi job declares environment: pypi, and
PyPI checks it. Creating that environment in the repository settings also lets
you require a manual approval before anything is published.
Integration tests are double-gated on purpose:
export GOOGLE_CLOUD_SUPPORT_MCP_IT_PARENT=projects/my-test-project # read-only tests
export GOOGLE_CLOUD_SUPPORT_MCP_IT_ALLOW_WRITES=1 # also the writing ones
Writing tests always pass test_case=True, which keeps the case out of a support
engineer's queue, and a guard in tests/integration/conftest.py refuses any
CreateCase without that flag. Escalation is never exercised against the live
API.
Not yet verified against the live API
Three checks need a project with a paid support plan, which the verification project did not have. The code is written and covered by mock-based contract tests; only the live confirmation is outstanding:
- attachment upload size limit and the media
uploadType(T081) - first-run time-to-first-query measurement (T083)
- the write walkthroughs, quickstart V4 and V5 (T085)
specs/001-gcp-support-mcp/deferred.md says what is blocked, what was already
confirmed live on 2026-09-01, and which files to touch when you finish each one.
Read it before picking any of them up — several non-obvious API behaviours are
recorded there and are easy to re-derive by accident.
Design documents live in specs/001-gcp-support-mcp/; project principles are in
.specify/memory/constitution.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file google_cloud_support_mcp-0.1.0.tar.gz.
File metadata
- Download URL: google_cloud_support_mcp-0.1.0.tar.gz
- Upload date:
- Size: 202.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8fd4a67177c77af2ec0fd5371bbd443887a7dc3bf7157050817d84f402bd6c21
|
|
| MD5 |
2adc8fea6e707dfaf6779c0ee534bfe6
|
|
| BLAKE2b-256 |
9d11a80096ec80e46f59448ddd44f344e0fc820cbc7805cc54adf5b648579f76
|
File details
Details for the file google_cloud_support_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: google_cloud_support_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 42.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b3153c44627c470f75e2a806ae7a626c435dfd6144b1de85a10d4c05cd1e4bc
|
|
| MD5 |
7a7fa17a5d1d1fa921c2b16446b266fa
|
|
| BLAKE2b-256 |
96a97248206b0175a51c1e59ad7bb28a4c5c426cc8af0f0312e7561293d70d63
|