feat(gcp-mcp-finops-agent): add FinOps recipe on the Google Cloud BigQuery MCP server - #2659
vishal-bulbule wants to merge 2 commits into
Conversation
…Query MCP server A single-agent Python recipe that answers cloud cost questions from the Cloud Billing export through the Google-hosted BigQuery MCP server. It shows per-request token refresh with header_provider, a read-only tool_filter allowlist, credentials loaded on first use, and an instruction built around how the billing export behaves. Proposal: google#2658
| - "Cloud Billing export" | ||
|
|
||
| ownership: | ||
| team: "TechTrapture" |
There was a problem hiding this comment.
[MAJOR] Can you please provide the specific maintaining team name here instead of the company?
There was a problem hiding this comment.
Updated in 331e7fd. I maintain this recipe on my own, so ownership.team now names me and poc stays my GitHub handle. The README also has a Maintainer section with a contact address.
| name="root_agent", | ||
| model=Gemini( | ||
| model=os.getenv("MODEL_NAME"), | ||
| retry_options=types.HttpRetryOptions(attempts=3), |
There was a problem hiding this comment.
[MINOR] Perhaps we should extract the retry attempt count to a named constant? This makes it easier to configure and reuse in other model definitions.
|
|
||
|
|
||
| def gcp_auth_headers( | ||
| _context: ReadonlyContext | None = None, |
There was a problem hiding this comment.
[MINOR] ignored parameter
| async for event in runner.run_async( | ||
| user_id="test_user", session_id=session.id, new_message=message | ||
| ): | ||
| for part in event.content.parts if event.content else []: |
There was a problem hiding this comment.
[MAJOR] If event.content.parts is None in some stream events (which can happen with metadata-only chunks), this loop will raise a TypeError. Can we use event.content.parts or [] to safely handle None values?
There was a problem hiding this comment.
Good catch, fixed in 331e7fd. The loop now iterates over parts or [].
Handle events with no parts in the integration test, name the maintainer in the manifest and README.
happyhuman
left a comment
There was a problem hiding this comment.
Please submit a recipe proposal here before submitting a PR.
|
Thanks. The proposal is #2658, filed the day before this PR. I opened it from the CLI, so it did not pick up the template's default assignees and may not be in the reviewers' queue. Could you assign it, or would you prefer that I refile it through the template? I'll convert this PR to a draft until the proposal is approved. |
Adds
contrib/python/gcp-mcp-finops-agent, proposed in #2658.What it is
A single-agent recipe that answers Google Cloud cost questions from the Cloud Billing export in BigQuery. It reaches BigQuery through the Google-hosted BigQuery MCP server, so there is no MCP server to install or run.
I could not find an existing recipe that connects to a Google Cloud hosted MCP server. This one is meant to be the runnable example for that integration.
What it teaches
McpToolsetto a Google-hosted MCP server over Streamable HTTP, with the IAM roles it needs.header_provider, so the agent keeps working after the first hour.tool_filterallowlist. The server also offersexecute_sqlandcancel_job; the model never sees them.If
BILLING_TABLEis not set, the agent still loads and tells the user what to configure.Testing
uv run validate contrib/python/gcp-mcp-finops-agent: manifest, structure, README and placement pass.ruff formatandruff check: clean.uv run pytest: 11 tests pass on Python 3.11 and 3.13 with.envcopied from.env.example, no credentials and no network.check_env_vars.py,check_recipe_pyproject.py,check_lockfile_hashes.pyandcheck_house_rules.py: pass, no house-rule findings.uv run pytest tests/integration: 2 tests pass against the live BigQuery MCP server and a real billing export. One checks that the server offers exactly the allowlisted tools through the filter, the other that the agent answers from anexecute_sql_readonlycall.adk web: data window, month over month net cost by service, a delete request (refused), and the unconfigured path.Notes
make-python-recipe-deployablein a follow-up if you want that.